Update Scintilla to version 3.4.4
[TortoiseGit.git] / ext / scintilla / src / Editor.cxx
blob4155ba5f99fba99f30af93a14657f1458c08f58b
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 <math.h>
12 #include <assert.h>
13 #include <ctype.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 "StringCopy.h"
27 #include "SplitVector.h"
28 #include "Partitioning.h"
29 #include "RunStyles.h"
30 #include "ContractionState.h"
31 #include "CellBuffer.h"
32 #include "KeyMap.h"
33 #include "Indicator.h"
34 #include "XPM.h"
35 #include "LineMarker.h"
36 #include "Style.h"
37 #include "ViewStyle.h"
38 #include "CharClassify.h"
39 #include "Decoration.h"
40 #include "CaseFolder.h"
41 #include "Document.h"
42 #include "UniConversion.h"
43 #include "Selection.h"
44 #include "PositionCache.h"
45 #include "Editor.h"
47 #ifdef SCI_NAMESPACE
48 using namespace Scintilla;
49 #endif
52 return whether this modification represents an operation that
53 may reasonably be deferred (not done now OR [possibly] at all)
55 static bool CanDeferToLastStep(const DocModification &mh) {
56 if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
57 return true; // CAN skip
58 if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)))
59 return false; // MUST do
60 if (mh.modificationType & SC_MULTISTEPUNDOREDO)
61 return true; // CAN skip
62 return false; // PRESUMABLY must do
65 static bool CanEliminate(const DocModification &mh) {
66 return
67 (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
71 return whether this modification represents the FINAL step
72 in a [possibly lengthy] multi-step Undo/Redo sequence
74 static bool IsLastStep(const DocModification &mh) {
75 return
76 (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0
77 && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
78 && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
79 && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
82 Caret::Caret() :
83 active(false), on(false), period(500) {}
85 Timer::Timer() :
86 ticking(false), ticksToWait(0), tickerID(0) {}
88 Idler::Idler() :
89 state(false), idlerID(0) {}
91 static inline bool IsControlCharacter(int ch) {
92 // iscntrl returns true for lots of chars > 127 which are displayable
93 return ch >= 0 && ch < ' ';
96 static inline bool IsAllSpacesOrTabs(char *s, unsigned int len) {
97 for (unsigned int i = 0; i < len; i++) {
98 // This is safe because IsSpaceOrTab() will return false for null terminators
99 if (!IsSpaceOrTab(s[i]))
100 return false;
102 return true;
105 PrintParameters::PrintParameters() {
106 magnification = 0;
107 colourMode = SC_PRINT_NORMAL;
108 wrapState = eWrapWord;
111 Editor::Editor() {
112 ctrlID = 0;
114 stylesValid = false;
115 technology = SC_TECHNOLOGY_DEFAULT;
116 scaleRGBAImage = 100.0f;
118 cursorMode = SC_CURSORNORMAL;
120 hasFocus = false;
121 hideSelection = false;
122 inOverstrike = false;
123 drawOverstrikeCaret = true;
124 errorStatus = 0;
125 mouseDownCaptures = true;
127 bufferedDraw = true;
128 twoPhaseDraw = true;
130 lastClickTime = 0;
131 dwellDelay = SC_TIME_FOREVER;
132 ticksToDwell = SC_TIME_FOREVER;
133 dwelling = false;
134 ptMouseLast.x = 0;
135 ptMouseLast.y = 0;
136 inDragDrop = ddNone;
137 dropWentOutside = false;
138 posDrag = SelectionPosition(invalidPosition);
139 posDrop = SelectionPosition(invalidPosition);
140 hotSpotClickPos = INVALID_POSITION;
141 selectionType = selChar;
143 lastXChosen = 0;
144 lineAnchorPos = 0;
145 originalAnchorPos = 0;
146 wordSelectAnchorStartPos = 0;
147 wordSelectAnchorEndPos = 0;
148 wordSelectInitialCaretPos = -1;
150 primarySelection = true;
152 caretXPolicy = CARET_SLOP | CARET_EVEN;
153 caretXSlop = 50;
155 caretYPolicy = CARET_EVEN;
156 caretYSlop = 0;
158 visiblePolicy = 0;
159 visibleSlop = 0;
161 searchAnchor = 0;
163 xOffset = 0;
164 xCaretMargin = 50;
165 horizontalScrollBarVisible = true;
166 scrollWidth = 2000;
167 trackLineWidth = false;
168 lineWidthMaxSeen = 0;
169 verticalScrollBarVisible = true;
170 endAtLastLine = true;
171 caretSticky = SC_CARETSTICKY_OFF;
172 marginOptions = SC_MARGINOPTION_NONE;
173 mouseSelectionRectangularSwitch = false;
174 multipleSelection = false;
175 additionalSelectionTyping = false;
176 multiPasteMode = SC_MULTIPASTE_ONCE;
177 additionalCaretsBlink = true;
178 additionalCaretsVisible = true;
179 virtualSpaceOptions = SCVS_NONE;
181 pixmapLine = 0;
182 pixmapSelMargin = 0;
183 pixmapSelPattern = 0;
184 pixmapSelPatternOffset1 = 0;
185 pixmapIndentGuide = 0;
186 pixmapIndentGuideHighlight = 0;
188 targetStart = 0;
189 targetEnd = 0;
190 searchFlags = 0;
192 topLine = 0;
193 posTopLine = 0;
195 lengthForEncode = -1;
197 needUpdateUI = 0;
198 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
199 braces[0] = invalidPosition;
200 braces[1] = invalidPosition;
201 bracesMatchStyle = STYLE_BRACEBAD;
202 highlightGuideColumn = 0;
204 paintState = notPainting;
205 paintAbandonedByStyling = false;
206 paintingAllText = false;
207 willRedrawAll = false;
209 modEventMask = SC_MODEVENTMASKALL;
211 pdoc = new Document();
212 pdoc->AddRef();
213 pdoc->AddWatcher(this, 0);
215 recordingMacro = false;
216 foldFlags = 0;
217 foldAutomatic = 0;
219 wrapWidth = LineLayout::wrapWidthInfinite;
221 convertPastes = true;
223 hotspot = Range(invalidPosition);
225 llc.SetLevel(LineLayoutCache::llcCaret);
226 posCache.SetSize(0x400);
228 SetRepresentations();
231 Editor::~Editor() {
232 pdoc->RemoveWatcher(this, 0);
233 pdoc->Release();
234 pdoc = 0;
235 DropGraphics(true);
238 void Editor::Finalise() {
239 SetIdle(false);
240 CancelModes();
243 void Editor::SetRepresentations() {
244 reprs.Clear();
246 // C0 control set
247 const char *reps[] = {
248 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
249 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
250 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
251 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
253 for (size_t j=0; j < ELEMENTS(reps); j++) {
254 char c[2] = { static_cast<char>(j), 0 };
255 reprs.SetRepresentation(c, reps[j]);
258 // C1 control set
259 // As well as Unicode mode, ISO-8859-1 should use these
260 if (IsUnicodeMode()) {
261 const char *repsC1[] = {
262 "PAD", "HOP", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
263 "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
264 "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
265 "SOS", "SGCI", "SCI", "CSI", "ST", "OSC", "PM", "APC"
267 for (size_t j=0; j < ELEMENTS(repsC1); j++) {
268 char c1[3] = { '\xc2', static_cast<char>(0x80+j), 0 };
269 reprs.SetRepresentation(c1, repsC1[j]);
271 reprs.SetRepresentation("\xe2\x80\xa8", "LS");
272 reprs.SetRepresentation("\xe2\x80\xa9", "PS");
275 // UTF-8 invalid bytes
276 if (IsUnicodeMode()) {
277 for (int k=0x80; k < 0x100; k++) {
278 char hiByte[2] = { static_cast<char>(k), 0 };
279 char hexits[4];
280 sprintf(hexits, "x%2X", k);
281 reprs.SetRepresentation(hiByte, hexits);
286 void Editor::DropGraphics(bool freeObjects) {
287 if (freeObjects) {
288 delete pixmapLine;
289 pixmapLine = 0;
290 delete pixmapSelMargin;
291 pixmapSelMargin = 0;
292 delete pixmapSelPattern;
293 pixmapSelPattern = 0;
294 delete pixmapSelPatternOffset1;
295 pixmapSelPatternOffset1 = 0;
296 delete pixmapIndentGuide;
297 pixmapIndentGuide = 0;
298 delete pixmapIndentGuideHighlight;
299 pixmapIndentGuideHighlight = 0;
300 } else {
301 if (pixmapLine)
302 pixmapLine->Release();
303 if (pixmapSelMargin)
304 pixmapSelMargin->Release();
305 if (pixmapSelPattern)
306 pixmapSelPattern->Release();
307 if (pixmapSelPatternOffset1)
308 pixmapSelPatternOffset1->Release();
309 if (pixmapIndentGuide)
310 pixmapIndentGuide->Release();
311 if (pixmapIndentGuideHighlight)
312 pixmapIndentGuideHighlight->Release();
316 void Editor::AllocateGraphics() {
317 if (!pixmapLine)
318 pixmapLine = Surface::Allocate(technology);
319 if (!pixmapSelMargin)
320 pixmapSelMargin = Surface::Allocate(technology);
321 if (!pixmapSelPattern)
322 pixmapSelPattern = Surface::Allocate(technology);
323 if (!pixmapSelPatternOffset1)
324 pixmapSelPatternOffset1 = Surface::Allocate(technology);
325 if (!pixmapIndentGuide)
326 pixmapIndentGuide = Surface::Allocate(technology);
327 if (!pixmapIndentGuideHighlight)
328 pixmapIndentGuideHighlight = Surface::Allocate(technology);
331 void Editor::InvalidateStyleData() {
332 stylesValid = false;
333 vs.technology = technology;
334 DropGraphics(false);
335 AllocateGraphics();
336 llc.Invalidate(LineLayout::llInvalid);
337 posCache.Clear();
340 void Editor::InvalidateStyleRedraw() {
341 NeedWrapping();
342 InvalidateStyleData();
343 Redraw();
346 void Editor::RefreshStyleData() {
347 if (!stylesValid) {
348 stylesValid = true;
349 AutoSurface surface(this);
350 if (surface) {
351 vs.Refresh(*surface, pdoc->tabInChars);
353 SetScrollBars();
354 SetRectangularRange();
358 Point Editor::GetVisibleOriginInMain() {
359 return Point(0,0);
362 Point Editor::DocumentPointFromView(Point ptView) {
363 Point ptDocument = ptView;
364 if (wMargin.GetID()) {
365 Point ptOrigin = GetVisibleOriginInMain();
366 ptDocument.x += ptOrigin.x;
367 ptDocument.y += ptOrigin.y;
368 } else {
369 ptDocument.x += xOffset;
370 ptDocument.y += topLine * vs.lineHeight;
372 return ptDocument;
375 int Editor::TopLineOfMain() const {
376 if (wMargin.GetID())
377 return 0;
378 else
379 return topLine;
382 PRectangle Editor::GetClientRectangle() {
383 return wMain.GetClientPosition();
386 PRectangle Editor::GetClientDrawingRectangle() {
387 return GetClientRectangle();
390 PRectangle Editor::GetTextRectangle() {
391 PRectangle rc = GetClientRectangle();
392 rc.left += vs.textStart;
393 rc.right -= vs.rightMarginWidth;
394 return rc;
397 int Editor::LinesOnScreen() {
398 PRectangle rcClient = GetClientRectangle();
399 int htClient = static_cast<int>(rcClient.bottom - rcClient.top);
400 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
401 return htClient / vs.lineHeight;
404 int Editor::LinesToScroll() {
405 int retVal = LinesOnScreen() - 1;
406 if (retVal < 1)
407 return 1;
408 else
409 return retVal;
412 int Editor::MaxScrollPos() {
413 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
414 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
415 int retVal = cs.LinesDisplayed();
416 if (endAtLastLine) {
417 retVal -= LinesOnScreen();
418 } else {
419 retVal--;
421 if (retVal < 0) {
422 return 0;
423 } else {
424 return retVal;
428 const char *ControlCharacterString(unsigned char ch) {
429 const char *reps[] = {
430 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
431 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
432 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
433 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
435 if (ch < ELEMENTS(reps)) {
436 return reps[ch];
437 } else {
438 return "BAD";
443 * Convenience class to ensure LineLayout objects are always disposed.
445 class AutoLineLayout {
446 LineLayoutCache &llc;
447 LineLayout *ll;
448 AutoLineLayout &operator=(const AutoLineLayout &);
449 public:
450 AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
451 ~AutoLineLayout() {
452 llc.Dispose(ll);
453 ll = 0;
455 LineLayout *operator->() const {
456 return ll;
458 operator LineLayout *() const {
459 return ll;
461 void Set(LineLayout *ll_) {
462 llc.Dispose(ll);
463 ll = ll_;
467 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
468 if (sp.Position() < 0) {
469 return SelectionPosition(0);
470 } else if (sp.Position() > pdoc->Length()) {
471 return SelectionPosition(pdoc->Length());
472 } else {
473 // If not at end of line then set offset to 0
474 if (!pdoc->IsLineEndPosition(sp.Position()))
475 sp.SetVirtualSpace(0);
476 return sp;
480 Point Editor::LocationFromPosition(SelectionPosition pos) {
481 Point pt;
482 RefreshStyleData();
483 if (pos.Position() == INVALID_POSITION)
484 return pt;
485 const int line = pdoc->LineFromPosition(pos.Position());
486 const int lineVisible = cs.DisplayFromDoc(line);
487 //Platform::DebugPrintf("line=%d\n", line);
488 AutoSurface surface(this);
489 AutoLineLayout ll(llc, RetrieveLineLayout(line));
490 if (surface && ll) {
491 const int posLineStart = pdoc->LineStart(line);
492 LayoutLine(line, surface, vs, ll, wrapWidth);
493 const int posInLine = pos.Position() - posLineStart;
494 pt = ll->PointFromPosition(posInLine, vs.lineHeight);
495 pt.y += (lineVisible - topLine) * vs.lineHeight;
496 pt.x += vs.textStart - xOffset;
498 pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
499 return pt;
502 Point Editor::LocationFromPosition(int pos) {
503 return LocationFromPosition(SelectionPosition(pos));
506 int Editor::XFromPosition(int pos) {
507 Point pt = LocationFromPosition(pos);
508 return static_cast<int>(pt.x) - vs.textStart + xOffset;
511 int Editor::XFromPosition(SelectionPosition sp) {
512 Point pt = LocationFromPosition(sp);
513 return static_cast<int>(pt.x) - vs.textStart + xOffset;
516 int Editor::LineFromLocation(Point pt) const {
517 return cs.DocFromDisplay(static_cast<int>(pt.y) / vs.lineHeight + topLine);
520 void Editor::SetTopLine(int topLineNew) {
521 if ((topLine != topLineNew) && (topLineNew >= 0)) {
522 topLine = topLineNew;
523 ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
525 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
528 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
529 RefreshStyleData();
530 if (canReturnInvalid) {
531 PRectangle rcClient = GetTextRectangle();
532 // May be in scroll view coordinates so translate back to main view
533 Point ptOrigin = GetVisibleOriginInMain();
534 rcClient.Move(-ptOrigin.x, -ptOrigin.y);
535 if (!rcClient.Contains(pt))
536 return SelectionPosition(INVALID_POSITION);
537 if (pt.x < vs.textStart)
538 return SelectionPosition(INVALID_POSITION);
539 if (pt.y < 0)
540 return SelectionPosition(INVALID_POSITION);
542 pt = DocumentPointFromView(pt);
543 pt.x = pt.x - vs.textStart;
544 int visibleLine = static_cast<int>(floor(pt.y / vs.lineHeight));
545 if (!canReturnInvalid && (visibleLine < 0))
546 visibleLine = 0;
547 const int lineDoc = cs.DocFromDisplay(visibleLine);
548 if (canReturnInvalid && (lineDoc < 0))
549 return SelectionPosition(INVALID_POSITION);
550 if (lineDoc >= pdoc->LinesTotal())
551 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length());
552 const int posLineStart = pdoc->LineStart(lineDoc);
553 AutoSurface surface(this);
554 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
555 if (surface && ll) {
556 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
557 const int lineStartSet = cs.DisplayFromDoc(lineDoc);
558 const int subLine = visibleLine - lineStartSet;
559 if (subLine < ll->lines) {
560 const Range rangeSubLine = ll->SubLineRange(subLine);
561 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
562 if (subLine > 0) // Wrapped
563 pt.x -= ll->wrapIndent;
564 const int positionInLine = ll->FindPositionFromX(pt.x + subLineStart, rangeSubLine, charPosition);
565 if (positionInLine < rangeSubLine.end) {
566 return SelectionPosition(pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
568 if (virtualSpace) {
569 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
570 const int spaceOffset = static_cast<int>(
571 (pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
572 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
573 } else if (canReturnInvalid) {
574 if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) {
575 return SelectionPosition(pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1));
577 } else {
578 return SelectionPosition(rangeSubLine.end + posLineStart);
581 if (!canReturnInvalid)
582 return SelectionPosition(ll->numCharsInLine + posLineStart);
584 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : posLineStart);
587 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
588 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
592 * Find the document position corresponding to an x coordinate on a particular document line.
593 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
594 * This method is used for rectangular selections and does not work on wrapped lines.
596 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
597 RefreshStyleData();
598 if (lineDoc >= pdoc->LinesTotal())
599 return SelectionPosition(pdoc->Length());
600 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
601 AutoSurface surface(this);
602 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
603 if (surface && ll) {
604 const int posLineStart = pdoc->LineStart(lineDoc);
605 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
606 const Range rangeSubLine = ll->SubLineRange(0);
607 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
608 const int positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false);
609 if (positionInLine < rangeSubLine.end) {
610 return SelectionPosition(pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
612 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
613 const int spaceOffset = static_cast<int>(
614 (x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
615 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
617 return SelectionPosition(0);
620 int Editor::PositionFromLineX(int lineDoc, int x) {
621 return SPositionFromLineX(lineDoc, x).Position();
625 * If painting then abandon the painting because a wider redraw is needed.
626 * @return true if calling code should stop drawing.
628 bool Editor::AbandonPaint() {
629 if ((paintState == painting) && !paintingAllText) {
630 paintState = paintAbandoned;
632 return paintState == paintAbandoned;
635 void Editor::RedrawRect(PRectangle rc) {
636 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
638 // Clip the redraw rectangle into the client area
639 PRectangle rcClient = GetClientRectangle();
640 if (rc.top < rcClient.top)
641 rc.top = rcClient.top;
642 if (rc.bottom > rcClient.bottom)
643 rc.bottom = rcClient.bottom;
644 if (rc.left < rcClient.left)
645 rc.left = rcClient.left;
646 if (rc.right > rcClient.right)
647 rc.right = rcClient.right;
649 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
650 wMain.InvalidateRectangle(rc);
654 void Editor::DiscardOverdraw() {
655 // Overridden on platforms that may draw outside visible area.
658 void Editor::Redraw() {
659 //Platform::DebugPrintf("Redraw all\n");
660 PRectangle rcClient = GetClientRectangle();
661 wMain.InvalidateRectangle(rcClient);
662 if (wMargin.GetID())
663 wMargin.InvalidateAll();
664 //wMain.InvalidateAll();
667 void Editor::RedrawSelMargin(int line, bool allAfter) {
668 bool abandonDraw = false;
669 if (!wMargin.GetID()) // Margin in main window so may need to abandon and retry
670 abandonDraw = AbandonPaint();
671 if (!abandonDraw) {
672 if (vs.maskInLine) {
673 Redraw();
674 } else {
675 PRectangle rcSelMargin = GetClientRectangle();
676 rcSelMargin.right = rcSelMargin.left + vs.fixedColumnWidth;
677 if (line != -1) {
678 PRectangle rcLine = RectangleFromRange(Range(pdoc->LineStart(line)));
680 // Inflate line rectangle if there are image markers with height larger than line height
681 if (vs.largestMarkerHeight > vs.lineHeight) {
682 int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
683 rcLine.top -= delta;
684 rcLine.bottom += delta;
685 if (rcLine.top < rcSelMargin.top)
686 rcLine.top = rcSelMargin.top;
687 if (rcLine.bottom > rcSelMargin.bottom)
688 rcLine.bottom = rcSelMargin.bottom;
691 rcSelMargin.top = rcLine.top;
692 if (!allAfter)
693 rcSelMargin.bottom = rcLine.bottom;
694 if (rcSelMargin.Empty())
695 return;
697 if (wMargin.GetID()) {
698 Point ptOrigin = GetVisibleOriginInMain();
699 rcSelMargin.Move(-ptOrigin.x, -ptOrigin.y);
700 wMargin.InvalidateRectangle(rcSelMargin);
701 } else {
702 wMain.InvalidateRectangle(rcSelMargin);
708 PRectangle Editor::RectangleFromRange(Range r) {
709 const int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(r.First()));
710 const int maxLine = cs.DisplayLastFromDoc(pdoc->LineFromPosition(r.Last()));
711 const PRectangle rcClientDrawing = GetClientDrawingRectangle();
712 PRectangle rc;
713 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
714 rc.left = static_cast<XYPOSITION>(vs.textStart - leftTextOverlap);
715 rc.top = static_cast<XYPOSITION>((minLine - TopLineOfMain()) * vs.lineHeight);
716 if (rc.top < rcClientDrawing.top)
717 rc.top = rcClientDrawing.top;
718 // Extend to right of prepared area if any to prevent artifacts from caret line highlight
719 rc.right = rcClientDrawing.right;
720 rc.bottom = static_cast<XYPOSITION>((maxLine - TopLineOfMain() + 1) * vs.lineHeight);
722 return rc;
725 void Editor::InvalidateRange(int start, int end) {
726 RedrawRect(RectangleFromRange(Range(start, end)));
729 int Editor::CurrentPosition() const {
730 return sel.MainCaret();
733 bool Editor::SelectionEmpty() const {
734 return sel.Empty();
737 SelectionPosition Editor::SelectionStart() {
738 return sel.RangeMain().Start();
741 SelectionPosition Editor::SelectionEnd() {
742 return sel.RangeMain().End();
745 void Editor::SetRectangularRange() {
746 if (sel.IsRectangular()) {
747 int xAnchor = XFromPosition(sel.Rectangular().anchor);
748 int xCaret = XFromPosition(sel.Rectangular().caret);
749 if (sel.selType == Selection::selThin) {
750 xCaret = xAnchor;
752 int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
753 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
754 int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
755 for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
756 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
757 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
758 range.ClearVirtualSpace();
759 if (line == lineAnchorRect)
760 sel.SetSelection(range);
761 else
762 sel.AddSelectionWithoutTrim(range);
767 void Editor::ThinRectangularRange() {
768 if (sel.IsRectangular()) {
769 sel.selType = Selection::selThin;
770 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
771 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
772 } else {
773 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
775 SetRectangularRange();
779 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
780 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
781 invalidateWholeSelection = true;
783 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
784 // +1 for lastAffected ensures caret repainted
785 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
786 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
787 if (invalidateWholeSelection) {
788 for (size_t r=0; r<sel.Count(); r++) {
789 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
790 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
791 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
792 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
795 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
796 InvalidateRange(firstAffected, lastAffected);
799 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
800 currentPos_ = ClampPositionIntoDocument(currentPos_);
801 anchor_ = ClampPositionIntoDocument(anchor_);
802 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
803 /* For Line selection - ensure the anchor and caret are always
804 at the beginning and end of the region lines. */
805 if (sel.selType == Selection::selLines) {
806 if (currentPos_ > anchor_) {
807 anchor_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
808 currentPos_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
809 } else {
810 currentPos_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
811 anchor_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
814 SelectionRange rangeNew(currentPos_, anchor_);
815 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
816 InvalidateSelection(rangeNew);
818 sel.RangeMain() = rangeNew;
819 SetRectangularRange();
820 ClaimSelection();
822 if (highlightDelimiter.NeedsDrawing(currentLine)) {
823 RedrawSelMargin();
825 QueueIdleWork(WorkNeeded::workUpdateUI);
828 void Editor::SetSelection(int currentPos_, int anchor_) {
829 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
832 // Just move the caret on the main selection
833 void Editor::SetSelection(SelectionPosition currentPos_) {
834 currentPos_ = ClampPositionIntoDocument(currentPos_);
835 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
836 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
837 InvalidateSelection(SelectionRange(currentPos_));
839 if (sel.IsRectangular()) {
840 sel.Rectangular() =
841 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
842 SetRectangularRange();
843 } else {
844 sel.RangeMain() =
845 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
847 ClaimSelection();
849 if (highlightDelimiter.NeedsDrawing(currentLine)) {
850 RedrawSelMargin();
852 QueueIdleWork(WorkNeeded::workUpdateUI);
855 void Editor::SetSelection(int currentPos_) {
856 SetSelection(SelectionPosition(currentPos_));
859 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
860 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
861 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
862 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
863 InvalidateSelection(rangeNew);
865 sel.Clear();
866 sel.RangeMain() = rangeNew;
867 SetRectangularRange();
868 ClaimSelection();
870 if (highlightDelimiter.NeedsDrawing(currentLine)) {
871 RedrawSelMargin();
873 QueueIdleWork(WorkNeeded::workUpdateUI);
876 void Editor::SetEmptySelection(int currentPos_) {
877 SetEmptySelection(SelectionPosition(currentPos_));
880 bool Editor::RangeContainsProtected(int start, int end) const {
881 if (vs.ProtectionActive()) {
882 if (start > end) {
883 int t = start;
884 start = end;
885 end = t;
887 for (int pos = start; pos < end; pos++) {
888 if (vs.styles[pdoc->StyleAt(pos)].IsProtected())
889 return true;
892 return false;
895 bool Editor::SelectionContainsProtected() {
896 for (size_t r=0; r<sel.Count(); r++) {
897 if (RangeContainsProtected(sel.Range(r).Start().Position(),
898 sel.Range(r).End().Position())) {
899 return true;
902 return false;
906 * Asks document to find a good position and then moves out of any invisible positions.
908 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
909 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
912 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
913 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
914 if (posMoved != pos.Position())
915 pos.SetPosition(posMoved);
916 if (vs.ProtectionActive()) {
917 if (moveDir > 0) {
918 if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1)].IsProtected()) {
919 while ((pos.Position() < pdoc->Length()) &&
920 (vs.styles[pdoc->StyleAt(pos.Position())].IsProtected()))
921 pos.Add(1);
923 } else if (moveDir < 0) {
924 if (vs.styles[pdoc->StyleAt(pos.Position())].IsProtected()) {
925 while ((pos.Position() > 0) &&
926 (vs.styles[pdoc->StyleAt(pos.Position() - 1)].IsProtected()))
927 pos.Add(-1);
931 return pos;
934 int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
935 bool simpleCaret = (sel.Count() == 1) && sel.Empty();
936 SelectionPosition spCaret = sel.Last();
938 int delta = newPos.Position() - sel.MainCaret();
939 newPos = ClampPositionIntoDocument(newPos);
940 newPos = MovePositionOutsideChar(newPos, delta);
941 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
942 // Can't turn into multiple selection so clear additional selections
943 InvalidateSelection(SelectionRange(newPos), true);
944 SelectionRange rangeMain = sel.RangeMain();
945 sel.SetSelection(rangeMain);
947 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
948 // Switching to rectangular
949 InvalidateSelection(sel.RangeMain(), false);
950 SelectionRange rangeMain = sel.RangeMain();
951 sel.Clear();
952 sel.Rectangular() = rangeMain;
954 if (selt != Selection::noSel) {
955 sel.selType = selt;
957 if (selt != Selection::noSel || sel.MoveExtends()) {
958 SetSelection(newPos);
959 } else {
960 SetEmptySelection(newPos);
962 ShowCaretAtCurrentPosition();
964 int currentLine = pdoc->LineFromPosition(newPos.Position());
965 if (ensureVisible) {
966 // In case in need of wrapping to ensure DisplayFromDoc works.
967 if (currentLine >= wrapPending.start)
968 WrapLines(wsAll);
969 XYScrollPosition newXY = XYScrollToMakeVisible(
970 SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret), xysDefault);
971 if (simpleCaret && (newXY.xOffset == xOffset)) {
972 // simple vertical scroll then invalidate
973 ScrollTo(newXY.topLine);
974 InvalidateSelection(SelectionRange(spCaret), true);
975 } else {
976 SetXYScroll(newXY);
980 if (highlightDelimiter.NeedsDrawing(currentLine)) {
981 RedrawSelMargin();
983 return 0;
986 int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
987 return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
990 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
991 pos = ClampPositionIntoDocument(pos);
992 pos = MovePositionOutsideChar(pos, moveDir);
993 int lineDoc = pdoc->LineFromPosition(pos.Position());
994 if (cs.GetVisible(lineDoc)) {
995 return pos;
996 } else {
997 int lineDisplay = cs.DisplayFromDoc(lineDoc);
998 if (moveDir > 0) {
999 // lineDisplay is already line before fold as lines in fold use display line of line after fold
1000 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
1001 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
1002 } else {
1003 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
1004 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
1009 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
1010 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
1013 Point Editor::PointMainCaret() {
1014 return LocationFromPosition(sel.Range(sel.Main()).caret);
1018 * Choose the x position that the caret will try to stick to
1019 * as it moves up and down.
1021 void Editor::SetLastXChosen() {
1022 Point pt = PointMainCaret();
1023 lastXChosen = static_cast<int>(pt.x) + xOffset;
1026 void Editor::ScrollTo(int line, bool moveThumb) {
1027 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
1028 if (topLineNew != topLine) {
1029 // Try to optimise small scrolls
1030 #ifndef UNDER_CE
1031 int linesToMove = topLine - topLineNew;
1032 bool performBlit = (abs(linesToMove) <= 10) && (paintState == notPainting);
1033 willRedrawAll = !performBlit;
1034 #endif
1035 SetTopLine(topLineNew);
1036 // Optimize by styling the view as this will invalidate any needed area
1037 // which could abort the initial paint if discovered later.
1038 StyleToPositionInView(PositionAfterArea(GetClientRectangle()));
1039 #ifndef UNDER_CE
1040 // Perform redraw rather than scroll if many lines would be redrawn anyway.
1041 if (performBlit) {
1042 ScrollText(linesToMove);
1043 } else {
1044 Redraw();
1046 willRedrawAll = false;
1047 #else
1048 Redraw();
1049 #endif
1050 if (moveThumb) {
1051 SetVerticalScrollPos();
1056 void Editor::ScrollText(int /* linesToMove */) {
1057 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
1058 Redraw();
1061 void Editor::HorizontalScrollTo(int xPos) {
1062 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
1063 if (xPos < 0)
1064 xPos = 0;
1065 if (!Wrapping() && (xOffset != xPos)) {
1066 xOffset = xPos;
1067 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1068 SetHorizontalScrollPos();
1069 RedrawRect(GetClientRectangle());
1073 void Editor::VerticalCentreCaret() {
1074 int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
1075 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1076 int newTop = lineDisplay - (LinesOnScreen() / 2);
1077 if (topLine != newTop) {
1078 SetTopLine(newTop > 0 ? newTop : 0);
1079 RedrawRect(GetClientRectangle());
1083 // Avoid 64 bit compiler warnings.
1084 // Scintilla does not support text buffers larger than 2**31
1085 static int istrlen(const char *s) {
1086 return static_cast<int>(s ? strlen(s) : 0);
1089 void Editor::MoveSelectedLines(int lineDelta) {
1091 // if selection doesn't start at the beginning of the line, set the new start
1092 int selectionStart = SelectionStart().Position();
1093 int startLine = pdoc->LineFromPosition(selectionStart);
1094 int beginningOfStartLine = pdoc->LineStart(startLine);
1095 selectionStart = beginningOfStartLine;
1097 // if selection doesn't end at the beginning of a line greater than that of the start,
1098 // then set it at the beginning of the next one
1099 int selectionEnd = SelectionEnd().Position();
1100 int endLine = pdoc->LineFromPosition(selectionEnd);
1101 int beginningOfEndLine = pdoc->LineStart(endLine);
1102 bool appendEol = false;
1103 if (selectionEnd > beginningOfEndLine
1104 || selectionStart == selectionEnd) {
1105 selectionEnd = pdoc->LineStart(endLine + 1);
1106 appendEol = (selectionEnd == pdoc->Length() && pdoc->LineFromPosition(selectionEnd) == endLine);
1109 // if there's nowhere for the selection to move
1110 // (i.e. at the beginning going up or at the end going down),
1111 // stop it right there!
1112 if ((selectionStart == 0 && lineDelta < 0)
1113 || (selectionEnd == pdoc->Length() && lineDelta > 0)
1114 || selectionStart == selectionEnd) {
1115 return;
1118 UndoGroup ug(pdoc);
1120 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
1121 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
1122 ClearSelection();
1123 selectionEnd = CurrentPosition();
1125 SetSelection(selectionStart, selectionEnd);
1127 SelectionText selectedText;
1128 CopySelectionRange(&selectedText);
1130 int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
1131 Point currentLocation = LocationFromPosition(CurrentPosition());
1132 int currentLine = LineFromLocation(currentLocation);
1134 if (appendEol)
1135 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
1136 ClearSelection();
1138 const char *eol = StringFromEOLMode(pdoc->eolMode);
1139 if (currentLine + lineDelta >= pdoc->LinesTotal())
1140 pdoc->InsertString(pdoc->Length(), eol, istrlen(eol));
1141 GoToLine(currentLine + lineDelta);
1143 selectionLength = pdoc->InsertString(CurrentPosition(), selectedText.Data(), selectionLength);
1144 if (appendEol) {
1145 const int lengthInserted = pdoc->InsertString(CurrentPosition() + selectionLength, eol, istrlen(eol));
1146 selectionLength += lengthInserted;
1148 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1151 void Editor::MoveSelectedLinesUp() {
1152 MoveSelectedLines(-1);
1155 void Editor::MoveSelectedLinesDown() {
1156 MoveSelectedLines(1);
1159 void Editor::MoveCaretInsideView(bool ensureVisible) {
1160 PRectangle rcClient = GetTextRectangle();
1161 Point pt = PointMainCaret();
1162 if (pt.y < rcClient.top) {
1163 MovePositionTo(SPositionFromLocation(
1164 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top)),
1165 false, false, UserVirtualSpace()),
1166 Selection::noSel, ensureVisible);
1167 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1168 int yOfLastLineFullyDisplayed = static_cast<int>(rcClient.top) + (LinesOnScreen() - 1) * vs.lineHeight;
1169 MovePositionTo(SPositionFromLocation(
1170 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top) + yOfLastLineFullyDisplayed),
1171 false, false, UserVirtualSpace()),
1172 Selection::noSel, ensureVisible);
1176 int Editor::DisplayFromPosition(int pos) {
1177 int lineDoc = pdoc->LineFromPosition(pos);
1178 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1179 AutoSurface surface(this);
1180 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
1181 if (surface && ll) {
1182 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
1183 unsigned int posLineStart = pdoc->LineStart(lineDoc);
1184 int posInLine = pos - posLineStart;
1185 lineDisplay--; // To make up for first increment ahead.
1186 for (int subLine = 0; subLine < ll->lines; subLine++) {
1187 if (posInLine >= ll->LineStart(subLine)) {
1188 lineDisplay++;
1192 return lineDisplay;
1196 * Ensure the caret is reasonably visible in context.
1198 Caret policy in SciTE
1200 If slop is set, we can define a slop value.
1201 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1202 This zone is defined as a number of pixels near the vertical margins,
1203 and as a number of lines near the horizontal margins.
1204 By keeping the caret away from the edges, it is seen within its context,
1205 so it is likely that the identifier that the caret is on can be completely seen,
1206 and that the current line is seen with some of the lines following it which are
1207 often dependent on that line.
1209 If strict is set, the policy is enforced... strictly.
1210 The caret is centred on the display if slop is not set,
1211 and cannot go in the UZ if slop is set.
1213 If jumps is set, the display is moved more energetically
1214 so the caret can move in the same direction longer before the policy is applied again.
1215 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1217 If even is not set, instead of having symmetrical UZs,
1218 the left and bottom UZs are extended up to right and top UZs respectively.
1219 This way, we favour the displaying of useful information: the beginning of lines,
1220 where most code reside, and the lines after the caret, eg. the body of a function.
1222 | | | | |
1223 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1224 | | | | | visibility or going into the UZ) display is...
1225 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1226 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1227 0 | 0 | 0 | 1 | Yes | moved by one position
1228 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1229 0 | 0 | 1 | 1 | Yes | centred on the caret
1230 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1231 0 | 1 | - | 1 | No, caret is always centred | -
1232 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1233 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1234 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1235 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1236 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1237 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1238 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1241 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange &range, const XYScrollOptions options) {
1242 PRectangle rcClient = GetTextRectangle();
1243 Point pt = LocationFromPosition(range.caret);
1244 Point ptAnchor = LocationFromPosition(range.anchor);
1245 const Point ptOrigin = GetVisibleOriginInMain();
1246 pt.x += ptOrigin.x;
1247 pt.y += ptOrigin.y;
1248 ptAnchor.x += ptOrigin.x;
1249 ptAnchor.y += ptOrigin.y;
1250 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1252 XYScrollPosition newXY(xOffset, topLine);
1253 if (rcClient.Empty()) {
1254 return newXY;
1257 // Vertical positioning
1258 if ((options & xysVertical) && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1259 const int lineCaret = DisplayFromPosition(range.caret.Position());
1260 const int linesOnScreen = LinesOnScreen();
1261 const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1262 const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
1263 const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
1264 const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
1265 const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
1267 // It should be possible to scroll the window to show the caret,
1268 // but this fails to remove the caret on GTK+
1269 if (bSlop) { // A margin is defined
1270 int yMoveT, yMoveB;
1271 if (bStrict) {
1272 int yMarginT, yMarginB;
1273 if (!(options & xysUseMargin)) {
1274 // In drag mode, avoid moves
1275 // otherwise, a double click will select several lines.
1276 yMarginT = yMarginB = 0;
1277 } else {
1278 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1279 // a maximum of slightly less than half the heigth of the text area.
1280 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1281 if (bEven) {
1282 yMarginB = yMarginT;
1283 } else {
1284 yMarginB = linesOnScreen - yMarginT - 1;
1287 yMoveT = yMarginT;
1288 if (bEven) {
1289 if (bJump) {
1290 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1292 yMoveB = yMoveT;
1293 } else {
1294 yMoveB = linesOnScreen - yMoveT - 1;
1296 if (lineCaret < topLine + yMarginT) {
1297 // Caret goes too high
1298 newXY.topLine = lineCaret - yMoveT;
1299 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1300 // Caret goes too low
1301 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1303 } else { // Not strict
1304 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1305 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1306 if (bEven) {
1307 yMoveB = yMoveT;
1308 } else {
1309 yMoveB = linesOnScreen - yMoveT - 1;
1311 if (lineCaret < topLine) {
1312 // Caret goes too high
1313 newXY.topLine = lineCaret - yMoveT;
1314 } else if (lineCaret > topLine + linesOnScreen - 1) {
1315 // Caret goes too low
1316 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1319 } else { // No slop
1320 if (!bStrict && !bJump) {
1321 // Minimal move
1322 if (lineCaret < topLine) {
1323 // Caret goes too high
1324 newXY.topLine = lineCaret;
1325 } else if (lineCaret > topLine + linesOnScreen - 1) {
1326 // Caret goes too low
1327 if (bEven) {
1328 newXY.topLine = lineCaret - linesOnScreen + 1;
1329 } else {
1330 newXY.topLine = lineCaret;
1333 } else { // Strict or going out of display
1334 if (bEven) {
1335 // Always center caret
1336 newXY.topLine = lineCaret - halfScreen;
1337 } else {
1338 // Always put caret on top of display
1339 newXY.topLine = lineCaret;
1343 if (!(range.caret == range.anchor)) {
1344 const int lineAnchor = DisplayFromPosition(range.anchor.Position());
1345 if (lineAnchor < lineCaret) {
1346 // Shift up to show anchor or as much of range as possible
1347 newXY.topLine = std::min(newXY.topLine, lineAnchor);
1348 newXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());
1349 } else {
1350 // Shift down to show anchor or as much of range as possible
1351 newXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());
1352 newXY.topLine = std::min(newXY.topLine, lineCaret);
1355 newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
1358 // Horizontal positioning
1359 if ((options & xysHorizontal) && !Wrapping()) {
1360 const int halfScreen = Platform::Maximum(static_cast<int>(rcClient.Width()) - 4, 4) / 2;
1361 const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
1362 const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
1363 const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
1364 const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
1366 if (bSlop) { // A margin is defined
1367 int xMoveL, xMoveR;
1368 if (bStrict) {
1369 int xMarginL, xMarginR;
1370 if (!(options & xysUseMargin)) {
1371 // In drag mode, avoid moves unless very near of the margin
1372 // otherwise, a simple click will select text.
1373 xMarginL = xMarginR = 2;
1374 } else {
1375 // xMargin must equal to caretXSlop, with a minimum of 2 and
1376 // a maximum of slightly less than half the width of the text area.
1377 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1378 if (bEven) {
1379 xMarginL = xMarginR;
1380 } else {
1381 xMarginL = static_cast<int>(rcClient.Width()) - xMarginR - 4;
1384 if (bJump && bEven) {
1385 // Jump is used only in even mode
1386 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1387 } else {
1388 xMoveL = xMoveR = 0; // Not used, avoid a warning
1390 if (pt.x < rcClient.left + xMarginL) {
1391 // Caret is on the left of the display
1392 if (bJump && bEven) {
1393 newXY.xOffset -= xMoveL;
1394 } else {
1395 // Move just enough to allow to display the caret
1396 newXY.xOffset -= static_cast<int>((rcClient.left + xMarginL) - pt.x);
1398 } else if (pt.x >= rcClient.right - xMarginR) {
1399 // Caret is on the right of the display
1400 if (bJump && bEven) {
1401 newXY.xOffset += xMoveR;
1402 } else {
1403 // Move just enough to allow to display the caret
1404 newXY.xOffset += static_cast<int>(pt.x - (rcClient.right - xMarginR) + 1);
1407 } else { // Not strict
1408 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1409 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1410 if (bEven) {
1411 xMoveL = xMoveR;
1412 } else {
1413 xMoveL = static_cast<int>(rcClient.Width()) - xMoveR - 4;
1415 if (pt.x < rcClient.left) {
1416 // Caret is on the left of the display
1417 newXY.xOffset -= xMoveL;
1418 } else if (pt.x >= rcClient.right) {
1419 // Caret is on the right of the display
1420 newXY.xOffset += xMoveR;
1423 } else { // No slop
1424 if (bStrict ||
1425 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1426 // Strict or going out of display
1427 if (bEven) {
1428 // Center caret
1429 newXY.xOffset += static_cast<int>(pt.x - rcClient.left - halfScreen);
1430 } else {
1431 // Put caret on right
1432 newXY.xOffset += static_cast<int>(pt.x - rcClient.right + 1);
1434 } else {
1435 // Move just enough to allow to display the caret
1436 if (pt.x < rcClient.left) {
1437 // Caret is on the left of the display
1438 if (bEven) {
1439 newXY.xOffset -= static_cast<int>(rcClient.left - pt.x);
1440 } else {
1441 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1443 } else if (pt.x >= rcClient.right) {
1444 // Caret is on the right of the display
1445 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1449 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1450 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1451 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 2;
1452 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1453 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 2;
1454 if (vs.caretStyle == CARETSTYLE_BLOCK) {
1455 // Ensure we can see a good portion of the block caret
1456 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1459 if (!(range.caret == range.anchor)) {
1460 if (ptAnchor.x < pt.x) {
1461 // Shift to left to show anchor or as much of range as possible
1462 int maxOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.left) - 1;
1463 int minOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 1;
1464 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1465 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1466 } else {
1467 // Shift to right to show anchor or as much of range as possible
1468 int minOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.right) + 1;
1469 int maxOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 1;
1470 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1471 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1474 if (newXY.xOffset < 0) {
1475 newXY.xOffset = 0;
1479 return newXY;
1482 void Editor::SetXYScroll(XYScrollPosition newXY) {
1483 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1484 if (newXY.topLine != topLine) {
1485 SetTopLine(newXY.topLine);
1486 SetVerticalScrollPos();
1488 if (newXY.xOffset != xOffset) {
1489 xOffset = newXY.xOffset;
1490 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1491 if (newXY.xOffset > 0) {
1492 PRectangle rcText = GetTextRectangle();
1493 if (horizontalScrollBarVisible &&
1494 rcText.Width() + xOffset > scrollWidth) {
1495 scrollWidth = xOffset + static_cast<int>(rcText.Width());
1496 SetScrollBars();
1499 SetHorizontalScrollPos();
1501 Redraw();
1502 UpdateSystemCaret();
1506 void Editor::ScrollRange(SelectionRange range) {
1507 SetXYScroll(XYScrollToMakeVisible(range, xysDefault));
1510 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1511 SetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret),
1512 static_cast<XYScrollOptions>((useMargin?xysUseMargin:0)|(vert?xysVertical:0)|(horiz?xysHorizontal:0))));
1515 void Editor::ShowCaretAtCurrentPosition() {
1516 if (hasFocus) {
1517 caret.active = true;
1518 caret.on = true;
1519 SetTicking(true);
1520 } else {
1521 caret.active = false;
1522 caret.on = false;
1524 InvalidateCaret();
1527 void Editor::DropCaret() {
1528 caret.active = false;
1529 InvalidateCaret();
1532 void Editor::CaretSetPeriod(int period) {
1533 if (caret.period != period) {
1534 caret.period = period;
1535 caret.on = true;
1536 InvalidateCaret();
1540 void Editor::InvalidateCaret() {
1541 if (posDrag.IsValid()) {
1542 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1543 } else {
1544 for (size_t r=0; r<sel.Count(); r++) {
1545 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1548 UpdateSystemCaret();
1551 void Editor::UpdateSystemCaret() {
1554 bool Editor::Wrapping() const {
1555 return vs.wrapState != eWrapNone;
1558 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1559 //Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd);
1560 if (wrapPending.AddRange(docLineStart, docLineEnd)) {
1561 llc.Invalidate(LineLayout::llPositions);
1563 // Wrap lines during idle.
1564 if (Wrapping() && wrapPending.NeedsWrap()) {
1565 SetIdle(true);
1569 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1570 AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
1571 int linesWrapped = 1;
1572 if (ll) {
1573 LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
1574 linesWrapped = ll->lines;
1576 return cs.SetHeight(lineToWrap, linesWrapped +
1577 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1580 // Perform wrapping for a subset of the lines needing wrapping.
1581 // wsAll: wrap all lines which need wrapping in this single call
1582 // wsVisible: wrap currently visible lines
1583 // wsIdle: wrap one page + 100 lines
1584 // Return true if wrapping occurred.
1585 bool Editor::WrapLines(enum wrapScope ws) {
1586 int goodTopLine = topLine;
1587 bool wrapOccurred = false;
1588 if (!Wrapping()) {
1589 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1590 wrapWidth = LineLayout::wrapWidthInfinite;
1591 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1592 cs.SetHeight(lineDoc, 1 +
1593 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1595 wrapOccurred = true;
1597 wrapPending.Reset();
1599 } else if (wrapPending.NeedsWrap()) {
1600 wrapPending.start = std::min(wrapPending.start, pdoc->LinesTotal());
1601 if (!SetIdle(true)) {
1602 // Idle processing not supported so full wrap required.
1603 ws = wsAll;
1605 // Decide where to start wrapping
1606 int lineToWrap = wrapPending.start;
1607 int lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal());
1608 const int lineDocTop = cs.DocFromDisplay(topLine);
1609 const int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1610 if (ws == wsVisible) {
1611 lineToWrap = Platform::Clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal());
1612 // Priority wrap to just after visible area.
1613 // Since wrapping could reduce display lines, treat each
1614 // as taking only one display line.
1615 lineToWrapEnd = lineDocTop;
1616 int lines = LinesOnScreen() + 1;
1617 while ((lineToWrapEnd < cs.LinesInDoc()) && (lines>0)) {
1618 if (cs.GetVisible(lineToWrapEnd))
1619 lines--;
1620 lineToWrapEnd++;
1622 // .. and if the paint window is outside pending wraps
1623 if ((lineToWrap > wrapPending.end) || (lineToWrapEnd < wrapPending.start)) {
1624 // Currently visible text does not need wrapping
1625 return false;
1627 } else if (ws == wsIdle) {
1628 lineToWrapEnd = lineToWrap + LinesOnScreen() + 100;
1630 const int lineEndNeedWrap = std::min(wrapPending.end, pdoc->LinesTotal());
1631 lineToWrapEnd = std::min(lineToWrapEnd, lineEndNeedWrap);
1633 // Ensure all lines being wrapped are styled.
1634 pdoc->EnsureStyledTo(pdoc->LineStart(lineToWrapEnd));
1636 if (lineToWrap < lineToWrapEnd) {
1638 PRectangle rcTextArea = GetClientRectangle();
1639 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1640 rcTextArea.right -= vs.rightMarginWidth;
1641 wrapWidth = static_cast<int>(rcTextArea.Width());
1642 RefreshStyleData();
1643 AutoSurface surface(this);
1644 if (surface) {
1645 //Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
1647 while (lineToWrap < lineToWrapEnd) {
1648 if (WrapOneLine(surface, lineToWrap)) {
1649 wrapOccurred = true;
1651 wrapPending.Wrapped(lineToWrap);
1652 lineToWrap++;
1655 goodTopLine = cs.DisplayFromDoc(lineDocTop) + std::min(subLineTop, cs.GetHeight(lineDocTop)-1);
1659 // If wrapping is done, bring it to resting position
1660 if (wrapPending.start >= lineEndNeedWrap) {
1661 wrapPending.Reset();
1665 if (wrapOccurred) {
1666 SetScrollBars();
1667 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1668 SetVerticalScrollPos();
1671 return wrapOccurred;
1674 void Editor::LinesJoin() {
1675 if (!RangeContainsProtected(targetStart, targetEnd)) {
1676 UndoGroup ug(pdoc);
1677 bool prevNonWS = true;
1678 for (int pos = targetStart; pos < targetEnd; pos++) {
1679 if (pdoc->IsPositionInLineEnd(pos)) {
1680 targetEnd -= pdoc->LenChar(pos);
1681 pdoc->DelChar(pos);
1682 if (prevNonWS) {
1683 // Ensure at least one space separating previous lines
1684 const int lengthInserted = pdoc->InsertString(pos, " ", 1);
1685 targetEnd += lengthInserted;
1687 } else {
1688 prevNonWS = pdoc->CharAt(pos) != ' ';
1694 const char *Editor::StringFromEOLMode(int eolMode) {
1695 if (eolMode == SC_EOL_CRLF) {
1696 return "\r\n";
1697 } else if (eolMode == SC_EOL_CR) {
1698 return "\r";
1699 } else {
1700 return "\n";
1704 void Editor::LinesSplit(int pixelWidth) {
1705 if (!RangeContainsProtected(targetStart, targetEnd)) {
1706 if (pixelWidth == 0) {
1707 PRectangle rcText = GetTextRectangle();
1708 pixelWidth = static_cast<int>(rcText.Width());
1710 int lineStart = pdoc->LineFromPosition(targetStart);
1711 int lineEnd = pdoc->LineFromPosition(targetEnd);
1712 const char *eol = StringFromEOLMode(pdoc->eolMode);
1713 UndoGroup ug(pdoc);
1714 for (int line = lineStart; line <= lineEnd; line++) {
1715 AutoSurface surface(this);
1716 AutoLineLayout ll(llc, RetrieveLineLayout(line));
1717 if (surface && ll) {
1718 unsigned int posLineStart = pdoc->LineStart(line);
1719 LayoutLine(line, surface, vs, ll, pixelWidth);
1720 int lengthInsertedTotal = 0;
1721 for (int subLine = 1; subLine < ll->lines; subLine++) {
1722 const int lengthInserted = pdoc->InsertString(
1723 static_cast<int>(posLineStart + lengthInsertedTotal +
1724 ll->LineStart(subLine)),
1725 eol, istrlen(eol));
1726 targetEnd += lengthInserted;
1727 lengthInsertedTotal += lengthInserted;
1730 lineEnd = pdoc->LineFromPosition(targetEnd);
1735 int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) const {
1736 if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
1737 return markerDefault;
1738 return markerCheck;
1741 bool ValidStyledText(const ViewStyle &vs, size_t styleOffset, const StyledText &st) {
1742 if (st.multipleStyles) {
1743 for (size_t iStyle=0; iStyle<st.length; iStyle++) {
1744 if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
1745 return false;
1747 } else {
1748 if (!vs.ValidStyle(styleOffset + st.style))
1749 return false;
1751 return true;
1754 static int WidthStyledText(Surface *surface, const ViewStyle &vs, int styleOffset,
1755 const char *text, const unsigned char *styles, size_t len) {
1756 int width = 0;
1757 size_t start = 0;
1758 while (start < len) {
1759 size_t style = styles[start];
1760 size_t endSegment = start;
1761 while ((endSegment+1 < len) && (static_cast<size_t>(styles[endSegment+1]) == style))
1762 endSegment++;
1763 FontAlias fontText = vs.styles[style + styleOffset].font;
1764 width += static_cast<int>(surface->WidthText(fontText, text + start,
1765 static_cast<int>(endSegment - start + 1)));
1766 start = endSegment + 1;
1768 return width;
1771 static int WidestLineWidth(Surface *surface, const ViewStyle &vs, int styleOffset, const StyledText &st) {
1772 int widthMax = 0;
1773 size_t start = 0;
1774 while (start < st.length) {
1775 size_t lenLine = st.LineLength(start);
1776 int widthSubLine;
1777 if (st.multipleStyles) {
1778 widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
1779 } else {
1780 FontAlias fontText = vs.styles[styleOffset + st.style].font;
1781 widthSubLine = static_cast<int>(surface->WidthText(fontText,
1782 st.text + start, static_cast<int>(lenLine)));
1784 if (widthSubLine > widthMax)
1785 widthMax = widthSubLine;
1786 start += lenLine + 1;
1788 return widthMax;
1791 static void DrawTextInStyle(Surface *surface, PRectangle rcText, const Style &style, XYPOSITION ybase, const char *s, size_t length) {
1792 FontAlias fontText = style.font;
1793 surface->DrawTextNoClip(rcText, fontText, ybase, s, static_cast<int>(length),
1794 style.fore, style.back);
1797 static void DrawStyledText(Surface *surface, const ViewStyle &vs, int styleOffset, PRectangle rcText,
1798 const StyledText &st, size_t start, size_t length) {
1800 if (st.multipleStyles) {
1801 int x = static_cast<int>(rcText.left);
1802 size_t i = 0;
1803 while (i < length) {
1804 size_t end = i;
1805 size_t style = st.styles[i + start];
1806 while (end < length-1 && st.styles[start+end+1] == style)
1807 end++;
1808 style += styleOffset;
1809 FontAlias fontText = vs.styles[style].font;
1810 const int width = static_cast<int>(surface->WidthText(fontText,
1811 st.text + start + i, static_cast<int>(end - i + 1)));
1812 PRectangle rcSegment = rcText;
1813 rcSegment.left = static_cast<XYPOSITION>(x);
1814 rcSegment.right = static_cast<XYPOSITION>(x + width + 1);
1815 DrawTextInStyle(surface, rcSegment, vs.styles[style], rcText.top + vs.maxAscent,
1816 st.text + start + i, end - i + 1);
1817 x += width;
1818 i = end + 1;
1820 } else {
1821 const size_t style = st.style + styleOffset;
1822 DrawTextInStyle(surface, rcText, vs.styles[style], rcText.top + vs.maxAscent,
1823 st.text + start, length);
1827 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1828 if (vs.fixedColumnWidth == 0)
1829 return;
1831 AllocateGraphics();
1832 RefreshStyleData();
1833 RefreshPixMaps(surfWindow);
1835 // On GTK+ with Ubuntu overlay scroll bars, the surface may have been finished
1836 // at this point. The Initialised call checks for this case and sets the status
1837 // to be bad which avoids crashes in following calls.
1838 if (!surfWindow->Initialised()) {
1839 return;
1842 PRectangle rcMargin = GetClientRectangle();
1843 Point ptOrigin = GetVisibleOriginInMain();
1844 rcMargin.Move(0, -ptOrigin.y);
1845 rcMargin.left = 0;
1846 rcMargin.right = static_cast<XYPOSITION>(vs.fixedColumnWidth);
1848 if (!rc.Intersects(rcMargin))
1849 return;
1851 Surface *surface;
1852 if (bufferedDraw) {
1853 surface = pixmapSelMargin;
1854 } else {
1855 surface = surfWindow;
1858 // Clip vertically to paint area to avoid drawing line numbers
1859 if (rcMargin.bottom > rc.bottom)
1860 rcMargin.bottom = rc.bottom;
1861 if (rcMargin.top < rc.top)
1862 rcMargin.top = rc.top;
1864 PRectangle rcSelMargin = rcMargin;
1865 rcSelMargin.right = rcMargin.left;
1866 if (rcSelMargin.bottom < rc.bottom)
1867 rcSelMargin.bottom = rc.bottom;
1869 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
1870 if (vs.ms[margin].width > 0) {
1872 rcSelMargin.left = rcSelMargin.right;
1873 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
1875 if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
1876 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1877 // Required because of special way brush is created for selection margin
1878 // Ensure patterns line up when scrolling with separate margin view
1879 // by choosing correctly aligned variant.
1880 bool invertPhase = static_cast<int>(ptOrigin.y) & 1;
1881 surface->FillRectangle(rcSelMargin,
1882 invertPhase ? *pixmapSelPattern : *pixmapSelPatternOffset1);
1883 } else {
1884 ColourDesired colour;
1885 switch (vs.ms[margin].style) {
1886 case SC_MARGIN_BACK:
1887 colour = vs.styles[STYLE_DEFAULT].back;
1888 break;
1889 case SC_MARGIN_FORE:
1890 colour = vs.styles[STYLE_DEFAULT].fore;
1891 break;
1892 default:
1893 colour = vs.styles[STYLE_LINENUMBER].back;
1894 break;
1896 surface->FillRectangle(rcSelMargin, colour);
1898 } else {
1899 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back);
1902 const int lineStartPaint = static_cast<int>(rcMargin.top + ptOrigin.y) / vs.lineHeight;
1903 int visibleLine = TopLineOfMain() + lineStartPaint;
1904 int yposScreen = lineStartPaint * vs.lineHeight - static_cast<int>(ptOrigin.y);
1905 // Work out whether the top line is whitespace located after a
1906 // lessening of fold level which implies a 'fold tail' but which should not
1907 // be displayed until the last of a sequence of whitespace.
1908 bool needWhiteClosure = false;
1909 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1910 int level = pdoc->GetLevel(cs.DocFromDisplay(visibleLine));
1911 if (level & SC_FOLDLEVELWHITEFLAG) {
1912 int lineBack = cs.DocFromDisplay(visibleLine);
1913 int levelPrev = level;
1914 while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
1915 lineBack--;
1916 levelPrev = pdoc->GetLevel(lineBack);
1918 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
1919 if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK))
1920 needWhiteClosure = true;
1923 if (highlightDelimiter.isEnabled) {
1924 int lastLine = cs.DocFromDisplay(topLine + LinesOnScreen()) + 1;
1925 pdoc->GetHighlightDelimiters(highlightDelimiter, pdoc->LineFromPosition(CurrentPosition()), lastLine);
1929 // Old code does not know about new markers needed to distinguish all cases
1930 const int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
1931 SC_MARKNUM_FOLDEROPEN);
1932 const int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
1933 SC_MARKNUM_FOLDER);
1935 while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rc.bottom) {
1937 PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed());
1938 const int lineDoc = cs.DocFromDisplay(visibleLine);
1939 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
1940 const bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
1941 const bool lastSubLine = visibleLine == cs.DisplayLastFromDoc(lineDoc);
1943 int marks = pdoc->GetMark(lineDoc);
1944 if (!firstSubLine)
1945 marks = 0;
1947 bool headWithTail = false;
1949 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1950 // Decide which fold indicator should be displayed
1951 const int level = pdoc->GetLevel(lineDoc);
1952 const int levelNext = pdoc->GetLevel(lineDoc + 1);
1953 const int levelNum = level & SC_FOLDLEVELNUMBERMASK;
1954 const int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
1955 if (level & SC_FOLDLEVELHEADERFLAG) {
1956 if (firstSubLine) {
1957 if (levelNum < levelNextNum) {
1958 if (cs.GetExpanded(lineDoc)) {
1959 if (levelNum == SC_FOLDLEVELBASE)
1960 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
1961 else
1962 marks |= 1 << folderOpenMid;
1963 } else {
1964 if (levelNum == SC_FOLDLEVELBASE)
1965 marks |= 1 << SC_MARKNUM_FOLDER;
1966 else
1967 marks |= 1 << folderEnd;
1969 } else if (levelNum > SC_FOLDLEVELBASE) {
1970 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1972 } else {
1973 if (levelNum < levelNextNum) {
1974 if (cs.GetExpanded(lineDoc)) {
1975 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1976 } else if (levelNum > SC_FOLDLEVELBASE) {
1977 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1979 } else if (levelNum > SC_FOLDLEVELBASE) {
1980 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1983 needWhiteClosure = false;
1984 const int firstFollowupLine = cs.DocFromDisplay(cs.DisplayFromDoc(lineDoc + 1));
1985 const int firstFollowupLineLevel = pdoc->GetLevel(firstFollowupLine);
1986 const int secondFollowupLineLevelNum = pdoc->GetLevel(firstFollowupLine + 1) & SC_FOLDLEVELNUMBERMASK;
1987 if (!cs.GetExpanded(lineDoc)) {
1988 if ((firstFollowupLineLevel & SC_FOLDLEVELWHITEFLAG) &&
1989 (levelNum > secondFollowupLineLevelNum))
1990 needWhiteClosure = true;
1992 if (highlightDelimiter.IsFoldBlockHighlighted(firstFollowupLine))
1993 headWithTail = true;
1995 } else if (level & SC_FOLDLEVELWHITEFLAG) {
1996 if (needWhiteClosure) {
1997 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1998 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1999 } else if (levelNextNum > SC_FOLDLEVELBASE) {
2000 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
2001 needWhiteClosure = false;
2002 } else {
2003 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
2004 needWhiteClosure = false;
2006 } else if (levelNum > SC_FOLDLEVELBASE) {
2007 if (levelNextNum < levelNum) {
2008 if (levelNextNum > SC_FOLDLEVELBASE) {
2009 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
2010 } else {
2011 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
2013 } else {
2014 marks |= 1 << SC_MARKNUM_FOLDERSUB;
2017 } else if (levelNum > SC_FOLDLEVELBASE) {
2018 if (levelNextNum < levelNum) {
2019 needWhiteClosure = false;
2020 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
2021 marks |= 1 << SC_MARKNUM_FOLDERSUB;
2022 needWhiteClosure = true;
2023 } else if (lastSubLine) {
2024 if (levelNextNum > SC_FOLDLEVELBASE) {
2025 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
2026 } else {
2027 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
2029 } else {
2030 marks |= 1 << SC_MARKNUM_FOLDERSUB;
2032 } else {
2033 marks |= 1 << SC_MARKNUM_FOLDERSUB;
2038 marks &= vs.ms[margin].mask;
2040 PRectangle rcMarker = rcSelMargin;
2041 rcMarker.top = static_cast<XYPOSITION>(yposScreen);
2042 rcMarker.bottom = static_cast<XYPOSITION>(yposScreen + vs.lineHeight);
2043 if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
2044 if (firstSubLine) {
2045 char number[100] = "";
2046 if (lineDoc >= 0)
2047 sprintf(number, "%d", lineDoc + 1);
2048 if (foldFlags & (SC_FOLDFLAG_LEVELNUMBERS | SC_FOLDFLAG_LINESTATE)) {
2049 if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
2050 int lev = pdoc->GetLevel(lineDoc);
2051 sprintf(number, "%c%c %03X %03X",
2052 (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
2053 (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
2054 lev & SC_FOLDLEVELNUMBERMASK,
2055 lev >> 16
2057 } else {
2058 int state = pdoc->GetLineState(lineDoc);
2059 sprintf(number, "%0X", state);
2062 PRectangle rcNumber = rcMarker;
2063 // Right justify
2064 XYPOSITION width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
2065 XYPOSITION xpos = rcNumber.right - width - vs.marginNumberPadding;
2066 rcNumber.left = xpos;
2067 DrawTextInStyle(surface, rcNumber, vs.styles[STYLE_LINENUMBER],
2068 rcNumber.top + vs.maxAscent, number, strlen(number));
2069 } else if (vs.wrapVisualFlags & SC_WRAPVISUALFLAG_MARGIN) {
2070 PRectangle rcWrapMarker = rcMarker;
2071 rcWrapMarker.right -= 3;
2072 rcWrapMarker.left = rcWrapMarker.right - vs.styles[STYLE_LINENUMBER].aveCharWidth;
2073 DrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
2075 } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
2076 if (firstSubLine) {
2077 const StyledText stMargin = pdoc->MarginStyledText(lineDoc);
2078 if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
2079 surface->FillRectangle(rcMarker,
2080 vs.styles[stMargin.StyleAt(0)+vs.marginStyleOffset].back);
2081 if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
2082 int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
2083 rcMarker.left = rcMarker.right - width - 3;
2085 DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker,
2086 stMargin, 0, stMargin.length);
2091 if (marks) {
2092 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
2093 if (marks & 1) {
2094 LineMarker::typeOfFold tFold = LineMarker::undefined;
2095 if ((vs.ms[margin].mask & SC_MASK_FOLDERS) && highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) {
2096 if (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) {
2097 tFold = LineMarker::body;
2098 } else if (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) {
2099 if (firstSubLine) {
2100 tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head;
2101 } else {
2102 if (cs.GetExpanded(lineDoc) || headWithTail) {
2103 tFold = LineMarker::body;
2104 } else {
2105 tFold = LineMarker::undefined;
2108 } else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) {
2109 tFold = LineMarker::tail;
2112 vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font, tFold, vs.ms[margin].style);
2114 marks >>= 1;
2118 visibleLine++;
2119 yposScreen += vs.lineHeight;
2124 PRectangle rcBlankMargin = rcMargin;
2125 rcBlankMargin.left = rcSelMargin.right;
2126 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back);
2128 if (bufferedDraw) {
2129 surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *pixmapSelMargin);
2133 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
2134 int ydiff = static_cast<int>(rcTab.bottom - rcTab.top) / 2;
2135 int xhead = static_cast<int>(rcTab.right) - 1 - ydiff;
2136 if (xhead <= rcTab.left) {
2137 ydiff -= static_cast<int>(rcTab.left) - xhead - 1;
2138 xhead = static_cast<int>(rcTab.left) - 1;
2140 if ((rcTab.left + 2) < (rcTab.right - 1))
2141 surface->MoveTo(static_cast<int>(rcTab.left) + 2, ymid);
2142 else
2143 surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
2144 surface->LineTo(static_cast<int>(rcTab.right) - 1, ymid);
2145 surface->LineTo(xhead, ymid - ydiff);
2146 surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
2147 surface->LineTo(xhead, ymid + ydiff);
2150 LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
2151 int posLineStart = pdoc->LineStart(lineNumber);
2152 int posLineEnd = pdoc->LineStart(lineNumber + 1);
2153 PLATFORM_ASSERT(posLineEnd >= posLineStart);
2154 int lineCaret = pdoc->LineFromPosition(sel.MainCaret());
2155 return llc.Retrieve(lineNumber, lineCaret,
2156 posLineEnd - posLineStart, pdoc->GetStyleClock(),
2157 LinesOnScreen() + 1, pdoc->LinesTotal());
2161 * Fill in the LineLayout data for the given line.
2162 * Copy the given @a line and its styles from the document into local arrays.
2163 * Also determine the x position at which each character starts.
2165 void Editor::LayoutLine(int line, Surface *surface, const ViewStyle &vstyle, LineLayout *ll, int width) {
2166 if (!ll)
2167 return;
2169 PLATFORM_ASSERT(line < pdoc->LinesTotal());
2170 PLATFORM_ASSERT(ll->chars != NULL);
2171 int posLineStart = pdoc->LineStart(line);
2172 int posLineEnd = pdoc->LineStart(line + 1);
2173 // If the line is very long, limit the treatment to a length that should fit in the viewport
2174 if (posLineEnd > (posLineStart + ll->maxLineLength)) {
2175 posLineEnd = posLineStart + ll->maxLineLength;
2177 if (ll->validity == LineLayout::llCheckTextAndStyle) {
2178 int lineLength = posLineEnd - posLineStart;
2179 if (!vstyle.viewEOL) {
2180 lineLength = pdoc->LineEnd(line) - posLineStart;
2182 if (lineLength == ll->numCharsInLine) {
2183 // See if chars, styles, indicators, are all the same
2184 bool allSame = true;
2185 // Check base line layout
2186 char styleByte = 0;
2187 int numCharsInLine = 0;
2188 while (numCharsInLine < lineLength) {
2189 int charInDoc = numCharsInLine + posLineStart;
2190 char chDoc = pdoc->CharAt(charInDoc);
2191 styleByte = pdoc->StyleAt(charInDoc);
2192 allSame = allSame &&
2193 (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte));
2194 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
2195 allSame = allSame &&
2196 (ll->chars[numCharsInLine] == chDoc);
2197 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
2198 allSame = allSame &&
2199 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
2200 else // Style::caseUpper
2201 allSame = allSame &&
2202 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
2203 numCharsInLine++;
2205 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
2206 if (allSame) {
2207 ll->validity = LineLayout::llPositions;
2208 } else {
2209 ll->validity = LineLayout::llInvalid;
2211 } else {
2212 ll->validity = LineLayout::llInvalid;
2215 if (ll->validity == LineLayout::llInvalid) {
2216 ll->widthLine = LineLayout::wrapWidthInfinite;
2217 ll->lines = 1;
2218 if (vstyle.edgeState == EDGE_BACKGROUND) {
2219 ll->edgeColumn = pdoc->FindColumn(line, vstyle.theEdge);
2220 if (ll->edgeColumn >= posLineStart) {
2221 ll->edgeColumn -= posLineStart;
2223 } else {
2224 ll->edgeColumn = -1;
2227 // Fill base line layout
2228 const int lineLength = posLineEnd - posLineStart;
2229 pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
2230 pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
2231 int numCharsBeforeEOL = pdoc->LineEnd(line) - posLineStart;
2232 const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
2233 for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
2234 const unsigned char styleByte = ll->styles[styleInLine];
2235 ll->styles[styleInLine] = styleByte;
2237 const unsigned char styleByteLast = (lineLength > 0) ? ll->styles[lineLength-1] : 0;
2238 if (vstyle.someStylesForceCase) {
2239 for (int charInLine = 0; charInLine<lineLength; charInLine++) {
2240 char chDoc = ll->chars[charInLine];
2241 if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
2242 ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
2243 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
2244 ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
2247 ll->xHighlightGuide = 0;
2248 // Extra element at the end of the line to hold end x position and act as
2249 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
2250 ll->styles[numCharsInLine] = styleByteLast; // For eolFilled
2252 // Layout the line, determining the position of each character,
2253 // with an extra element at the end for the end of the line.
2254 ll->positions[0] = 0;
2255 bool lastSegItalics = false;
2257 BreakFinder bfLayout(ll, NULL, 0, numCharsInLine, posLineStart, 0, false, pdoc, &reprs);
2258 while (bfLayout.More()) {
2260 const TextSegment ts = bfLayout.Next();
2262 std::fill(&ll->positions[ts.start+1], &ll->positions[ts.end()+1], 0.0f);
2263 if (vstyle.styles[ll->styles[ts.start]].visible) {
2264 if (ts.representation) {
2265 XYPOSITION representationWidth = vstyle.controlCharWidth;
2266 if (ll->chars[ts.start] == '\t') {
2267 // Tab is a special case of representation, taking a variable amount of space
2268 representationWidth =
2269 ((static_cast<int>((ll->positions[ts.start] + 2) / vstyle.tabWidth) + 1) * vstyle.tabWidth) - ll->positions[ts.start];
2270 } else {
2271 if (representationWidth <= 0.0) {
2272 XYPOSITION positionsRepr[256]; // Should expand when needed
2273 posCache.MeasureWidths(surface, vstyle, STYLE_CONTROLCHAR, ts.representation->stringRep.c_str(),
2274 static_cast<unsigned int>(ts.representation->stringRep.length()), positionsRepr, pdoc);
2275 representationWidth = positionsRepr[ts.representation->stringRep.length()-1] + vstyle.ctrlCharPadding;
2278 for (int ii=0; ii < ts.length; ii++)
2279 ll->positions[ts.start + 1 + ii] = representationWidth;
2280 } else {
2281 if ((ts.length == 1) && (' ' == ll->chars[ts.start])) {
2282 // Over half the segments are single characters and of these about half are space characters.
2283 ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth;
2284 } else {
2285 posCache.MeasureWidths(surface, vstyle, ll->styles[ts.start], ll->chars + ts.start,
2286 ts.length, ll->positions + ts.start + 1, pdoc);
2289 lastSegItalics = (!ts.representation) && ((ll->chars[ts.end()-1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic);
2292 for (int posToIncrease = ts.start+1; posToIncrease <= ts.end(); posToIncrease++) {
2293 ll->positions[posToIncrease] += ll->positions[ts.start];
2297 // Small hack to make lines that end with italics not cut off the edge of the last character
2298 if (lastSegItalics) {
2299 ll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset;
2301 ll->numCharsInLine = numCharsInLine;
2302 ll->numCharsBeforeEOL = numCharsBeforeEOL;
2303 ll->validity = LineLayout::llPositions;
2305 // Hard to cope when too narrow, so just assume there is space
2306 if (width < 20) {
2307 width = 20;
2309 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
2310 ll->widthLine = width;
2311 if (width == LineLayout::wrapWidthInfinite) {
2312 ll->lines = 1;
2313 } else if (width > ll->positions[ll->numCharsInLine]) {
2314 // Simple common case where line does not need wrapping.
2315 ll->lines = 1;
2316 } else {
2317 if (vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2318 width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
2320 XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
2321 if (vstyle.wrapIndentMode == SC_WRAPINDENT_INDENT) {
2322 wrapAddIndent = pdoc->IndentSize() * vstyle.spaceWidth;
2323 } else if (vstyle.wrapIndentMode == SC_WRAPINDENT_FIXED) {
2324 wrapAddIndent = vstyle.wrapVisualStartIndent * vstyle.aveCharWidth;
2326 ll->wrapIndent = wrapAddIndent;
2327 if (vstyle.wrapIndentMode != SC_WRAPINDENT_FIXED)
2328 for (int i = 0; i < ll->numCharsInLine; i++) {
2329 if (!IsSpaceOrTab(ll->chars[i])) {
2330 ll->wrapIndent += ll->positions[i]; // Add line indent
2331 break;
2334 // Check for text width minimum
2335 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
2336 ll->wrapIndent = wrapAddIndent;
2337 // Check for wrapIndent minimum
2338 if ((vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
2339 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
2340 ll->lines = 0;
2341 // Calculate line start positions based upon width.
2342 int lastGoodBreak = 0;
2343 int lastLineStart = 0;
2344 XYACCUMULATOR startOffset = 0;
2345 int p = 0;
2346 while (p < ll->numCharsInLine) {
2347 if ((ll->positions[p + 1] - startOffset) >= width) {
2348 if (lastGoodBreak == lastLineStart) {
2349 // Try moving to start of last character
2350 if (p > 0) {
2351 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2352 - posLineStart;
2354 if (lastGoodBreak == lastLineStart) {
2355 // Ensure at least one character on line.
2356 lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
2357 - posLineStart;
2360 lastLineStart = lastGoodBreak;
2361 ll->lines++;
2362 ll->SetLineStart(ll->lines, lastGoodBreak);
2363 startOffset = ll->positions[lastGoodBreak];
2364 // take into account the space for start wrap mark and indent
2365 startOffset -= ll->wrapIndent;
2366 p = lastGoodBreak + 1;
2367 continue;
2369 if (p > 0) {
2370 if (vstyle.wrapState == eWrapChar) {
2371 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2372 - posLineStart;
2373 p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
2374 continue;
2375 } else if ((vstyle.wrapState == eWrapWord) && (ll->styles[p] != ll->styles[p - 1])) {
2376 lastGoodBreak = p;
2377 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
2378 lastGoodBreak = p;
2381 p++;
2383 ll->lines++;
2385 ll->validity = LineLayout::llLines;
2389 ColourDesired Editor::SelectionBackground(const ViewStyle &vsDraw, bool main) const {
2390 return main ?
2391 (primarySelection ? vsDraw.selColours.back : vsDraw.selBackground2) :
2392 vsDraw.selAdditionalBackground;
2395 ColourDesired Editor::TextBackground(const ViewStyle &vsDraw,
2396 ColourOptional background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) const {
2397 if (inSelection == 1) {
2398 if (vsDraw.selColours.back.isSet && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
2399 return SelectionBackground(vsDraw, true);
2401 } else if (inSelection == 2) {
2402 if (vsDraw.selColours.back.isSet && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
2403 return SelectionBackground(vsDraw, false);
2405 } else {
2406 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
2407 (i >= ll->edgeColumn) &&
2408 (i < ll->numCharsBeforeEOL))
2409 return vsDraw.edgecolour;
2410 if (inHotspot && vsDraw.hotspotColours.back.isSet)
2411 return vsDraw.hotspotColours.back;
2413 if (background.isSet && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
2414 return background;
2415 } else {
2416 return vsDraw.styles[styleMain].back;
2420 void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
2421 Point from = Point::FromInts(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
2422 PRectangle rcCopyArea = PRectangle::FromInts(start + 1, static_cast<int>(rcSegment.top), start + 2, static_cast<int>(rcSegment.bottom));
2423 surface->Copy(rcCopyArea, from,
2424 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
2427 void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
2428 bool isEndMarker, ColourDesired wrapColour) {
2429 surface->PenColour(wrapColour);
2431 enum { xa = 1 }; // gap before start
2432 int w = static_cast<int>(rcPlace.right - rcPlace.left) - xa - 1;
2434 bool xStraight = isEndMarker; // x-mirrored symbol for start marker
2436 int x0 = static_cast<int>(xStraight ? rcPlace.left : rcPlace.right - 1);
2437 int y0 = static_cast<int>(rcPlace.top);
2439 int dy = static_cast<int>(rcPlace.bottom - rcPlace.top) / 5;
2440 int y = static_cast<int>(rcPlace.bottom - rcPlace.top) / 2 + dy;
2442 struct Relative {
2443 Surface *surface;
2444 int xBase;
2445 int xDir;
2446 int yBase;
2447 int yDir;
2448 void MoveTo(int xRelative, int yRelative) {
2449 surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2451 void LineTo(int xRelative, int yRelative) {
2452 surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2455 Relative rel = {surface, x0, xStraight ? 1 : -1, y0, 1};
2457 // arrow head
2458 rel.MoveTo(xa, y);
2459 rel.LineTo(xa + 2*w / 3, y - dy);
2460 rel.MoveTo(xa, y);
2461 rel.LineTo(xa + 2*w / 3, y + dy);
2463 // arrow body
2464 rel.MoveTo(xa, y);
2465 rel.LineTo(xa + w, y);
2466 rel.LineTo(xa + w, y - 2 * dy);
2467 rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
2468 y - 2 * dy);
2471 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
2472 if (alpha != SC_ALPHA_NOALPHA) {
2473 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
2477 void DrawTextBlob(Surface *surface, const ViewStyle &vsDraw, PRectangle rcSegment,
2478 const char *s, ColourDesired textBack, ColourDesired textFore, bool twoPhaseDraw) {
2479 if (!twoPhaseDraw) {
2480 surface->FillRectangle(rcSegment, textBack);
2482 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2483 int normalCharHeight = static_cast<int>(surface->Ascent(ctrlCharsFont) -
2484 surface->InternalLeading(ctrlCharsFont));
2485 PRectangle rcCChar = rcSegment;
2486 rcCChar.left = rcCChar.left + 1;
2487 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
2488 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
2489 PRectangle rcCentral = rcCChar;
2490 rcCentral.top++;
2491 rcCentral.bottom--;
2492 surface->FillRectangle(rcCentral, textFore);
2493 PRectangle rcChar = rcCChar;
2494 rcChar.left++;
2495 rcChar.right--;
2496 surface->DrawTextClipped(rcChar, ctrlCharsFont,
2497 rcSegment.top + vsDraw.maxAscent, s, istrlen(s),
2498 textBack, textFore);
2501 void Editor::DrawEOL(Surface *surface, const ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
2502 int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
2503 ColourOptional background) {
2505 const int posLineStart = pdoc->LineStart(line);
2506 PRectangle rcSegment = rcLine;
2508 const bool lastSubLine = subLine == (ll->lines - 1);
2509 XYPOSITION virtualSpace = 0;
2510 if (lastSubLine) {
2511 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
2512 virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * spaceWidth;
2514 XYPOSITION xEol = static_cast<XYPOSITION>(ll->positions[lineEnd] - subLineStart);
2516 // Fill the virtual space and show selections within it
2517 if (virtualSpace) {
2518 rcSegment.left = xEol + xStart;
2519 rcSegment.right = xEol + xStart + virtualSpace;
2520 surface->FillRectangle(rcSegment, background.isSet ? background : vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
2521 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
2522 SelectionSegment virtualSpaceRange(SelectionPosition(pdoc->LineEnd(line)), SelectionPosition(pdoc->LineEnd(line), sel.VirtualSpaceFor(pdoc->LineEnd(line))));
2523 for (size_t r=0; r<sel.Count(); r++) {
2524 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2525 if (alpha == SC_ALPHA_NOALPHA) {
2526 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
2527 if (!portion.Empty()) {
2528 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
2529 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
2530 static_cast<XYPOSITION>(subLineStart) + portion.start.VirtualSpace() * spaceWidth;
2531 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
2532 static_cast<XYPOSITION>(subLineStart) + portion.end.VirtualSpace() * spaceWidth;
2533 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
2534 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
2535 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == sel.Main()));
2542 int eolInSelection = 0;
2543 int alpha = SC_ALPHA_NOALPHA;
2544 if (!hideSelection) {
2545 int posAfterLineEnd = pdoc->LineStart(line + 1);
2546 eolInSelection = (subLine == (ll->lines - 1)) ? sel.InSelectionForEOL(posAfterLineEnd) : 0;
2547 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2550 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
2551 XYPOSITION blobsWidth = 0;
2552 if (lastSubLine) {
2553 for (int eolPos=ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
2554 rcSegment.left = xStart + ll->positions[eolPos] - static_cast<XYPOSITION>(subLineStart) + virtualSpace;
2555 rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast<XYPOSITION>(subLineStart) + virtualSpace;
2556 blobsWidth += rcSegment.Width();
2557 char hexits[4];
2558 const char *ctrlChar;
2559 unsigned char chEOL = ll->chars[eolPos];
2560 int styleMain = ll->styles[eolPos];
2561 ColourDesired textBack = TextBackground(vsDraw, background, eolInSelection, false, styleMain, eolPos, ll);
2562 if (UTF8IsAscii(chEOL)) {
2563 ctrlChar = ControlCharacterString(chEOL);
2564 } else {
2565 const Representation *repr = reprs.RepresentationFromCharacter(ll->chars + eolPos, ll->numCharsInLine - eolPos);
2566 if (repr) {
2567 ctrlChar = repr->stringRep.c_str();
2568 eolPos = ll->numCharsInLine;
2569 } else {
2570 sprintf(hexits, "x%2X", chEOL);
2571 ctrlChar = hexits;
2574 ColourDesired textFore = vsDraw.styles[styleMain].fore;
2575 if (eolInSelection && vsDraw.selColours.fore.isSet) {
2576 textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
2578 if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1)) {
2579 if (alpha == SC_ALPHA_NOALPHA) {
2580 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2581 } else {
2582 surface->FillRectangle(rcSegment, textBack);
2584 } else {
2585 surface->FillRectangle(rcSegment, textBack);
2587 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
2588 if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2589 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2594 // Draw the eol-is-selected rectangle
2595 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
2596 rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
2598 if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2599 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2600 } else {
2601 if (background.isSet) {
2602 surface->FillRectangle(rcSegment, background);
2603 } else if (line < pdoc->LinesTotal() - 1) {
2604 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
2605 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
2606 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
2607 } else {
2608 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
2610 if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2611 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2615 // Fill the remainder of the line
2616 rcSegment.left = rcSegment.right;
2617 if (rcSegment.left < rcLine.left)
2618 rcSegment.left = rcLine.left;
2619 rcSegment.right = rcLine.right;
2621 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2622 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2623 } else {
2624 if (background.isSet) {
2625 surface->FillRectangle(rcSegment, background);
2626 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine]].eolFilled) {
2627 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine]].back);
2628 } else {
2629 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
2631 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2632 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2636 bool drawWrapMarkEnd = false;
2638 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2639 if (subLine + 1 < ll->lines) {
2640 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
2644 if (drawWrapMarkEnd) {
2645 PRectangle rcPlace = rcSegment;
2647 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
2648 rcPlace.left = xEol + xStart + virtualSpace;
2649 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2650 } else {
2651 // rcLine is clipped to text area
2652 rcPlace.right = rcLine.right;
2653 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2655 DrawWrapMarker(surface, rcPlace, true, vsDraw.WrapColour());
2659 void Editor::DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, const ViewStyle &vsDraw,
2660 int xStart, PRectangle rcLine, LineLayout *ll, int subLine) {
2661 const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
2662 PRectangle rcIndic(
2663 ll->positions[startPos] + xStart - subLineStart,
2664 rcLine.top + vsDraw.maxAscent,
2665 ll->positions[endPos] + xStart - subLineStart,
2666 rcLine.top + vsDraw.maxAscent + 3);
2667 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine);
2670 void Editor::DrawIndicators(Surface *surface, const ViewStyle &vsDraw, int line, int xStart,
2671 PRectangle rcLine, LineLayout *ll, int subLine, int lineEnd, bool under) {
2672 // Draw decorators
2673 const int posLineStart = pdoc->LineStart(line);
2674 const int lineStart = ll->LineStart(subLine);
2675 const int posLineEnd = posLineStart + lineEnd;
2677 for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
2678 if (under == vsDraw.indicators[deco->indicator].under) {
2679 int startPos = posLineStart + lineStart;
2680 if (!deco->rs.ValueAt(startPos)) {
2681 startPos = deco->rs.EndRun(startPos);
2683 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
2684 int endPos = deco->rs.EndRun(startPos);
2685 if (endPos > posLineEnd)
2686 endPos = posLineEnd;
2687 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
2688 surface, vsDraw, xStart, rcLine, ll, subLine);
2689 startPos = endPos;
2690 if (!deco->rs.ValueAt(startPos)) {
2691 startPos = deco->rs.EndRun(startPos);
2697 // Use indicators to highlight matching braces
2698 if ((vsDraw.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
2699 (vsDraw.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
2700 int braceIndicator = (bracesMatchStyle == STYLE_BRACELIGHT) ? vsDraw.braceHighlightIndicator : vsDraw.braceBadLightIndicator;
2701 if (under == vsDraw.indicators[braceIndicator].under) {
2702 Range rangeLine(posLineStart + lineStart, posLineEnd);
2703 if (rangeLine.ContainsCharacter(braces[0])) {
2704 int braceOffset = braces[0] - posLineStart;
2705 if (braceOffset < ll->numCharsInLine) {
2706 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2709 if (rangeLine.ContainsCharacter(braces[1])) {
2710 int braceOffset = braces[1] - posLineStart;
2711 if (braceOffset < ll->numCharsInLine) {
2712 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2719 void Editor::DrawAnnotation(Surface *surface, const ViewStyle &vsDraw, int line, int xStart,
2720 PRectangle rcLine, LineLayout *ll, int subLine) {
2721 int indent = static_cast<int>(pdoc->GetLineIndentation(line) * vsDraw.spaceWidth);
2722 PRectangle rcSegment = rcLine;
2723 int annotationLine = subLine - ll->lines;
2724 const StyledText stAnnotation = pdoc->AnnotationStyledText(line);
2725 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
2726 surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
2727 rcSegment.left = static_cast<XYPOSITION>(xStart);
2728 if (trackLineWidth || (vsDraw.annotationVisible == ANNOTATION_BOXED)) {
2729 // Only care about calculating width if tracking or need to draw box
2730 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
2731 if (vsDraw.annotationVisible == ANNOTATION_BOXED) {
2732 widthAnnotation += static_cast<int>(vsDraw.spaceWidth * 2); // Margins
2734 if (widthAnnotation > lineWidthMaxSeen)
2735 lineWidthMaxSeen = widthAnnotation;
2736 if (vsDraw.annotationVisible == ANNOTATION_BOXED) {
2737 rcSegment.left = static_cast<XYPOSITION>(xStart + indent);
2738 rcSegment.right = rcSegment.left + widthAnnotation;
2741 const int annotationLines = pdoc->AnnotationLines(line);
2742 size_t start = 0;
2743 size_t lengthAnnotation = stAnnotation.LineLength(start);
2744 int lineInAnnotation = 0;
2745 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
2746 start += lengthAnnotation + 1;
2747 lengthAnnotation = stAnnotation.LineLength(start);
2748 lineInAnnotation++;
2750 PRectangle rcText = rcSegment;
2751 if (vsDraw.annotationVisible == ANNOTATION_BOXED) {
2752 surface->FillRectangle(rcText,
2753 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
2754 rcText.left += vsDraw.spaceWidth;
2756 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText,
2757 stAnnotation, start, lengthAnnotation);
2758 if (vsDraw.annotationVisible == ANNOTATION_BOXED) {
2759 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
2760 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
2761 surface->LineTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom));
2762 surface->MoveTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
2763 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom));
2764 if (subLine == ll->lines) {
2765 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
2766 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
2768 if (subLine == ll->lines+annotationLines-1) {
2769 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom - 1));
2770 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom - 1));
2776 void Editor::DrawLine(Surface *surface, const ViewStyle &vsDraw, int line, int lineVisible, int xStart,
2777 PRectangle rcLine, LineLayout *ll, int subLine) {
2779 if (subLine >= ll->lines) {
2780 DrawAnnotation(surface, vsDraw, line, xStart, rcLine, ll, subLine);
2781 return; // No further drawing
2784 PRectangle rcSegment = rcLine;
2786 // Using one font for all control characters so it can be controlled independently to ensure
2787 // the box goes around the characters tightly. Seems to be no way to work out what height
2788 // is taken by an individual character - internal leading gives varying results.
2789 FontAlias ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2791 // See if something overrides the line background color.
2792 const ColourOptional background = vsDraw.Background(pdoc->GetMark(line), caret.active, ll->containsCaret);
2794 const bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
2795 (!background.isSet) && (vsDraw.whitespaceColours.back.isSet);
2797 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2798 const XYPOSITION indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
2799 const XYPOSITION epsilon = 0.0001f; // A small nudge to avoid floating point precision issues
2801 const int posLineStart = pdoc->LineStart(line);
2803 const int startseg = ll->LineStart(subLine);
2804 const XYACCUMULATOR subLineStart = ll->positions[startseg];
2805 int lineStart = 0;
2806 int lineEnd = 0;
2807 if (subLine < ll->lines) {
2808 lineStart = ll->LineStart(subLine);
2809 lineEnd = ll->LineStart(subLine + 1);
2810 if (subLine == ll->lines - 1) {
2811 lineEnd = ll->numCharsBeforeEOL;
2815 if (ll->wrapIndent != 0) {
2817 bool continuedWrapLine = false;
2818 if (subLine < ll->lines) {
2819 continuedWrapLine = ll->LineStart(subLine) != 0;
2822 if (continuedWrapLine) {
2823 // draw continuation rect
2824 PRectangle rcPlace = rcSegment;
2826 rcPlace.left = ll->positions[startseg] + xStart - static_cast<XYPOSITION>(subLineStart);
2827 rcPlace.right = rcPlace.left + ll->wrapIndent;
2829 // default bgnd here..
2830 surface->FillRectangle(rcSegment, background.isSet ? background :
2831 vsDraw.styles[STYLE_DEFAULT].back);
2833 // main line style would be below but this would be inconsistent with end markers
2834 // also would possibly not be the style at wrap point
2835 //int styleMain = ll->styles[lineStart];
2836 //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back);
2838 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
2840 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
2841 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2842 else
2843 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2845 DrawWrapMarker(surface, rcPlace, false, vsDraw.WrapColour());
2848 xStart += static_cast<int>(ll->wrapIndent);
2852 const bool selBackDrawn = vsDraw.selColours.back.isSet &&
2853 ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA));
2855 // Does not take margin into account but not significant
2856 const int xStartVisible = static_cast<int>(subLineStart) - xStart;
2858 if (twoPhaseDraw) {
2859 BreakFinder bfBack(ll, &sel, lineStart, lineEnd, posLineStart, xStartVisible, selBackDrawn, pdoc, &reprs);
2861 // Background drawing loop
2862 while (bfBack.More()) {
2864 const TextSegment ts = bfBack.Next();
2865 const int i = ts.end() - 1;
2866 const int iDoc = i + posLineStart;
2868 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
2869 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
2870 // Only try to draw if really visible - enhances performance by not calling environment to
2871 // draw strings that are completely past the right side of the window.
2872 if (rcSegment.Intersects(rcLine)) {
2873 // Clip to line rectangle, since may have a huge position which will not work with some platforms
2874 if (rcSegment.left < rcLine.left)
2875 rcSegment.left = rcLine.left;
2876 if (rcSegment.right > rcLine.right)
2877 rcSegment.right = rcLine.right;
2879 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2880 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
2881 ColourDesired textBack = TextBackground(vsDraw, background, inSelection,
2882 inHotspot, ll->styles[i], i, ll);
2883 if (ts.representation) {
2884 if (ll->chars[i] == '\t') {
2885 // Tab display
2886 if (drawWhitespaceBackground &&
2887 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2888 textBack = vsDraw.whitespaceColours.back;
2889 } else {
2890 // Blob display
2891 inIndentation = false;
2893 surface->FillRectangle(rcSegment, textBack);
2894 } else {
2895 // Normal text display
2896 surface->FillRectangle(rcSegment, textBack);
2897 if (vsDraw.viewWhitespace != wsInvisible ||
2898 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
2899 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
2900 if (ll->chars[cpos + ts.start] == ' ') {
2901 if (drawWhitespaceBackground &&
2902 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
2903 PRectangle rcSpace(
2904 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
2905 rcSegment.top,
2906 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
2907 rcSegment.bottom);
2908 surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back);
2910 } else {
2911 inIndentation = false;
2916 } else if (rcSegment.left > rcLine.right) {
2917 break;
2921 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
2922 xStart, subLine, subLineStart, background);
2925 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, true);
2927 if (vsDraw.edgeState == EDGE_LINE) {
2928 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
2929 rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
2930 if ((ll->wrapIndent != 0) && (lineStart != 0))
2931 rcSegment.left -= ll->wrapIndent;
2932 rcSegment.right = rcSegment.left + 1;
2933 surface->FillRectangle(rcSegment, vsDraw.edgecolour);
2936 // Draw underline mark as part of background if not transparent
2937 int marks = pdoc->GetMark(line);
2938 int markBit;
2939 for (markBit = 0; (markBit < 32) && marks; markBit++) {
2940 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
2941 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2942 PRectangle rcUnderline = rcLine;
2943 rcUnderline.top = rcUnderline.bottom - 2;
2944 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
2946 marks >>= 1;
2949 inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2950 // Foreground drawing loop
2951 BreakFinder bfFore(ll, &sel, lineStart, lineEnd, posLineStart, xStartVisible,
2952 ((!twoPhaseDraw && selBackDrawn) || vsDraw.selColours.fore.isSet), pdoc, &reprs);
2954 while (bfFore.More()) {
2956 const TextSegment ts = bfFore.Next();
2957 const int i = ts.end() - 1;
2958 const int iDoc = i + posLineStart;
2960 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
2961 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
2962 // Only try to draw if really visible - enhances performance by not calling environment to
2963 // draw strings that are completely past the right side of the window.
2964 if (rcSegment.Intersects(rcLine)) {
2965 int styleMain = ll->styles[i];
2966 ColourDesired textFore = vsDraw.styles[styleMain].fore;
2967 FontAlias textFont = vsDraw.styles[styleMain].font;
2968 //hotspot foreground
2969 const bool inHotspot = (ll->hotspot.Valid()) && ll->hotspot.ContainsCharacter(iDoc);
2970 if (inHotspot) {
2971 if (vsDraw.hotspotColours.fore.isSet)
2972 textFore = vsDraw.hotspotColours.fore;
2974 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2975 if (inSelection && (vsDraw.selColours.fore.isSet)) {
2976 textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
2978 ColourDesired textBack = TextBackground(vsDraw, background, inSelection, inHotspot, styleMain, i, ll);
2979 if (ts.representation) {
2980 if (ll->chars[i] == '\t') {
2981 // Tab display
2982 if (!twoPhaseDraw) {
2983 if (drawWhitespaceBackground &&
2984 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2985 textBack = vsDraw.whitespaceColours.back;
2986 surface->FillRectangle(rcSegment, textBack);
2988 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
2989 for (int indentCount = static_cast<int>((ll->positions[i] + epsilon) / indentWidth);
2990 indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
2991 indentCount++) {
2992 if (indentCount > 0) {
2993 int xIndent = static_cast<int>(indentCount * indentWidth);
2994 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
2995 (ll->xHighlightGuide == xIndent));
2999 if (vsDraw.viewWhitespace != wsInvisible) {
3000 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3001 if (vsDraw.whitespaceColours.fore.isSet)
3002 textFore = vsDraw.whitespaceColours.fore;
3003 surface->PenColour(textFore);
3004 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
3005 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
3006 DrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
3009 } else {
3010 inIndentation = false;
3011 if (vsDraw.controlCharSymbol >= 32) {
3012 char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\0' };
3013 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
3014 rcSegment.top + vsDraw.maxAscent,
3015 cc, 1, textBack, textFore);
3016 } else {
3017 DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(), textBack, textFore, twoPhaseDraw);
3020 } else {
3021 // Normal text display
3022 if (vsDraw.styles[styleMain].visible) {
3023 if (twoPhaseDraw) {
3024 surface->DrawTextTransparent(rcSegment, textFont,
3025 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
3026 i - ts.start + 1, textFore);
3027 } else {
3028 surface->DrawTextNoClip(rcSegment, textFont,
3029 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
3030 i - ts.start + 1, textFore, textBack);
3033 if (vsDraw.viewWhitespace != wsInvisible ||
3034 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
3035 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
3036 if (ll->chars[cpos + ts.start] == ' ') {
3037 if (vsDraw.viewWhitespace != wsInvisible) {
3038 if (vsDraw.whitespaceColours.fore.isSet)
3039 textFore = vsDraw.whitespaceColours.fore;
3040 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3041 XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2;
3042 if (!twoPhaseDraw && drawWhitespaceBackground &&
3043 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
3044 textBack = vsDraw.whitespaceColours.back;
3045 PRectangle rcSpace(
3046 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
3047 rcSegment.top,
3048 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
3049 rcSegment.bottom);
3050 surface->FillRectangle(rcSpace, textBack);
3052 PRectangle rcDot(xmid + xStart - static_cast<XYPOSITION>(subLineStart),
3053 rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f);
3054 rcDot.right = rcDot.left + vsDraw.whitespaceSize;
3055 rcDot.bottom = rcDot.top + vsDraw.whitespaceSize;
3056 surface->FillRectangle(rcDot, textFore);
3059 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
3060 for (int indentCount = static_cast<int>((ll->positions[cpos + ts.start] + epsilon) / indentWidth);
3061 indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth;
3062 indentCount++) {
3063 if (indentCount > 0) {
3064 int xIndent = static_cast<int>(indentCount * indentWidth);
3065 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3066 (ll->xHighlightGuide == xIndent));
3070 } else {
3071 inIndentation = false;
3076 if (ll->hotspot.Valid() && vsDraw.hotspotUnderline && ll->hotspot.ContainsCharacter(iDoc)) {
3077 PRectangle rcUL = rcSegment;
3078 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3079 rcUL.bottom = rcUL.top + 1;
3080 if (vsDraw.hotspotColours.fore.isSet)
3081 surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore);
3082 else
3083 surface->FillRectangle(rcUL, textFore);
3084 } else if (vsDraw.styles[styleMain].underline) {
3085 PRectangle rcUL = rcSegment;
3086 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3087 rcUL.bottom = rcUL.top + 1;
3088 surface->FillRectangle(rcUL, textFore);
3090 } else if (rcSegment.left > rcLine.right) {
3091 break;
3094 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
3095 && (subLine == 0)) {
3096 int indentSpace = pdoc->GetLineIndentation(line);
3097 int xStartText = static_cast<int>(ll->positions[pdoc->GetLineIndentPosition(line) - posLineStart]);
3099 // Find the most recent line with some text
3101 int lineLastWithText = line;
3102 while (lineLastWithText > Platform::Maximum(line-20, 0) && pdoc->IsWhiteLine(lineLastWithText)) {
3103 lineLastWithText--;
3105 if (lineLastWithText < line) {
3106 xStartText = 100000; // Don't limit to visible indentation on empty line
3107 // This line is empty, so use indentation of last line with text
3108 int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText);
3109 int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
3110 if (isFoldHeader) {
3111 // Level is one more level than parent
3112 indentLastWithText += pdoc->IndentSize();
3114 if (vsDraw.viewIndentationGuides == ivLookForward) {
3115 // In viLookForward mode, previous line only used if it is a fold header
3116 if (isFoldHeader) {
3117 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3119 } else { // viLookBoth
3120 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3124 int lineNextWithText = line;
3125 while (lineNextWithText < Platform::Minimum(line+20, pdoc->LinesTotal()) && pdoc->IsWhiteLine(lineNextWithText)) {
3126 lineNextWithText++;
3128 if (lineNextWithText > line) {
3129 xStartText = 100000; // Don't limit to visible indentation on empty line
3130 // This line is empty, so use indentation of first next line with text
3131 indentSpace = Platform::Maximum(indentSpace,
3132 pdoc->GetLineIndentation(lineNextWithText));
3135 for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) {
3136 int xIndent = static_cast<int>(indentPos * vsDraw.spaceWidth);
3137 if (xIndent < xStartText) {
3138 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3139 (ll->xHighlightGuide == xIndent));
3144 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, false);
3146 // End of the drawing of the current line
3147 if (!twoPhaseDraw) {
3148 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
3149 xStart, subLine, subLineStart, background);
3151 if (!hideSelection && ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA))) {
3152 // For each selection draw
3153 int virtualSpaces = 0;
3154 if (subLine == (ll->lines - 1)) {
3155 virtualSpaces = sel.VirtualSpaceFor(pdoc->LineEnd(line));
3157 SelectionPosition posStart(posLineStart + lineStart);
3158 SelectionPosition posEnd(posLineStart + lineEnd, virtualSpaces);
3159 SelectionSegment virtualSpaceRange(posStart, posEnd);
3160 for (size_t r=0; r<sel.Count(); r++) {
3161 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
3162 if (alpha != SC_ALPHA_NOALPHA) {
3163 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
3164 if (!portion.Empty()) {
3165 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
3166 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
3167 static_cast<XYPOSITION>(subLineStart) + portion.start.VirtualSpace() * spaceWidth;
3168 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
3169 static_cast<XYPOSITION>(subLineStart) + portion.end.VirtualSpace() * spaceWidth;
3170 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3171 if ((portion.start.Position() - posLineStart) == lineStart && sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
3172 rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
3174 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
3175 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
3176 if (rcSegment.right > rcLine.left)
3177 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == sel.Main()), alpha);
3183 // Draw any translucent whole line states
3184 rcSegment = rcLine;
3185 if ((caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) {
3186 SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
3188 marks = pdoc->GetMark(line);
3189 for (markBit = 0; (markBit < 32) && marks; markBit++) {
3190 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
3191 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3192 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
3193 PRectangle rcUnderline = rcSegment;
3194 rcUnderline.top = rcUnderline.bottom - 2;
3195 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3197 marks >>= 1;
3199 if (vsDraw.maskInLine) {
3200 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
3201 if (marksMasked) {
3202 for (markBit = 0; (markBit < 32) && marksMasked; markBit++) {
3203 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
3204 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3206 marksMasked >>= 1;
3212 void Editor::DrawBlockCaret(Surface *surface, const ViewStyle &vsDraw, LineLayout *ll, int subLine,
3213 int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) const {
3215 int lineStart = ll->LineStart(subLine);
3216 int posBefore = posCaret;
3217 int posAfter = MovePositionOutsideChar(posCaret + 1, 1);
3218 int numCharsToDraw = posAfter - posCaret;
3220 // Work out where the starting and ending offsets are. We need to
3221 // see if the previous character shares horizontal space, such as a
3222 // glyph / combining character. If so we'll need to draw that too.
3223 int offsetFirstChar = offset;
3224 int offsetLastChar = offset + (posAfter - posCaret);
3225 while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
3226 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
3227 // The char does not share horizontal space
3228 break;
3230 // Char shares horizontal space, update the numChars to draw
3231 // Update posBefore to point to the prev char
3232 posBefore = MovePositionOutsideChar(posBefore - 1, -1);
3233 numCharsToDraw = posAfter - posBefore;
3234 offsetFirstChar = offset - (posCaret - posBefore);
3237 // See if the next character shares horizontal space, if so we'll
3238 // need to draw that too.
3239 if (offsetFirstChar < 0)
3240 offsetFirstChar = 0;
3241 numCharsToDraw = offsetLastChar - offsetFirstChar;
3242 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
3243 // Update posAfter to point to the 2nd next char, this is where
3244 // the next character ends, and 2nd next begins. We'll need
3245 // to compare these two
3246 posBefore = posAfter;
3247 posAfter = MovePositionOutsideChar(posAfter + 1, 1);
3248 offsetLastChar = offset + (posAfter - posCaret);
3249 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
3250 // The char does not share horizontal space
3251 break;
3253 // Char shares horizontal space, update the numChars to draw
3254 numCharsToDraw = offsetLastChar - offsetFirstChar;
3257 // We now know what to draw, update the caret drawing rectangle
3258 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
3259 rcCaret.right = ll->positions[offsetFirstChar+numCharsToDraw] - ll->positions[lineStart] + xStart;
3261 // Adjust caret position to take into account any word wrapping symbols.
3262 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3263 XYPOSITION wordWrapCharWidth = ll->wrapIndent;
3264 rcCaret.left += wordWrapCharWidth;
3265 rcCaret.right += wordWrapCharWidth;
3268 // This character is where the caret block is, we override the colours
3269 // (inversed) for drawing the caret here.
3270 int styleMain = ll->styles[offsetFirstChar];
3271 FontAlias fontText = vsDraw.styles[styleMain].font;
3272 surface->DrawTextClipped(rcCaret, fontText,
3273 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
3274 numCharsToDraw, vsDraw.styles[styleMain].back,
3275 caretColour);
3278 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
3279 if (!pixmapSelPattern->Initialised()) {
3280 const int patternSize = 8;
3281 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3282 pixmapSelPatternOffset1->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3283 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
3284 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
3285 // way between the chrome colour and the chrome highlight colour making a nice transition
3286 // between the window chrome and the content area. And it works in low colour depths.
3287 PRectangle rcPattern = PRectangle::FromInts(0, 0, patternSize, patternSize);
3289 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
3290 ColourDesired colourFMFill = vs.selbar;
3291 ColourDesired colourFMStripes = vs.selbarlight;
3293 if (!(vs.selbarlight == ColourDesired(0xff, 0xff, 0xff))) {
3294 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
3295 // (Typically, the highlight colour is white.)
3296 colourFMFill = vs.selbarlight;
3299 if (vs.foldmarginColour.isSet) {
3300 // override default fold margin colour
3301 colourFMFill = vs.foldmarginColour;
3303 if (vs.foldmarginHighlightColour.isSet) {
3304 // override default fold margin highlight colour
3305 colourFMStripes = vs.foldmarginHighlightColour;
3308 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
3309 pixmapSelPatternOffset1->FillRectangle(rcPattern, colourFMStripes);
3310 for (int y = 0; y < patternSize; y++) {
3311 for (int x = y % 2; x < patternSize; x+=2) {
3312 PRectangle rcPixel = PRectangle::FromInts(x, y, x + 1, y + 1);
3313 pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);
3314 pixmapSelPatternOffset1->FillRectangle(rcPixel, colourFMFill);
3319 if (!pixmapIndentGuide->Initialised()) {
3320 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
3321 pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3322 pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3323 PRectangle rcIG = PRectangle::FromInts(0, 0, 1, vs.lineHeight);
3324 pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back);
3325 pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore);
3326 pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back);
3327 pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore);
3328 for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
3329 PRectangle rcPixel = PRectangle::FromInts(0, stripe, 1, stripe + 1);
3330 pixmapIndentGuide->FillRectangle(rcPixel, vs.styles[STYLE_INDENTGUIDE].fore);
3331 pixmapIndentGuideHighlight->FillRectangle(rcPixel, vs.styles[STYLE_BRACELIGHT].fore);
3335 if (bufferedDraw) {
3336 if (!pixmapLine->Initialised()) {
3337 PRectangle rcClient = GetClientRectangle();
3338 pixmapLine->InitPixMap(static_cast<int>(rcClient.Width()), vs.lineHeight,
3339 surfaceWindow, wMain.GetID());
3340 pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
3341 static_cast<int>(rcClient.Height()), surfaceWindow, wMain.GetID());
3346 void Editor::DrawCarets(Surface *surface, const ViewStyle &vsDraw, int lineDoc, int xStart,
3347 PRectangle rcLine, LineLayout *ll, int subLine) {
3348 // When drag is active it is the only caret drawn
3349 bool drawDrag = posDrag.IsValid();
3350 if (hideSelection && !drawDrag)
3351 return;
3352 const int posLineStart = pdoc->LineStart(lineDoc);
3353 // For each selection draw
3354 for (size_t r=0; (r<sel.Count()) || drawDrag; r++) {
3355 const bool mainCaret = r == sel.Main();
3356 const SelectionPosition posCaret = (drawDrag ? posDrag : sel.Range(r).caret);
3357 const int offset = posCaret.Position() - posLineStart;
3358 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
3359 const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
3360 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
3361 XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
3362 if (ll->wrapIndent != 0) {
3363 int lineStart = ll->LineStart(subLine);
3364 if (lineStart != 0) // Wrapped
3365 xposCaret += ll->wrapIndent;
3367 bool caretBlinkState = (caret.active && caret.on) || (!additionalCaretsBlink && !mainCaret);
3368 bool caretVisibleState = additionalCaretsVisible || mainCaret;
3369 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
3370 ((posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
3371 bool caretAtEOF = false;
3372 bool caretAtEOL = false;
3373 bool drawBlockCaret = false;
3374 XYPOSITION widthOverstrikeCaret;
3375 XYPOSITION caretWidthOffset = 0;
3376 PRectangle rcCaret = rcLine;
3378 if (posCaret.Position() == pdoc->Length()) { // At end of document
3379 caretAtEOF = true;
3380 widthOverstrikeCaret = vsDraw.aveCharWidth;
3381 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
3382 caretAtEOL = true;
3383 widthOverstrikeCaret = vsDraw.aveCharWidth;
3384 } else {
3385 widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
3387 if (widthOverstrikeCaret < 3) // Make sure its visible
3388 widthOverstrikeCaret = 3;
3390 if (xposCaret > 0)
3391 caretWidthOffset = 0.51f; // Move back so overlaps both character cells.
3392 xposCaret += xStart;
3393 if (posDrag.IsValid()) {
3394 /* Dragging text, use a line caret */
3395 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
3396 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3397 } else if (inOverstrike && drawOverstrikeCaret) {
3398 /* Overstrike (insert mode), use a modified bar caret */
3399 rcCaret.top = rcCaret.bottom - 2;
3400 rcCaret.left = xposCaret + 1;
3401 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
3402 } else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) {
3403 /* Block caret */
3404 rcCaret.left = xposCaret;
3405 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
3406 drawBlockCaret = true;
3407 rcCaret.right = xposCaret + widthOverstrikeCaret;
3408 } else {
3409 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
3411 } else {
3412 /* Line caret */
3413 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
3414 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3416 ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
3417 if (drawBlockCaret) {
3418 DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
3419 } else {
3420 surface->FillRectangle(rcCaret, caretColour);
3424 if (drawDrag)
3425 break;
3429 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
3430 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
3431 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
3432 AllocateGraphics();
3434 RefreshStyleData();
3435 if (paintState == paintAbandoned)
3436 return; // Scroll bars may have changed so need redraw
3437 RefreshPixMaps(surfaceWindow);
3439 paintAbandonedByStyling = false;
3441 StyleToPositionInView(PositionAfterArea(rcArea));
3443 PRectangle rcClient = GetClientRectangle();
3444 Point ptOrigin = GetVisibleOriginInMain();
3445 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
3446 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3448 int screenLinePaintFirst = static_cast<int>(rcArea.top) / vs.lineHeight;
3450 int xStart = vs.textStart - xOffset + static_cast<int>(ptOrigin.x);
3451 int ypos = 0;
3452 if (!bufferedDraw)
3453 ypos += screenLinePaintFirst * vs.lineHeight;
3454 int yposScreen = screenLinePaintFirst * vs.lineHeight;
3456 if (NotifyUpdateUI()) {
3457 RefreshStyleData();
3458 RefreshPixMaps(surfaceWindow);
3461 // Wrap the visible lines if needed.
3462 if (WrapLines(wsVisible)) {
3463 // The wrapping process has changed the height of some lines so
3464 // abandon this paint for a complete repaint.
3465 if (AbandonPaint()) {
3466 return;
3468 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
3470 PLATFORM_ASSERT(pixmapSelPattern->Initialised());
3472 if (!bufferedDraw)
3473 surfaceWindow->SetClip(rcArea);
3475 if (paintState != paintAbandoned) {
3476 if (vs.marginInside) {
3477 PaintSelMargin(surfaceWindow, rcArea);
3478 PRectangle rcRightMargin = rcClient;
3479 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
3480 if (rcArea.Intersects(rcRightMargin)) {
3481 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
3483 } else { // Else separate view so separate paint event but leftMargin included to allow overlap
3484 PRectangle rcLeftMargin = rcArea;
3485 rcLeftMargin.left = 0;
3486 rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
3487 if (rcArea.Intersects(rcLeftMargin)) {
3488 surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[STYLE_DEFAULT].back);
3493 if (paintState == paintAbandoned) {
3494 // Either styling or NotifyUpdateUI noticed that painting is needed
3495 // outside the current painting rectangle
3496 //Platform::DebugPrintf("Abandoning paint\n");
3497 if (Wrapping()) {
3498 if (paintAbandonedByStyling) {
3499 // Styling has spilled over a line end, such as occurs by starting a multiline
3500 // comment. The width of subsequent text may have changed, so rewrap.
3501 NeedWrapping(cs.DocFromDisplay(topLine));
3504 return;
3506 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
3508 // Allow text at start of line to overlap 1 pixel into the margin as this displays
3509 // serifs and italic stems for aliased text.
3510 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
3512 // Do the painting
3513 if (rcArea.right > vs.textStart - leftTextOverlap) {
3515 Surface *surface = surfaceWindow;
3516 if (bufferedDraw) {
3517 surface = pixmapLine;
3518 PLATFORM_ASSERT(pixmapLine->Initialised());
3520 surface->SetUnicodeMode(IsUnicodeMode());
3521 surface->SetDBCSMode(CodePage());
3523 int visibleLine = TopLineOfMain() + screenLinePaintFirst;
3525 SelectionPosition posCaret = sel.RangeMain().caret;
3526 if (posDrag.IsValid())
3527 posCaret = posDrag;
3528 int lineCaret = pdoc->LineFromPosition(posCaret.Position());
3530 PRectangle rcTextArea = rcClient;
3531 if (vs.marginInside) {
3532 rcTextArea.left += vs.textStart;
3533 rcTextArea.right -= vs.rightMarginWidth;
3534 } else {
3535 rcTextArea = rcArea;
3538 // Remove selection margin from drawing area so text will not be drawn
3539 // on it in unbuffered mode.
3540 if (!bufferedDraw && vs.marginInside) {
3541 PRectangle rcClipText = rcTextArea;
3542 rcClipText.left -= leftTextOverlap;
3543 surfaceWindow->SetClip(rcClipText);
3546 // Loop on visible lines
3547 //double durLayout = 0.0;
3548 //double durPaint = 0.0;
3549 //double durCopy = 0.0;
3550 //ElapsedTime etWhole;
3551 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
3552 AutoLineLayout ll(llc, 0);
3553 while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
3555 int lineDoc = cs.DocFromDisplay(visibleLine);
3556 // Only visible lines should be handled by the code within the loop
3557 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
3558 int lineStartSet = cs.DisplayFromDoc(lineDoc);
3559 int subLine = visibleLine - lineStartSet;
3561 // Copy this line and its styles from the document into local arrays
3562 // and determine the x position at which each character starts.
3563 //ElapsedTime et;
3564 if (lineDoc != lineDocPrevious) {
3565 ll.Set(0);
3566 ll.Set(RetrieveLineLayout(lineDoc));
3567 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
3568 lineDocPrevious = lineDoc;
3570 //durLayout += et.Duration(true);
3572 if (ll) {
3573 ll->containsCaret = lineDoc == lineCaret;
3574 if (hideSelection) {
3575 ll->containsCaret = false;
3578 ll->hotspot = GetHotSpotRange();
3580 PRectangle rcLine = rcTextArea;
3581 rcLine.top = static_cast<XYPOSITION>(ypos);
3582 rcLine.bottom = static_cast<XYPOSITION>(ypos + vs.lineHeight);
3584 bool bracesIgnoreStyle = false;
3585 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
3586 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
3587 bracesIgnoreStyle = true;
3589 Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
3590 // Highlight the current braces if any
3591 ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
3592 static_cast<int>(highlightGuideColumn * vs.spaceWidth), bracesIgnoreStyle);
3594 if (leftTextOverlap && bufferedDraw) {
3595 PRectangle rcSpacer = rcLine;
3596 rcSpacer.right = rcSpacer.left;
3597 rcSpacer.left -= 1;
3598 surface->FillRectangle(rcSpacer, vs.styles[STYLE_DEFAULT].back);
3601 // Draw the line
3602 DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
3603 //durPaint += et.Duration(true);
3605 // Restore the previous styles for the brace highlights in case layout is in cache.
3606 ll->RestoreBracesHighlight(rangeLine, braces, bracesIgnoreStyle);
3608 bool expanded = cs.GetExpanded(lineDoc);
3609 const int level = pdoc->GetLevel(lineDoc);
3610 const int levelNext = pdoc->GetLevel(lineDoc + 1);
3611 if ((level & SC_FOLDLEVELHEADERFLAG) &&
3612 ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
3613 // Paint the line above the fold
3614 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
3616 (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
3617 PRectangle rcFoldLine = rcLine;
3618 rcFoldLine.bottom = rcFoldLine.top + 1;
3619 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore);
3621 // Paint the line below the fold
3622 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
3624 (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
3625 PRectangle rcFoldLine = rcLine;
3626 rcFoldLine.top = rcFoldLine.bottom - 1;
3627 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore);
3631 DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine);
3633 if (bufferedDraw) {
3634 Point from = Point::FromInts(vs.textStart-leftTextOverlap, 0);
3635 PRectangle rcCopyArea = PRectangle::FromInts(vs.textStart - leftTextOverlap, yposScreen,
3636 static_cast<int>(rcClient.right - vs.rightMarginWidth),
3637 yposScreen + vs.lineHeight);
3638 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
3641 lineWidthMaxSeen = Platform::Maximum(
3642 lineWidthMaxSeen, static_cast<int>(ll->positions[ll->numCharsInLine]));
3643 //durCopy += et.Duration(true);
3646 if (!bufferedDraw) {
3647 ypos += vs.lineHeight;
3650 yposScreen += vs.lineHeight;
3651 visibleLine++;
3653 //gdk_flush();
3655 ll.Set(0);
3656 //if (durPaint < 0.00000001)
3657 // durPaint = 0.00000001;
3659 // Right column limit indicator
3660 PRectangle rcBeyondEOF = (vs.marginInside) ? rcClient : rcArea;
3661 rcBeyondEOF.left = static_cast<XYPOSITION>(vs.textStart);
3662 rcBeyondEOF.right = rcBeyondEOF.right - ((vs.marginInside) ? vs.rightMarginWidth : 0);
3663 rcBeyondEOF.top = static_cast<XYPOSITION>((cs.LinesDisplayed() - TopLineOfMain()) * vs.lineHeight);
3664 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
3665 surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back);
3666 if (vs.edgeState == EDGE_LINE) {
3667 int edgeX = static_cast<int>(vs.theEdge * vs.spaceWidth);
3668 rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
3669 rcBeyondEOF.right = rcBeyondEOF.left + 1;
3670 surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour);
3673 //Platform::DebugPrintf(
3674 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
3675 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
3676 NotifyPainted();
3680 // Space (3 space characters) between line numbers and text when printing.
3681 #define lineNumberPrintSpace " "
3683 ColourDesired InvertedLight(ColourDesired orig) {
3684 unsigned int r = orig.GetRed();
3685 unsigned int g = orig.GetGreen();
3686 unsigned int b = orig.GetBlue();
3687 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
3688 unsigned int il = 0xff - l;
3689 if (l == 0)
3690 return ColourDesired(0xff, 0xff, 0xff);
3691 r = r * il / l;
3692 g = g * il / l;
3693 b = b * il / l;
3694 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
3697 // This is mostly copied from the Paint method but with some things omitted
3698 // such as the margin markers, line numbers, selection and caret
3699 // Should be merged back into a combined Draw method.
3700 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
3701 if (!pfr)
3702 return 0;
3704 AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
3705 if (!surface)
3706 return 0;
3707 AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
3708 if (!surfaceMeasure) {
3709 return 0;
3712 // Can't use measurements cached for screen
3713 posCache.Clear();
3715 ViewStyle vsPrint(vs);
3716 vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
3718 // Modify the view style for printing as do not normally want any of the transient features to be printed
3719 // Printing supports only the line number margin.
3720 int lineNumberIndex = -1;
3721 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
3722 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
3723 lineNumberIndex = margin;
3724 } else {
3725 vsPrint.ms[margin].width = 0;
3728 vsPrint.fixedColumnWidth = 0;
3729 vsPrint.zoomLevel = printParameters.magnification;
3730 // Don't show indentation guides
3731 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
3732 vsPrint.viewIndentationGuides = ivNone;
3733 // Don't show the selection when printing
3734 vsPrint.selColours.back.isSet = false;
3735 vsPrint.selColours.fore.isSet = false;
3736 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
3737 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
3738 vsPrint.whitespaceColours.back.isSet = false;
3739 vsPrint.whitespaceColours.fore.isSet = false;
3740 vsPrint.showCaretLineBackground = false;
3741 vsPrint.alwaysShowCaretLineBackground = false;
3742 // Don't highlight matching braces using indicators
3743 vsPrint.braceHighlightIndicatorSet = false;
3744 vsPrint.braceBadLightIndicatorSet = false;
3746 // Set colours for printing according to users settings
3747 for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) {
3748 if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) {
3749 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
3750 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
3751 } else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) {
3752 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
3753 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3754 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) {
3755 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3756 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
3757 if (sty <= STYLE_DEFAULT) {
3758 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3762 // White background for the line numbers
3763 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
3765 // Printing uses different margins, so reset screen margins
3766 vsPrint.leftMarginWidth = 0;
3767 vsPrint.rightMarginWidth = 0;
3769 vsPrint.Refresh(*surfaceMeasure, pdoc->tabInChars);
3770 // Determining width must happen after fonts have been realised in Refresh
3771 int lineNumberWidth = 0;
3772 if (lineNumberIndex >= 0) {
3773 lineNumberWidth = static_cast<int>(surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
3774 "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace)));
3775 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
3776 vsPrint.Refresh(*surfaceMeasure, pdoc->tabInChars); // Recalculate fixedColumnWidth
3779 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
3780 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
3781 if (linePrintLast < linePrintStart)
3782 linePrintLast = linePrintStart;
3783 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
3784 if (linePrintLast > linePrintMax)
3785 linePrintLast = linePrintMax;
3786 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
3787 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
3788 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
3789 int endPosPrint = pdoc->Length();
3790 if (linePrintLast < pdoc->LinesTotal())
3791 endPosPrint = pdoc->LineStart(linePrintLast + 1);
3793 // Ensure we are styled to where we are formatting.
3794 pdoc->EnsureStyledTo(endPosPrint);
3796 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
3797 int ypos = pfr->rc.top;
3799 int lineDoc = linePrintStart;
3801 int nPrintPos = pfr->chrg.cpMin;
3802 int visibleLine = 0;
3803 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
3804 if (printParameters.wrapState == eWrapNone)
3805 widthPrint = LineLayout::wrapWidthInfinite;
3807 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
3809 // When printing, the hdc and hdcTarget may be the same, so
3810 // changing the state of surfaceMeasure may change the underlying
3811 // state of surface. Therefore, any cached state is discarded before
3812 // using each surface.
3813 surfaceMeasure->FlushCachedState();
3815 // Copy this line and its styles from the document into local arrays
3816 // and determine the x position at which each character starts.
3817 LineLayout ll(pdoc->LineStart(lineDoc+1)-pdoc->LineStart(lineDoc)+1);
3818 LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
3820 ll.containsCaret = false;
3822 PRectangle rcLine = PRectangle::FromInts(
3823 pfr->rc.left,
3824 ypos,
3825 pfr->rc.right - 1,
3826 ypos + vsPrint.lineHeight);
3828 // When document line is wrapped over multiple display lines, find where
3829 // to start printing from to ensure a particular position is on the first
3830 // line of the page.
3831 if (visibleLine == 0) {
3832 int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
3833 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
3834 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
3835 visibleLine = -iwl;
3839 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
3840 visibleLine = -(ll.lines - 1);
3844 if (draw && lineNumberWidth &&
3845 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
3846 (visibleLine >= 0)) {
3847 char number[100];
3848 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
3849 PRectangle rcNumber = rcLine;
3850 rcNumber.right = rcNumber.left + lineNumberWidth;
3851 // Right justify
3852 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
3853 vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
3854 surface->FlushCachedState();
3855 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
3856 static_cast<XYPOSITION>(ypos + vsPrint.maxAscent), number, istrlen(number),
3857 vsPrint.styles[STYLE_LINENUMBER].fore,
3858 vsPrint.styles[STYLE_LINENUMBER].back);
3861 // Draw the line
3862 surface->FlushCachedState();
3864 for (int iwl = 0; iwl < ll.lines; iwl++) {
3865 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
3866 if (visibleLine >= 0) {
3867 if (draw) {
3868 rcLine.top = static_cast<XYPOSITION>(ypos);
3869 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight);
3870 DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
3872 ypos += vsPrint.lineHeight;
3874 visibleLine++;
3875 if (iwl == ll.lines - 1)
3876 nPrintPos = pdoc->LineStart(lineDoc + 1);
3877 else
3878 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
3882 ++lineDoc;
3885 // Clear cache so measurements are not used for screen
3886 posCache.Clear();
3888 return nPrintPos;
3891 int Editor::TextWidth(int style, const char *text) {
3892 RefreshStyleData();
3893 AutoSurface surface(this);
3894 if (surface) {
3895 return static_cast<int>(surface->WidthText(vs.styles[style].font, text, istrlen(text)));
3896 } else {
3897 return 1;
3901 // Empty method is overridden on GTK+ to show / hide scrollbars
3902 void Editor::ReconfigureScrollBars() {}
3904 void Editor::SetScrollBars() {
3905 RefreshStyleData();
3907 int nMax = MaxScrollPos();
3908 int nPage = LinesOnScreen();
3909 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
3910 if (modified) {
3911 DwellEnd(true);
3914 // TODO: ensure always showing as many lines as possible
3915 // May not be, if, for example, window made larger
3916 if (topLine > MaxScrollPos()) {
3917 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
3918 SetVerticalScrollPos();
3919 Redraw();
3921 if (modified) {
3922 if (!AbandonPaint())
3923 Redraw();
3925 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
3928 void Editor::ChangeSize() {
3929 DropGraphics(false);
3930 SetScrollBars();
3931 if (Wrapping()) {
3932 PRectangle rcTextArea = GetClientRectangle();
3933 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
3934 rcTextArea.right -= vs.rightMarginWidth;
3935 if (wrapWidth != rcTextArea.Width()) {
3936 NeedWrapping();
3937 Redraw();
3942 int Editor::InsertSpace(int position, unsigned int spaces) {
3943 if (spaces > 0) {
3944 std::string spaceText(spaces, ' ');
3945 const int lengthInserted = pdoc->InsertString(position, spaceText.c_str(), spaces);
3946 position += lengthInserted;
3948 return position;
3951 void Editor::AddChar(char ch) {
3952 char s[2];
3953 s[0] = ch;
3954 s[1] = '\0';
3955 AddCharUTF(s, 1);
3958 void Editor::FilterSelections() {
3959 if (!additionalSelectionTyping && (sel.Count() > 1)) {
3960 SelectionRange rangeOnly = sel.RangeMain();
3961 InvalidateSelection(rangeOnly, true);
3962 sel.SetSelection(rangeOnly);
3966 static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
3967 return *a < *b;
3970 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
3971 void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
3972 FilterSelections();
3974 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
3976 std::vector<SelectionRange *> selPtrs;
3977 for (size_t r = 0; r < sel.Count(); r++) {
3978 selPtrs.push_back(&sel.Range(r));
3980 std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
3982 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
3983 rit != selPtrs.rend(); ++rit) {
3984 SelectionRange *currentSel = *rit;
3985 if (!RangeContainsProtected(currentSel->Start().Position(),
3986 currentSel->End().Position())) {
3987 int positionInsert = currentSel->Start().Position();
3988 if (!currentSel->Empty()) {
3989 if (currentSel->Length()) {
3990 pdoc->DeleteChars(positionInsert, currentSel->Length());
3991 currentSel->ClearVirtualSpace();
3992 } else {
3993 // Range is all virtual so collapse to start of virtual space
3994 currentSel->MinimizeVirtualSpace();
3996 } else if (inOverstrike) {
3997 if (positionInsert < pdoc->Length()) {
3998 if (!pdoc->IsPositionInLineEnd(positionInsert)) {
3999 pdoc->DelChar(positionInsert);
4000 currentSel->ClearVirtualSpace();
4004 positionInsert = InsertSpace(positionInsert, currentSel->caret.VirtualSpace());
4005 const int lengthInserted = pdoc->InsertString(positionInsert, s, len);
4006 if (lengthInserted > 0) {
4007 currentSel->caret.SetPosition(positionInsert + lengthInserted);
4008 currentSel->anchor.SetPosition(positionInsert + lengthInserted);
4010 currentSel->ClearVirtualSpace();
4011 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4012 if (Wrapping()) {
4013 AutoSurface surface(this);
4014 if (surface) {
4015 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
4016 SetScrollBars();
4017 SetVerticalScrollPos();
4018 Redraw();
4025 if (Wrapping()) {
4026 SetScrollBars();
4028 ThinRectangularRange();
4029 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4030 EnsureCaretVisible();
4031 // Avoid blinking during rapid typing:
4032 ShowCaretAtCurrentPosition();
4033 if ((caretSticky == SC_CARETSTICKY_OFF) ||
4034 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
4035 SetLastXChosen();
4038 if (treatAsDBCS) {
4039 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
4040 static_cast<unsigned char>(s[1]));
4041 } else if (len > 0) {
4042 int byte = static_cast<unsigned char>(s[0]);
4043 if ((byte < 0xC0) || (1 == len)) {
4044 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
4045 // characters when not in UTF-8 mode.
4046 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
4047 // characters representing themselves.
4048 } else {
4049 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
4050 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
4051 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
4052 if (byte < 0xE0) {
4053 int byte2 = static_cast<unsigned char>(s[1]);
4054 if ((byte2 & 0xC0) == 0x80) {
4055 // Two-byte-character lead-byte followed by a trail-byte.
4056 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
4058 // A two-byte-character lead-byte not followed by trail-byte
4059 // represents itself.
4060 } else if (byte < 0xF0) {
4061 int byte2 = static_cast<unsigned char>(s[1]);
4062 int byte3 = static_cast<unsigned char>(s[2]);
4063 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
4064 // Three-byte-character lead byte followed by two trail bytes.
4065 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
4066 (byte3 & 0x3F));
4068 // A three-byte-character lead-byte not followed by two trail-bytes
4069 // represents itself.
4072 NotifyChar(byte);
4075 if (recordingMacro) {
4076 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
4080 void Editor::InsertPaste(const char *text, int len) {
4081 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
4082 SelectionPosition selStart = sel.Start();
4083 selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
4084 const int lengthInserted = pdoc->InsertString(selStart.Position(), text, len);
4085 if (lengthInserted > 0) {
4086 SetEmptySelection(selStart.Position() + lengthInserted);
4088 } else {
4089 // SC_MULTIPASTE_EACH
4090 for (size_t r=0; r<sel.Count(); r++) {
4091 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4092 sel.Range(r).End().Position())) {
4093 int positionInsert = sel.Range(r).Start().Position();
4094 if (!sel.Range(r).Empty()) {
4095 if (sel.Range(r).Length()) {
4096 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
4097 sel.Range(r).ClearVirtualSpace();
4098 } else {
4099 // Range is all virtual so collapse to start of virtual space
4100 sel.Range(r).MinimizeVirtualSpace();
4103 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
4104 const int lengthInserted = pdoc->InsertString(positionInsert, text, len);
4105 if (lengthInserted > 0) {
4106 sel.Range(r).caret.SetPosition(positionInsert + lengthInserted);
4107 sel.Range(r).anchor.SetPosition(positionInsert + lengthInserted);
4109 sel.Range(r).ClearVirtualSpace();
4115 void Editor::InsertPasteShape(const char *text, int len, PasteShape shape) {
4116 std::string convertedText;
4117 if (convertPastes) {
4118 // Convert line endings of the paste into our local line-endings mode
4119 convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
4120 len = static_cast<int>(convertedText.length());
4121 text = convertedText.c_str();
4123 if (shape == pasteRectangular) {
4124 PasteRectangular(sel.Start(), text, len);
4125 } else {
4126 if (shape == pasteLine) {
4127 int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
4128 int lengthInserted = pdoc->InsertString(insertPos, text, len);
4129 // add the newline if necessary
4130 if ((len > 0) && (text[len - 1] != '\n' && text[len - 1] != '\r')) {
4131 const char *endline = StringFromEOLMode(pdoc->eolMode);
4132 int length = static_cast<int>(strlen(endline));
4133 lengthInserted += pdoc->InsertString(insertPos + lengthInserted, endline, length);
4135 if (sel.MainCaret() == insertPos) {
4136 SetEmptySelection(sel.MainCaret() + lengthInserted);
4138 } else {
4139 InsertPaste(text, len);
4144 void Editor::ClearSelection(bool retainMultipleSelections) {
4145 if (!sel.IsRectangular() && !retainMultipleSelections)
4146 FilterSelections();
4147 UndoGroup ug(pdoc);
4148 for (size_t r=0; r<sel.Count(); r++) {
4149 if (!sel.Range(r).Empty()) {
4150 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4151 sel.Range(r).End().Position())) {
4152 pdoc->DeleteChars(sel.Range(r).Start().Position(),
4153 sel.Range(r).Length());
4154 sel.Range(r) = SelectionRange(sel.Range(r).Start());
4158 ThinRectangularRange();
4159 sel.RemoveDuplicates();
4160 ClaimSelection();
4163 void Editor::ClearAll() {
4165 UndoGroup ug(pdoc);
4166 if (0 != pdoc->Length()) {
4167 pdoc->DeleteChars(0, pdoc->Length());
4169 if (!pdoc->IsReadOnly()) {
4170 cs.Clear();
4171 pdoc->AnnotationClearAll();
4172 pdoc->MarginClearAll();
4175 sel.Clear();
4176 SetTopLine(0);
4177 SetVerticalScrollPos();
4178 InvalidateStyleRedraw();
4181 void Editor::ClearDocumentStyle() {
4182 Decoration *deco = pdoc->decorations.root;
4183 while (deco) {
4184 // Save next in case deco deleted
4185 Decoration *decoNext = deco->next;
4186 if (deco->indicator < INDIC_CONTAINER) {
4187 pdoc->decorations.SetCurrentIndicator(deco->indicator);
4188 pdoc->DecorationFillRange(0, 0, pdoc->Length());
4190 deco = decoNext;
4192 pdoc->StartStyling(0, '\377');
4193 pdoc->SetStyleFor(pdoc->Length(), 0);
4194 cs.ShowAll();
4195 pdoc->ClearLevels();
4198 void Editor::CopyAllowLine() {
4199 SelectionText selectedText;
4200 CopySelectionRange(&selectedText, true);
4201 CopyToClipboard(selectedText);
4204 void Editor::Cut() {
4205 pdoc->CheckReadOnly();
4206 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
4207 Copy();
4208 ClearSelection();
4212 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
4213 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
4214 return;
4216 sel.Clear();
4217 sel.RangeMain() = SelectionRange(pos);
4218 int line = pdoc->LineFromPosition(sel.MainCaret());
4219 UndoGroup ug(pdoc);
4220 sel.RangeMain().caret = SelectionPosition(
4221 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
4222 int xInsert = XFromPosition(sel.RangeMain().caret);
4223 bool prevCr = false;
4224 while ((len > 0) && IsEOLChar(ptr[len-1]))
4225 len--;
4226 for (int i = 0; i < len; i++) {
4227 if (IsEOLChar(ptr[i])) {
4228 if ((ptr[i] == '\r') || (!prevCr))
4229 line++;
4230 if (line >= pdoc->LinesTotal()) {
4231 if (pdoc->eolMode != SC_EOL_LF)
4232 pdoc->InsertString(pdoc->Length(), "\r", 1);
4233 if (pdoc->eolMode != SC_EOL_CR)
4234 pdoc->InsertString(pdoc->Length(), "\n", 1);
4236 // Pad the end of lines with spaces if required
4237 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
4238 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
4239 while (XFromPosition(sel.MainCaret()) < xInsert) {
4240 const int lengthInserted = pdoc->InsertString(sel.MainCaret(), " ", 1);
4241 sel.RangeMain().caret.Add(lengthInserted);
4244 prevCr = ptr[i] == '\r';
4245 } else {
4246 const int lengthInserted = pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
4247 sel.RangeMain().caret.Add(lengthInserted);
4248 prevCr = false;
4251 SetEmptySelection(pos);
4254 bool Editor::CanPaste() {
4255 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
4258 void Editor::Clear() {
4259 // If multiple selections, don't delete EOLS
4260 if (sel.Empty()) {
4261 bool singleVirtual = false;
4262 if ((sel.Count() == 1) &&
4263 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
4264 sel.RangeMain().Start().VirtualSpace()) {
4265 singleVirtual = true;
4267 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
4268 for (size_t r=0; r<sel.Count(); r++) {
4269 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
4270 if (sel.Range(r).Start().VirtualSpace()) {
4271 if (sel.Range(r).anchor < sel.Range(r).caret)
4272 sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
4273 else
4274 sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
4276 if ((sel.Count() == 1) || !pdoc->IsPositionInLineEnd(sel.Range(r).caret.Position())) {
4277 pdoc->DelChar(sel.Range(r).caret.Position());
4278 sel.Range(r).ClearVirtualSpace();
4279 } // else multiple selection so don't eat line ends
4280 } else {
4281 sel.Range(r).ClearVirtualSpace();
4284 } else {
4285 ClearSelection();
4287 sel.RemoveDuplicates();
4290 void Editor::SelectAll() {
4291 sel.Clear();
4292 SetSelection(0, pdoc->Length());
4293 Redraw();
4296 void Editor::Undo() {
4297 if (pdoc->CanUndo()) {
4298 InvalidateCaret();
4299 int newPos = pdoc->Undo();
4300 if (newPos >= 0)
4301 SetEmptySelection(newPos);
4302 EnsureCaretVisible();
4306 void Editor::Redo() {
4307 if (pdoc->CanRedo()) {
4308 int newPos = pdoc->Redo();
4309 if (newPos >= 0)
4310 SetEmptySelection(newPos);
4311 EnsureCaretVisible();
4315 void Editor::DelChar() {
4316 if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) {
4317 pdoc->DelChar(sel.MainCaret());
4319 // Avoid blinking during rapid typing:
4320 ShowCaretAtCurrentPosition();
4323 void Editor::DelCharBack(bool allowLineStartDeletion) {
4324 if (!sel.IsRectangular())
4325 FilterSelections();
4326 if (sel.IsRectangular())
4327 allowLineStartDeletion = false;
4328 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
4329 if (sel.Empty()) {
4330 for (size_t r=0; r<sel.Count(); r++) {
4331 if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
4332 if (sel.Range(r).caret.VirtualSpace()) {
4333 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
4334 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
4335 } else {
4336 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4337 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
4338 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
4339 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
4340 UndoGroup ugInner(pdoc, !ug.Needed());
4341 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4342 int indentationStep = pdoc->IndentSize();
4343 int indentationChange = indentation % indentationStep;
4344 if (indentationChange == 0)
4345 indentationChange = indentationStep;
4346 const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationChange);
4347 // SetEmptySelection
4348 sel.Range(r) = SelectionRange(posSelect);
4349 } else {
4350 pdoc->DelCharBack(sel.Range(r).caret.Position());
4354 } else {
4355 sel.Range(r).ClearVirtualSpace();
4358 ThinRectangularRange();
4359 } else {
4360 ClearSelection();
4362 sel.RemoveDuplicates();
4363 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
4364 // Avoid blinking during rapid typing:
4365 ShowCaretAtCurrentPosition();
4368 int Editor::ModifierFlags(bool shift, bool ctrl, bool alt, bool meta) {
4369 return
4370 (shift ? SCI_SHIFT : 0) |
4371 (ctrl ? SCI_CTRL : 0) |
4372 (alt ? SCI_ALT : 0) |
4373 (meta ? SCI_META : 0);
4376 void Editor::NotifyFocus(bool focus) {
4377 SCNotification scn = {};
4378 scn.nmhdr.code = focus ? SCN_FOCUSIN : SCN_FOCUSOUT;
4379 NotifyParent(scn);
4382 void Editor::SetCtrlID(int identifier) {
4383 ctrlID = identifier;
4386 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
4387 SCNotification scn = {};
4388 scn.nmhdr.code = SCN_STYLENEEDED;
4389 scn.position = endStyleNeeded;
4390 NotifyParent(scn);
4393 void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
4394 NotifyStyleToNeeded(endStyleNeeded);
4397 void Editor::NotifyLexerChanged(Document *, void *) {
4400 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
4401 errorStatus = status;
4404 void Editor::NotifyChar(int ch) {
4405 SCNotification scn = {};
4406 scn.nmhdr.code = SCN_CHARADDED;
4407 scn.ch = ch;
4408 NotifyParent(scn);
4411 void Editor::NotifySavePoint(bool isSavePoint) {
4412 SCNotification scn = {};
4413 if (isSavePoint) {
4414 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
4415 } else {
4416 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
4418 NotifyParent(scn);
4421 void Editor::NotifyModifyAttempt() {
4422 SCNotification scn = {};
4423 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
4424 NotifyParent(scn);
4427 void Editor::NotifyDoubleClick(Point pt, int modifiers) {
4428 SCNotification scn = {};
4429 scn.nmhdr.code = SCN_DOUBLECLICK;
4430 scn.line = LineFromLocation(pt);
4431 scn.position = PositionFromLocation(pt, true);
4432 scn.modifiers = modifiers;
4433 NotifyParent(scn);
4436 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
4437 NotifyDoubleClick(pt, ModifierFlags(shift, ctrl, alt));
4440 void Editor::NotifyHotSpotDoubleClicked(int position, int modifiers) {
4441 SCNotification scn = {};
4442 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
4443 scn.position = position;
4444 scn.modifiers = modifiers;
4445 NotifyParent(scn);
4448 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
4449 NotifyHotSpotDoubleClicked(position, ModifierFlags(shift, ctrl, alt));
4452 void Editor::NotifyHotSpotClicked(int position, int modifiers) {
4453 SCNotification scn = {};
4454 scn.nmhdr.code = SCN_HOTSPOTCLICK;
4455 scn.position = position;
4456 scn.modifiers = modifiers;
4457 NotifyParent(scn);
4460 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
4461 NotifyHotSpotClicked(position, ModifierFlags(shift, ctrl, alt));
4464 void Editor::NotifyHotSpotReleaseClick(int position, int modifiers) {
4465 SCNotification scn = {};
4466 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
4467 scn.position = position;
4468 scn.modifiers = modifiers;
4469 NotifyParent(scn);
4472 void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
4473 NotifyHotSpotReleaseClick(position, ModifierFlags(shift, ctrl, alt));
4476 bool Editor::NotifyUpdateUI() {
4477 if (needUpdateUI) {
4478 SCNotification scn = {};
4479 scn.nmhdr.code = SCN_UPDATEUI;
4480 scn.updated = needUpdateUI;
4481 NotifyParent(scn);
4482 needUpdateUI = 0;
4483 return true;
4485 return false;
4488 void Editor::NotifyPainted() {
4489 SCNotification scn = {};
4490 scn.nmhdr.code = SCN_PAINTED;
4491 NotifyParent(scn);
4494 void Editor::NotifyIndicatorClick(bool click, int position, int modifiers) {
4495 int mask = pdoc->decorations.AllOnFor(position);
4496 if ((click && mask) || pdoc->decorations.clickNotified) {
4497 SCNotification scn = {};
4498 pdoc->decorations.clickNotified = click;
4499 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
4500 scn.modifiers = modifiers;
4501 scn.position = position;
4502 NotifyParent(scn);
4506 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
4507 NotifyIndicatorClick(click, position, ModifierFlags(shift, ctrl, alt));
4510 bool Editor::NotifyMarginClick(Point pt, int modifiers) {
4511 int marginClicked = -1;
4512 int x = vs.textStart - vs.fixedColumnWidth;
4513 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
4514 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
4515 marginClicked = margin;
4516 x += vs.ms[margin].width;
4518 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
4519 int position = pdoc->LineStart(LineFromLocation(pt));
4520 if ((vs.ms[marginClicked].mask & SC_MASK_FOLDERS) && (foldAutomatic & SC_AUTOMATICFOLD_CLICK)) {
4521 const bool ctrl = (modifiers & SCI_CTRL) != 0;
4522 const bool shift = (modifiers & SCI_SHIFT) != 0;
4523 int lineClick = pdoc->LineFromPosition(position);
4524 if (shift && ctrl) {
4525 FoldAll(SC_FOLDACTION_TOGGLE);
4526 } else {
4527 int levelClick = pdoc->GetLevel(lineClick);
4528 if (levelClick & SC_FOLDLEVELHEADERFLAG) {
4529 if (shift) {
4530 // Ensure all children visible
4531 FoldExpand(lineClick, SC_FOLDACTION_EXPAND, levelClick);
4532 } else if (ctrl) {
4533 FoldExpand(lineClick, SC_FOLDACTION_TOGGLE, levelClick);
4534 } else {
4535 // Toggle this line
4536 FoldLine(lineClick, SC_FOLDACTION_TOGGLE);
4540 return true;
4542 SCNotification scn = {};
4543 scn.nmhdr.code = SCN_MARGINCLICK;
4544 scn.modifiers = modifiers;
4545 scn.position = position;
4546 scn.margin = marginClicked;
4547 NotifyParent(scn);
4548 return true;
4549 } else {
4550 return false;
4554 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
4555 return NotifyMarginClick(pt, ModifierFlags(shift, ctrl, alt));
4558 void Editor::NotifyNeedShown(int pos, int len) {
4559 SCNotification scn = {};
4560 scn.nmhdr.code = SCN_NEEDSHOWN;
4561 scn.position = pos;
4562 scn.length = len;
4563 NotifyParent(scn);
4566 void Editor::NotifyDwelling(Point pt, bool state) {
4567 SCNotification scn = {};
4568 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
4569 scn.position = PositionFromLocation(pt, true);
4570 scn.x = static_cast<int>(pt.x + vs.ExternalMarginWidth());
4571 scn.y = static_cast<int>(pt.y);
4572 NotifyParent(scn);
4575 void Editor::NotifyZoom() {
4576 SCNotification scn = {};
4577 scn.nmhdr.code = SCN_ZOOM;
4578 NotifyParent(scn);
4581 // Notifications from document
4582 void Editor::NotifyModifyAttempt(Document *, void *) {
4583 //Platform::DebugPrintf("** Modify Attempt\n");
4584 NotifyModifyAttempt();
4587 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
4588 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
4589 NotifySavePoint(atSavePoint);
4592 void Editor::CheckModificationForWrap(DocModification mh) {
4593 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
4594 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4595 int lineDoc = pdoc->LineFromPosition(mh.position);
4596 int lines = Platform::Maximum(0, mh.linesAdded);
4597 if (Wrapping()) {
4598 NeedWrapping(lineDoc, lineDoc + lines + 1);
4600 RefreshStyleData();
4601 // Fix up annotation heights
4602 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
4606 // Move a position so it is still after the same character as before the insertion.
4607 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
4608 if (position > startInsertion) {
4609 return position + length;
4611 return position;
4614 // Move a position so it is still after the same character as before the deletion if that
4615 // character is still present else after the previous surviving character.
4616 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
4617 if (position > startDeletion) {
4618 int endDeletion = startDeletion + length;
4619 if (position > endDeletion) {
4620 return position - length;
4621 } else {
4622 return startDeletion;
4624 } else {
4625 return position;
4629 void Editor::NotifyModified(Document *, DocModification mh, void *) {
4630 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
4631 if (paintState == painting) {
4632 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
4634 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
4635 if (paintState == painting) {
4636 CheckForChangeOutsidePaint(
4637 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
4638 } else {
4639 // Could check that change is before last visible line.
4640 Redraw();
4643 if (mh.modificationType & SC_MOD_LEXERSTATE) {
4644 if (paintState == painting) {
4645 CheckForChangeOutsidePaint(
4646 Range(mh.position, mh.position + mh.length));
4647 } else {
4648 Redraw();
4651 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
4652 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4653 pdoc->IncrementStyleClock();
4655 if (paintState == notPainting) {
4656 if (mh.position < pdoc->LineStart(topLine)) {
4657 // Styling performed before this view
4658 Redraw();
4659 } else {
4660 InvalidateRange(mh.position, mh.position + mh.length);
4663 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4664 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4666 } else {
4667 // Move selection and brace highlights
4668 if (mh.modificationType & SC_MOD_INSERTTEXT) {
4669 sel.MovePositions(true, mh.position, mh.length);
4670 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
4671 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
4672 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
4673 sel.MovePositions(false, mh.position, mh.length);
4674 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
4675 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
4677 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && cs.HiddenLines()) {
4678 // Some lines are hidden so may need shown.
4679 // TODO: check if the modified area is hidden.
4680 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
4681 int lineOfPos = pdoc->LineFromPosition(mh.position);
4682 bool insertingNewLine = false;
4683 for (int i=0; i < mh.length; i++) {
4684 if ((mh.text[i] == '\n') || (mh.text[i] == '\r'))
4685 insertingNewLine = true;
4687 if (insertingNewLine && (mh.position != pdoc->LineStart(lineOfPos)))
4688 NeedShown(mh.position, pdoc->LineStart(lineOfPos+1) - mh.position);
4689 else
4690 NeedShown(mh.position, 0);
4691 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
4692 NeedShown(mh.position, mh.length);
4695 if (mh.linesAdded != 0) {
4696 // Update contraction state for inserted and removed lines
4697 // lineOfPos should be calculated in context of state before modification, shouldn't it
4698 int lineOfPos = pdoc->LineFromPosition(mh.position);
4699 if (mh.position > pdoc->LineStart(lineOfPos))
4700 lineOfPos++; // Affecting subsequent lines
4701 if (mh.linesAdded > 0) {
4702 cs.InsertLines(lineOfPos, mh.linesAdded);
4703 } else {
4704 cs.DeleteLines(lineOfPos, -mh.linesAdded);
4707 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
4708 int lineDoc = pdoc->LineFromPosition(mh.position);
4709 if (vs.annotationVisible) {
4710 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
4711 Redraw();
4714 CheckModificationForWrap(mh);
4715 if (mh.linesAdded != 0) {
4716 // Avoid scrolling of display if change before current display
4717 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
4718 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
4719 if (newTop != topLine) {
4720 SetTopLine(newTop);
4721 SetVerticalScrollPos();
4725 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
4726 QueueIdleWork(WorkNeeded::workStyle, pdoc->Length());
4727 Redraw();
4729 } else {
4730 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
4731 QueueIdleWork(WorkNeeded::workStyle, mh.position + mh.length);
4732 InvalidateRange(mh.position, mh.position + mh.length);
4737 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
4738 SetScrollBars();
4741 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
4742 if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) {
4743 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
4744 // Fold changes can affect the drawing of following lines so redraw whole margin
4745 RedrawSelMargin(highlightDelimiter.isEnabled ? -1 : mh.line-1, true);
4746 } else {
4747 RedrawSelMargin(mh.line);
4751 if ((mh.modificationType & SC_MOD_CHANGEFOLD) && (foldAutomatic & SC_AUTOMATICFOLD_CHANGE)) {
4752 FoldChanged(mh.line, mh.foldLevelNow, mh.foldLevelPrev);
4755 // NOW pay the piper WRT "deferred" visual updates
4756 if (IsLastStep(mh)) {
4757 SetScrollBars();
4758 Redraw();
4761 // If client wants to see this modification
4762 if (mh.modificationType & modEventMask) {
4763 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
4764 // Real modification made to text of document.
4765 NotifyChange(); // Send EN_CHANGE
4768 SCNotification scn = {};
4769 scn.nmhdr.code = SCN_MODIFIED;
4770 scn.position = mh.position;
4771 scn.modificationType = mh.modificationType;
4772 scn.text = mh.text;
4773 scn.length = mh.length;
4774 scn.linesAdded = mh.linesAdded;
4775 scn.line = mh.line;
4776 scn.foldLevelNow = mh.foldLevelNow;
4777 scn.foldLevelPrev = mh.foldLevelPrev;
4778 scn.token = mh.token;
4779 scn.annotationLinesAdded = mh.annotationLinesAdded;
4780 NotifyParent(scn);
4784 void Editor::NotifyDeleted(Document *, void *) {
4785 /* Do nothing */
4788 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
4790 // Enumerates all macroable messages
4791 switch (iMessage) {
4792 case SCI_CUT:
4793 case SCI_COPY:
4794 case SCI_PASTE:
4795 case SCI_CLEAR:
4796 case SCI_REPLACESEL:
4797 case SCI_ADDTEXT:
4798 case SCI_INSERTTEXT:
4799 case SCI_APPENDTEXT:
4800 case SCI_CLEARALL:
4801 case SCI_SELECTALL:
4802 case SCI_GOTOLINE:
4803 case SCI_GOTOPOS:
4804 case SCI_SEARCHANCHOR:
4805 case SCI_SEARCHNEXT:
4806 case SCI_SEARCHPREV:
4807 case SCI_LINEDOWN:
4808 case SCI_LINEDOWNEXTEND:
4809 case SCI_PARADOWN:
4810 case SCI_PARADOWNEXTEND:
4811 case SCI_LINEUP:
4812 case SCI_LINEUPEXTEND:
4813 case SCI_PARAUP:
4814 case SCI_PARAUPEXTEND:
4815 case SCI_CHARLEFT:
4816 case SCI_CHARLEFTEXTEND:
4817 case SCI_CHARRIGHT:
4818 case SCI_CHARRIGHTEXTEND:
4819 case SCI_WORDLEFT:
4820 case SCI_WORDLEFTEXTEND:
4821 case SCI_WORDRIGHT:
4822 case SCI_WORDRIGHTEXTEND:
4823 case SCI_WORDPARTLEFT:
4824 case SCI_WORDPARTLEFTEXTEND:
4825 case SCI_WORDPARTRIGHT:
4826 case SCI_WORDPARTRIGHTEXTEND:
4827 case SCI_WORDLEFTEND:
4828 case SCI_WORDLEFTENDEXTEND:
4829 case SCI_WORDRIGHTEND:
4830 case SCI_WORDRIGHTENDEXTEND:
4831 case SCI_HOME:
4832 case SCI_HOMEEXTEND:
4833 case SCI_LINEEND:
4834 case SCI_LINEENDEXTEND:
4835 case SCI_HOMEWRAP:
4836 case SCI_HOMEWRAPEXTEND:
4837 case SCI_LINEENDWRAP:
4838 case SCI_LINEENDWRAPEXTEND:
4839 case SCI_DOCUMENTSTART:
4840 case SCI_DOCUMENTSTARTEXTEND:
4841 case SCI_DOCUMENTEND:
4842 case SCI_DOCUMENTENDEXTEND:
4843 case SCI_STUTTEREDPAGEUP:
4844 case SCI_STUTTEREDPAGEUPEXTEND:
4845 case SCI_STUTTEREDPAGEDOWN:
4846 case SCI_STUTTEREDPAGEDOWNEXTEND:
4847 case SCI_PAGEUP:
4848 case SCI_PAGEUPEXTEND:
4849 case SCI_PAGEDOWN:
4850 case SCI_PAGEDOWNEXTEND:
4851 case SCI_EDITTOGGLEOVERTYPE:
4852 case SCI_CANCEL:
4853 case SCI_DELETEBACK:
4854 case SCI_TAB:
4855 case SCI_BACKTAB:
4856 case SCI_FORMFEED:
4857 case SCI_VCHOME:
4858 case SCI_VCHOMEEXTEND:
4859 case SCI_VCHOMEWRAP:
4860 case SCI_VCHOMEWRAPEXTEND:
4861 case SCI_VCHOMEDISPLAY:
4862 case SCI_VCHOMEDISPLAYEXTEND:
4863 case SCI_DELWORDLEFT:
4864 case SCI_DELWORDRIGHT:
4865 case SCI_DELWORDRIGHTEND:
4866 case SCI_DELLINELEFT:
4867 case SCI_DELLINERIGHT:
4868 case SCI_LINECOPY:
4869 case SCI_LINECUT:
4870 case SCI_LINEDELETE:
4871 case SCI_LINETRANSPOSE:
4872 case SCI_LINEDUPLICATE:
4873 case SCI_LOWERCASE:
4874 case SCI_UPPERCASE:
4875 case SCI_LINESCROLLDOWN:
4876 case SCI_LINESCROLLUP:
4877 case SCI_DELETEBACKNOTLINE:
4878 case SCI_HOMEDISPLAY:
4879 case SCI_HOMEDISPLAYEXTEND:
4880 case SCI_LINEENDDISPLAY:
4881 case SCI_LINEENDDISPLAYEXTEND:
4882 case SCI_SETSELECTIONMODE:
4883 case SCI_LINEDOWNRECTEXTEND:
4884 case SCI_LINEUPRECTEXTEND:
4885 case SCI_CHARLEFTRECTEXTEND:
4886 case SCI_CHARRIGHTRECTEXTEND:
4887 case SCI_HOMERECTEXTEND:
4888 case SCI_VCHOMERECTEXTEND:
4889 case SCI_LINEENDRECTEXTEND:
4890 case SCI_PAGEUPRECTEXTEND:
4891 case SCI_PAGEDOWNRECTEXTEND:
4892 case SCI_SELECTIONDUPLICATE:
4893 case SCI_COPYALLOWLINE:
4894 case SCI_VERTICALCENTRECARET:
4895 case SCI_MOVESELECTEDLINESUP:
4896 case SCI_MOVESELECTEDLINESDOWN:
4897 case SCI_SCROLLTOSTART:
4898 case SCI_SCROLLTOEND:
4899 break;
4901 // Filter out all others like display changes. Also, newlines are redundant
4902 // with char insert messages.
4903 case SCI_NEWLINE:
4904 default:
4905 // printf("Filtered out %ld of macro recording\n", iMessage);
4906 return;
4909 // Send notification
4910 SCNotification scn = {};
4911 scn.nmhdr.code = SCN_MACRORECORD;
4912 scn.message = iMessage;
4913 scn.wParam = wParam;
4914 scn.lParam = lParam;
4915 NotifyParent(scn);
4918 // Something has changed that the container should know about
4919 void Editor::ContainerNeedsUpdate(int flags) {
4920 needUpdateUI |= flags;
4924 * Force scroll and keep position relative to top of window.
4926 * If stuttered = true and not already at first/last row, move to first/last row of window.
4927 * If stuttered = true and already at first/last row, scroll as normal.
4929 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
4930 int topLineNew;
4931 SelectionPosition newPos;
4933 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
4934 int topStutterLine = topLine + caretYSlop;
4935 int bottomStutterLine =
4936 pdoc->LineFromPosition(PositionFromLocation(
4937 Point::FromInts(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
4938 - caretYSlop - 1;
4940 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
4941 topLineNew = topLine;
4942 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
4943 false, false, UserVirtualSpace());
4945 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
4946 topLineNew = topLine;
4947 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
4948 false, false, UserVirtualSpace());
4950 } else {
4951 Point pt = LocationFromPosition(sel.MainCaret());
4953 topLineNew = Platform::Clamp(
4954 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
4955 newPos = SPositionFromLocation(
4956 Point::FromInts(lastXChosen - xOffset, static_cast<int>(pt.y) + direction * (vs.lineHeight * LinesToScroll())),
4957 false, false, UserVirtualSpace());
4960 if (topLineNew != topLine) {
4961 SetTopLine(topLineNew);
4962 MovePositionTo(newPos, selt);
4963 Redraw();
4964 SetVerticalScrollPos();
4965 } else {
4966 MovePositionTo(newPos, selt);
4970 void Editor::ChangeCaseOfSelection(int caseMapping) {
4971 UndoGroup ug(pdoc);
4972 for (size_t r=0; r<sel.Count(); r++) {
4973 SelectionRange current = sel.Range(r);
4974 SelectionRange currentNoVS = current;
4975 currentNoVS.ClearVirtualSpace();
4976 size_t rangeBytes = currentNoVS.Length();
4977 if (rangeBytes > 0) {
4978 std::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position());
4980 std::string sMapped = CaseMapString(sText, caseMapping);
4982 if (sMapped != sText) {
4983 size_t firstDifference = 0;
4984 while (sMapped[firstDifference] == sText[firstDifference])
4985 firstDifference++;
4986 size_t lastDifferenceText = sText.size() - 1;
4987 size_t lastDifferenceMapped = sMapped.size() - 1;
4988 while (sMapped[lastDifferenceMapped] == sText[lastDifferenceText]) {
4989 lastDifferenceText--;
4990 lastDifferenceMapped--;
4992 size_t endDifferenceText = sText.size() - 1 - lastDifferenceText;
4993 pdoc->DeleteChars(
4994 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
4995 static_cast<int>(rangeBytes - firstDifference - endDifferenceText));
4996 const int lengthChange = static_cast<int>(lastDifferenceMapped - firstDifference + 1);
4997 const int lengthInserted = pdoc->InsertString(
4998 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
4999 sMapped.c_str() + firstDifference,
5000 lengthChange);
5001 // Automatic movement changes selection so reset to exactly the same as it was.
5002 int diffSizes = static_cast<int>(sMapped.size() - sText.size()) + lengthInserted - lengthChange;
5003 if (diffSizes != 0) {
5004 if (current.anchor > current.caret)
5005 current.anchor.Add(diffSizes);
5006 else
5007 current.caret.Add(diffSizes);
5009 sel.Range(r) = current;
5015 void Editor::LineTranspose() {
5016 int line = pdoc->LineFromPosition(sel.MainCaret());
5017 if (line > 0) {
5018 UndoGroup ug(pdoc);
5020 const int startPrevious = pdoc->LineStart(line - 1);
5021 const std::string linePrevious = RangeText(startPrevious, pdoc->LineEnd(line - 1));
5023 int startCurrent = pdoc->LineStart(line);
5024 const std::string lineCurrent = RangeText(startCurrent, pdoc->LineEnd(line));
5026 pdoc->DeleteChars(startCurrent, static_cast<int>(lineCurrent.length()));
5027 pdoc->DeleteChars(startPrevious, static_cast<int>(linePrevious.length()));
5028 startCurrent -= static_cast<int>(linePrevious.length());
5030 startCurrent += pdoc->InsertString(startPrevious, lineCurrent.c_str(),
5031 static_cast<int>(lineCurrent.length()));
5032 pdoc->InsertString(startCurrent, linePrevious.c_str(),
5033 static_cast<int>(linePrevious.length()));
5034 // Move caret to start of current line
5035 MovePositionTo(SelectionPosition(startCurrent));
5039 void Editor::Duplicate(bool forLine) {
5040 if (sel.Empty()) {
5041 forLine = true;
5043 UndoGroup ug(pdoc);
5044 const char *eol = "";
5045 int eolLen = 0;
5046 if (forLine) {
5047 eol = StringFromEOLMode(pdoc->eolMode);
5048 eolLen = istrlen(eol);
5050 for (size_t r=0; r<sel.Count(); r++) {
5051 SelectionPosition start = sel.Range(r).Start();
5052 SelectionPosition end = sel.Range(r).End();
5053 if (forLine) {
5054 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
5055 start = SelectionPosition(pdoc->LineStart(line));
5056 end = SelectionPosition(pdoc->LineEnd(line));
5058 std::string text = RangeText(start.Position(), end.Position());
5059 int lengthInserted = eolLen;
5060 if (forLine)
5061 lengthInserted = pdoc->InsertString(end.Position(), eol, eolLen);
5062 pdoc->InsertString(end.Position() + lengthInserted, text.c_str(), static_cast<int>(text.length()));
5064 if (sel.Count() && sel.IsRectangular()) {
5065 SelectionPosition last = sel.Last();
5066 if (forLine) {
5067 int line = pdoc->LineFromPosition(last.Position());
5068 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
5070 if (sel.Rectangular().anchor > sel.Rectangular().caret)
5071 sel.Rectangular().anchor = last;
5072 else
5073 sel.Rectangular().caret = last;
5074 SetRectangularRange();
5078 void Editor::CancelModes() {
5079 sel.SetMoveExtends(false);
5082 void Editor::NewLine() {
5083 // Remove non-main ranges
5084 InvalidateSelection(sel.RangeMain(), true);
5085 sel.SetSelection(sel.RangeMain());
5086 sel.RangeMain().ClearVirtualSpace();
5088 // Clear main range and insert line end
5089 bool needGroupUndo = !sel.Empty();
5090 if (needGroupUndo)
5091 pdoc->BeginUndoAction();
5093 if (!sel.Empty())
5094 ClearSelection();
5095 const char *eol = "\n";
5096 if (pdoc->eolMode == SC_EOL_CRLF) {
5097 eol = "\r\n";
5098 } else if (pdoc->eolMode == SC_EOL_CR) {
5099 eol = "\r";
5100 } // else SC_EOL_LF -> "\n" already set
5101 const int insertLength = pdoc->InsertString(sel.MainCaret(), eol, istrlen(eol));
5102 // Want to end undo group before NotifyChar as applications often modify text here
5103 if (needGroupUndo)
5104 pdoc->EndUndoAction();
5105 if (insertLength > 0) {
5106 SetEmptySelection(sel.MainCaret() + insertLength);
5107 while (*eol) {
5108 NotifyChar(*eol);
5109 if (recordingMacro) {
5110 char txt[2];
5111 txt[0] = *eol;
5112 txt[1] = '\0';
5113 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
5115 eol++;
5118 SetLastXChosen();
5119 SetScrollBars();
5120 EnsureCaretVisible();
5121 // Avoid blinking during rapid typing:
5122 ShowCaretAtCurrentPosition();
5125 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
5126 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
5127 if (sel.IsRectangular()) {
5128 if (selt == Selection::noSel) {
5129 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
5130 } else {
5131 caretToUse = sel.Rectangular().caret;
5135 Point pt = LocationFromPosition(caretToUse);
5136 int skipLines = 0;
5138 if (vs.annotationVisible) {
5139 int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
5140 Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
5141 int subLine = static_cast<int>(pt.y - ptStartLine.y) / vs.lineHeight;
5143 if (direction < 0 && subLine == 0) {
5144 int lineDisplay = cs.DisplayFromDoc(lineDoc);
5145 if (lineDisplay > 0) {
5146 skipLines = pdoc->AnnotationLines(cs.DocFromDisplay(lineDisplay - 1));
5148 } else if (direction > 0 && subLine >= (cs.GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
5149 skipLines = pdoc->AnnotationLines(lineDoc);
5153 int newY = static_cast<int>(pt.y) + (1 + skipLines) * direction * vs.lineHeight;
5154 SelectionPosition posNew = SPositionFromLocation(
5155 Point::FromInts(lastXChosen - xOffset, newY), false, false, UserVirtualSpace());
5157 if (direction < 0) {
5158 // Line wrapping may lead to a location on the same line, so
5159 // seek back if that is the case.
5160 Point ptNew = LocationFromPosition(posNew.Position());
5161 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
5162 posNew.Add(-1);
5163 posNew.SetVirtualSpace(0);
5164 ptNew = LocationFromPosition(posNew.Position());
5166 } else if (direction > 0 && posNew.Position() != pdoc->Length()) {
5167 // There is an equivalent case when moving down which skips
5168 // over a line.
5169 Point ptNew = LocationFromPosition(posNew.Position());
5170 while ((posNew.Position() > caretToUse.Position()) && (ptNew.y > newY)) {
5171 posNew.Add(-1);
5172 posNew.SetVirtualSpace(0);
5173 ptNew = LocationFromPosition(posNew.Position());
5177 MovePositionTo(MovePositionSoVisible(posNew, direction), selt);
5180 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
5181 int lineDoc, savedPos = sel.MainCaret();
5182 do {
5183 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
5184 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
5185 if (direction > 0) {
5186 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
5187 if (selt == Selection::noSel) {
5188 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
5190 break;
5193 } while (!cs.GetVisible(lineDoc));
5196 int Editor::StartEndDisplayLine(int pos, bool start) {
5197 RefreshStyleData();
5198 int line = pdoc->LineFromPosition(pos);
5199 AutoSurface surface(this);
5200 AutoLineLayout ll(llc, RetrieveLineLayout(line));
5201 int posRet = INVALID_POSITION;
5202 if (surface && ll) {
5203 unsigned int posLineStart = pdoc->LineStart(line);
5204 LayoutLine(line, surface, vs, ll, wrapWidth);
5205 int posInLine = pos - posLineStart;
5206 if (posInLine <= ll->maxLineLength) {
5207 for (int subLine = 0; subLine < ll->lines; subLine++) {
5208 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
5209 if (start) {
5210 posRet = ll->LineStart(subLine) + posLineStart;
5211 } else {
5212 if (subLine == ll->lines - 1)
5213 posRet = ll->LineStart(subLine + 1) + posLineStart;
5214 else
5215 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
5221 if (posRet == INVALID_POSITION) {
5222 return pos;
5223 } else {
5224 return posRet;
5228 int Editor::KeyCommand(unsigned int iMessage) {
5229 switch (iMessage) {
5230 case SCI_LINEDOWN:
5231 CursorUpOrDown(1);
5232 break;
5233 case SCI_LINEDOWNEXTEND:
5234 CursorUpOrDown(1, Selection::selStream);
5235 break;
5236 case SCI_LINEDOWNRECTEXTEND:
5237 CursorUpOrDown(1, Selection::selRectangle);
5238 break;
5239 case SCI_PARADOWN:
5240 ParaUpOrDown(1);
5241 break;
5242 case SCI_PARADOWNEXTEND:
5243 ParaUpOrDown(1, Selection::selStream);
5244 break;
5245 case SCI_LINESCROLLDOWN:
5246 ScrollTo(topLine + 1);
5247 MoveCaretInsideView(false);
5248 break;
5249 case SCI_LINEUP:
5250 CursorUpOrDown(-1);
5251 break;
5252 case SCI_LINEUPEXTEND:
5253 CursorUpOrDown(-1, Selection::selStream);
5254 break;
5255 case SCI_LINEUPRECTEXTEND:
5256 CursorUpOrDown(-1, Selection::selRectangle);
5257 break;
5258 case SCI_PARAUP:
5259 ParaUpOrDown(-1);
5260 break;
5261 case SCI_PARAUPEXTEND:
5262 ParaUpOrDown(-1, Selection::selStream);
5263 break;
5264 case SCI_LINESCROLLUP:
5265 ScrollTo(topLine - 1);
5266 MoveCaretInsideView(false);
5267 break;
5268 case SCI_CHARLEFT:
5269 if (SelectionEmpty() || sel.MoveExtends()) {
5270 if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5271 SelectionPosition spCaret = sel.RangeMain().caret;
5272 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5273 MovePositionTo(spCaret);
5274 } else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
5275 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1));
5276 } else {
5277 MovePositionTo(MovePositionSoVisible(
5278 SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
5280 } else {
5281 MovePositionTo(sel.LimitsForRectangularElseMain().start);
5283 SetLastXChosen();
5284 break;
5285 case SCI_CHARLEFTEXTEND:
5286 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5287 SelectionPosition spCaret = sel.RangeMain().caret;
5288 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5289 MovePositionTo(spCaret, Selection::selStream);
5290 } else {
5291 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
5293 SetLastXChosen();
5294 break;
5295 case SCI_CHARLEFTRECTEXTEND:
5296 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5297 SelectionPosition spCaret = sel.RangeMain().caret;
5298 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5299 MovePositionTo(spCaret, Selection::selRectangle);
5300 } else {
5301 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
5303 SetLastXChosen();
5304 break;
5305 case SCI_CHARRIGHT:
5306 if (SelectionEmpty() || sel.MoveExtends()) {
5307 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5308 SelectionPosition spCaret = sel.RangeMain().caret;
5309 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5310 MovePositionTo(spCaret);
5311 } else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
5312 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1));
5313 } else {
5314 MovePositionTo(MovePositionSoVisible(
5315 SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
5317 } else {
5318 MovePositionTo(sel.LimitsForRectangularElseMain().end);
5320 SetLastXChosen();
5321 break;
5322 case SCI_CHARRIGHTEXTEND:
5323 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5324 SelectionPosition spCaret = sel.RangeMain().caret;
5325 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5326 MovePositionTo(spCaret, Selection::selStream);
5327 } else {
5328 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
5330 SetLastXChosen();
5331 break;
5332 case SCI_CHARRIGHTRECTEXTEND:
5333 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5334 SelectionPosition spCaret = sel.RangeMain().caret;
5335 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5336 MovePositionTo(spCaret, Selection::selRectangle);
5337 } else {
5338 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
5340 SetLastXChosen();
5341 break;
5342 case SCI_WORDLEFT:
5343 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
5344 SetLastXChosen();
5345 break;
5346 case SCI_WORDLEFTEXTEND:
5347 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
5348 SetLastXChosen();
5349 break;
5350 case SCI_WORDRIGHT:
5351 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
5352 SetLastXChosen();
5353 break;
5354 case SCI_WORDRIGHTEXTEND:
5355 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
5356 SetLastXChosen();
5357 break;
5359 case SCI_WORDLEFTEND:
5360 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
5361 SetLastXChosen();
5362 break;
5363 case SCI_WORDLEFTENDEXTEND:
5364 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
5365 SetLastXChosen();
5366 break;
5367 case SCI_WORDRIGHTEND:
5368 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
5369 SetLastXChosen();
5370 break;
5371 case SCI_WORDRIGHTENDEXTEND:
5372 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
5373 SetLastXChosen();
5374 break;
5376 case SCI_HOME:
5377 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5378 SetLastXChosen();
5379 break;
5380 case SCI_HOMEEXTEND:
5381 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
5382 SetLastXChosen();
5383 break;
5384 case SCI_HOMERECTEXTEND:
5385 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
5386 SetLastXChosen();
5387 break;
5388 case SCI_LINEEND:
5389 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
5390 SetLastXChosen();
5391 break;
5392 case SCI_LINEENDEXTEND:
5393 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
5394 SetLastXChosen();
5395 break;
5396 case SCI_LINEENDRECTEXTEND:
5397 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
5398 SetLastXChosen();
5399 break;
5400 case SCI_HOMEWRAP: {
5401 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5402 if (sel.RangeMain().caret <= homePos)
5403 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5404 MovePositionTo(homePos);
5405 SetLastXChosen();
5407 break;
5408 case SCI_HOMEWRAPEXTEND: {
5409 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5410 if (sel.RangeMain().caret <= homePos)
5411 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5412 MovePositionTo(homePos, Selection::selStream);
5413 SetLastXChosen();
5415 break;
5416 case SCI_LINEENDWRAP: {
5417 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5418 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5419 if (endPos > realEndPos // if moved past visible EOLs
5420 || sel.RangeMain().caret >= endPos) // if at end of display line already
5421 endPos = realEndPos;
5422 MovePositionTo(endPos);
5423 SetLastXChosen();
5425 break;
5426 case SCI_LINEENDWRAPEXTEND: {
5427 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5428 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5429 if (endPos > realEndPos // if moved past visible EOLs
5430 || sel.RangeMain().caret >= endPos) // if at end of display line already
5431 endPos = realEndPos;
5432 MovePositionTo(endPos, Selection::selStream);
5433 SetLastXChosen();
5435 break;
5436 case SCI_DOCUMENTSTART:
5437 MovePositionTo(0);
5438 SetLastXChosen();
5439 break;
5440 case SCI_DOCUMENTSTARTEXTEND:
5441 MovePositionTo(0, Selection::selStream);
5442 SetLastXChosen();
5443 break;
5444 case SCI_DOCUMENTEND:
5445 MovePositionTo(pdoc->Length());
5446 SetLastXChosen();
5447 break;
5448 case SCI_DOCUMENTENDEXTEND:
5449 MovePositionTo(pdoc->Length(), Selection::selStream);
5450 SetLastXChosen();
5451 break;
5452 case SCI_STUTTEREDPAGEUP:
5453 PageMove(-1, Selection::noSel, true);
5454 break;
5455 case SCI_STUTTEREDPAGEUPEXTEND:
5456 PageMove(-1, Selection::selStream, true);
5457 break;
5458 case SCI_STUTTEREDPAGEDOWN:
5459 PageMove(1, Selection::noSel, true);
5460 break;
5461 case SCI_STUTTEREDPAGEDOWNEXTEND:
5462 PageMove(1, Selection::selStream, true);
5463 break;
5464 case SCI_PAGEUP:
5465 PageMove(-1);
5466 break;
5467 case SCI_PAGEUPEXTEND:
5468 PageMove(-1, Selection::selStream);
5469 break;
5470 case SCI_PAGEUPRECTEXTEND:
5471 PageMove(-1, Selection::selRectangle);
5472 break;
5473 case SCI_PAGEDOWN:
5474 PageMove(1);
5475 break;
5476 case SCI_PAGEDOWNEXTEND:
5477 PageMove(1, Selection::selStream);
5478 break;
5479 case SCI_PAGEDOWNRECTEXTEND:
5480 PageMove(1, Selection::selRectangle);
5481 break;
5482 case SCI_EDITTOGGLEOVERTYPE:
5483 inOverstrike = !inOverstrike;
5484 DropCaret();
5485 ShowCaretAtCurrentPosition();
5486 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
5487 NotifyUpdateUI();
5488 break;
5489 case SCI_CANCEL: // Cancel any modes - handled in subclass
5490 // Also unselect text
5491 CancelModes();
5492 break;
5493 case SCI_DELETEBACK:
5494 DelCharBack(true);
5495 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5496 SetLastXChosen();
5498 EnsureCaretVisible();
5499 break;
5500 case SCI_DELETEBACKNOTLINE:
5501 DelCharBack(false);
5502 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5503 SetLastXChosen();
5505 EnsureCaretVisible();
5506 break;
5507 case SCI_TAB:
5508 Indent(true);
5509 if (caretSticky == SC_CARETSTICKY_OFF) {
5510 SetLastXChosen();
5512 EnsureCaretVisible();
5513 ShowCaretAtCurrentPosition(); // Avoid blinking
5514 break;
5515 case SCI_BACKTAB:
5516 Indent(false);
5517 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5518 SetLastXChosen();
5520 EnsureCaretVisible();
5521 ShowCaretAtCurrentPosition(); // Avoid blinking
5522 break;
5523 case SCI_NEWLINE:
5524 NewLine();
5525 break;
5526 case SCI_FORMFEED:
5527 AddChar('\f');
5528 break;
5529 case SCI_VCHOME:
5530 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
5531 SetLastXChosen();
5532 break;
5533 case SCI_VCHOMEEXTEND:
5534 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
5535 SetLastXChosen();
5536 break;
5537 case SCI_VCHOMERECTEXTEND:
5538 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
5539 SetLastXChosen();
5540 break;
5541 case SCI_VCHOMEWRAP: {
5542 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5543 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5544 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5545 homePos = viewLineStart;
5547 MovePositionTo(homePos);
5548 SetLastXChosen();
5550 break;
5551 case SCI_VCHOMEWRAPEXTEND: {
5552 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5553 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5554 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5555 homePos = viewLineStart;
5557 MovePositionTo(homePos, Selection::selStream);
5558 SetLastXChosen();
5560 break;
5561 case SCI_ZOOMIN:
5562 if (vs.zoomLevel < 20) {
5563 vs.zoomLevel++;
5564 InvalidateStyleRedraw();
5565 NotifyZoom();
5567 break;
5568 case SCI_ZOOMOUT:
5569 if (vs.zoomLevel > -10) {
5570 vs.zoomLevel--;
5571 InvalidateStyleRedraw();
5572 NotifyZoom();
5574 break;
5575 case SCI_DELWORDLEFT: {
5576 int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
5577 pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
5578 sel.RangeMain().ClearVirtualSpace();
5579 SetLastXChosen();
5581 break;
5582 case SCI_DELWORDRIGHT: {
5583 UndoGroup ug(pdoc);
5584 sel.RangeMain().caret = SelectionPosition(
5585 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5586 sel.RangeMain().anchor = sel.RangeMain().caret;
5587 int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
5588 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5590 break;
5591 case SCI_DELWORDRIGHTEND: {
5592 UndoGroup ug(pdoc);
5593 sel.RangeMain().caret = SelectionPosition(
5594 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5595 int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
5596 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5598 break;
5599 case SCI_DELLINELEFT: {
5600 int line = pdoc->LineFromPosition(sel.MainCaret());
5601 int start = pdoc->LineStart(line);
5602 pdoc->DeleteChars(start, sel.MainCaret() - start);
5603 sel.RangeMain().ClearVirtualSpace();
5604 SetLastXChosen();
5606 break;
5607 case SCI_DELLINERIGHT: {
5608 int line = pdoc->LineFromPosition(sel.MainCaret());
5609 int end = pdoc->LineEnd(line);
5610 pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
5612 break;
5613 case SCI_LINECOPY: {
5614 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5615 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5616 CopyRangeToClipboard(pdoc->LineStart(lineStart),
5617 pdoc->LineStart(lineEnd + 1));
5619 break;
5620 case SCI_LINECUT: {
5621 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5622 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5623 int start = pdoc->LineStart(lineStart);
5624 int end = pdoc->LineStart(lineEnd + 1);
5625 SetSelection(start, end);
5626 Cut();
5627 SetLastXChosen();
5629 break;
5630 case SCI_LINEDELETE: {
5631 int line = pdoc->LineFromPosition(sel.MainCaret());
5632 int start = pdoc->LineStart(line);
5633 int end = pdoc->LineStart(line + 1);
5634 pdoc->DeleteChars(start, end - start);
5636 break;
5637 case SCI_LINETRANSPOSE:
5638 LineTranspose();
5639 break;
5640 case SCI_LINEDUPLICATE:
5641 Duplicate(true);
5642 break;
5643 case SCI_SELECTIONDUPLICATE:
5644 Duplicate(false);
5645 break;
5646 case SCI_LOWERCASE:
5647 ChangeCaseOfSelection(cmLower);
5648 break;
5649 case SCI_UPPERCASE:
5650 ChangeCaseOfSelection(cmUpper);
5651 break;
5652 case SCI_WORDPARTLEFT:
5653 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
5654 SetLastXChosen();
5655 break;
5656 case SCI_WORDPARTLEFTEXTEND:
5657 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
5658 SetLastXChosen();
5659 break;
5660 case SCI_WORDPARTRIGHT:
5661 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
5662 SetLastXChosen();
5663 break;
5664 case SCI_WORDPARTRIGHTEXTEND:
5665 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
5666 SetLastXChosen();
5667 break;
5668 case SCI_HOMEDISPLAY:
5669 MovePositionTo(MovePositionSoVisible(
5670 StartEndDisplayLine(sel.MainCaret(), true), -1));
5671 SetLastXChosen();
5672 break;
5673 case SCI_VCHOMEDISPLAY: {
5674 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5675 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5676 if (viewLineStart > homePos)
5677 homePos = viewLineStart;
5679 MovePositionTo(homePos);
5680 SetLastXChosen();
5682 break;
5683 case SCI_HOMEDISPLAYEXTEND:
5684 MovePositionTo(MovePositionSoVisible(
5685 StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
5686 SetLastXChosen();
5687 break;
5688 case SCI_VCHOMEDISPLAYEXTEND: {
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, Selection::selStream);
5695 SetLastXChosen();
5697 break;
5698 case SCI_LINEENDDISPLAY:
5699 MovePositionTo(MovePositionSoVisible(
5700 StartEndDisplayLine(sel.MainCaret(), false), 1));
5701 SetLastXChosen();
5702 break;
5703 case SCI_LINEENDDISPLAYEXTEND:
5704 MovePositionTo(MovePositionSoVisible(
5705 StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
5706 SetLastXChosen();
5707 break;
5708 case SCI_SCROLLTOSTART:
5709 ScrollTo(0);
5710 break;
5711 case SCI_SCROLLTOEND:
5712 ScrollTo(MaxScrollPos());
5713 break;
5715 return 0;
5718 int Editor::KeyDefault(int, int) {
5719 return 0;
5722 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
5723 DwellEnd(false);
5724 int msg = kmap.Find(key, modifiers);
5725 if (msg) {
5726 if (consumed)
5727 *consumed = true;
5728 return static_cast<int>(WndProc(msg, 0, 0));
5729 } else {
5730 if (consumed)
5731 *consumed = false;
5732 return KeyDefault(key, modifiers);
5736 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
5737 return KeyDownWithModifiers(key, ModifierFlags(shift, ctrl, alt), consumed);
5740 void Editor::Indent(bool forwards) {
5741 UndoGroup ug(pdoc);
5742 for (size_t r=0; r<sel.Count(); r++) {
5743 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
5744 int caretPosition = sel.Range(r).caret.Position();
5745 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
5746 if (lineOfAnchor == lineCurrentPos) {
5747 if (forwards) {
5748 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
5749 caretPosition = sel.Range(r).caret.Position();
5750 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
5751 pdoc->tabIndents) {
5752 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5753 int indentationStep = pdoc->IndentSize();
5754 const int posSelect = pdoc->SetLineIndentation(
5755 lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
5756 sel.Range(r) = SelectionRange(posSelect);
5757 } else {
5758 if (pdoc->useTabs) {
5759 const int lengthInserted = pdoc->InsertString(caretPosition, "\t", 1);
5760 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
5761 } else {
5762 int numSpaces = (pdoc->tabInChars) -
5763 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
5764 if (numSpaces < 1)
5765 numSpaces = pdoc->tabInChars;
5766 const std::string spaceText(numSpaces, ' ');
5767 const int lengthInserted = pdoc->InsertString(caretPosition, spaceText.c_str(),
5768 static_cast<int>(spaceText.length()));
5769 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
5772 } else {
5773 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
5774 pdoc->tabIndents) {
5775 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5776 int indentationStep = pdoc->IndentSize();
5777 const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
5778 sel.Range(r) = SelectionRange(posSelect);
5779 } else {
5780 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
5781 pdoc->tabInChars;
5782 if (newColumn < 0)
5783 newColumn = 0;
5784 int newPos = caretPosition;
5785 while (pdoc->GetColumn(newPos) > newColumn)
5786 newPos--;
5787 sel.Range(r) = SelectionRange(newPos);
5790 } else { // Multiline
5791 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
5792 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
5793 // Multiple lines selected so indent / dedent
5794 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
5795 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
5796 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
5797 lineBottomSel--; // If not selecting any characters on a line, do not indent
5798 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
5799 if (lineOfAnchor < lineCurrentPos) {
5800 if (currentPosPosOnLine == 0)
5801 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5802 else
5803 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
5804 } else {
5805 if (anchorPosOnLine == 0)
5806 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5807 else
5808 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
5812 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
5815 class CaseFolderASCII : public CaseFolderTable {
5816 public:
5817 CaseFolderASCII() {
5818 StandardASCII();
5820 ~CaseFolderASCII() {
5825 CaseFolder *Editor::CaseFolderForEncoding() {
5826 // Simple default that only maps ASCII upper case to lower case.
5827 return new CaseFolderASCII();
5831 * Search of a text in the document, in the given range.
5832 * @return The position of the found text, -1 if not found.
5834 long Editor::FindText(
5835 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5836 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5837 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
5839 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
5840 int lengthFound = istrlen(ft->lpstrText);
5841 if (!pdoc->HasCaseFolder())
5842 pdoc->SetCaseFolder(CaseFolderForEncoding());
5843 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
5844 (wParam & SCFIND_MATCHCASE) != 0,
5845 (wParam & SCFIND_WHOLEWORD) != 0,
5846 (wParam & SCFIND_WORDSTART) != 0,
5847 (wParam & SCFIND_REGEXP) != 0,
5848 static_cast<int>(wParam),
5849 &lengthFound);
5850 if (pos != -1) {
5851 ft->chrgText.cpMin = pos;
5852 ft->chrgText.cpMax = pos + lengthFound;
5854 return pos;
5858 * Relocatable search support : Searches relative to current selection
5859 * point and sets the selection to the found text range with
5860 * each search.
5863 * Anchor following searches at current selection start: This allows
5864 * multiple incremental interactive searches to be macro recorded
5865 * while still setting the selection to found text so the find/select
5866 * operation is self-contained.
5868 void Editor::SearchAnchor() {
5869 searchAnchor = SelectionStart().Position();
5873 * Find text from current search anchor: Must call @c SearchAnchor first.
5874 * Used for next text and previous text requests.
5875 * @return The position of the found text, -1 if not found.
5877 long Editor::SearchText(
5878 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
5879 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5880 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5881 sptr_t lParam) { ///< The text to search for.
5883 const char *txt = reinterpret_cast<char *>(lParam);
5884 int pos;
5885 int lengthFound = istrlen(txt);
5886 if (!pdoc->HasCaseFolder())
5887 pdoc->SetCaseFolder(CaseFolderForEncoding());
5888 if (iMessage == SCI_SEARCHNEXT) {
5889 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
5890 (wParam & SCFIND_MATCHCASE) != 0,
5891 (wParam & SCFIND_WHOLEWORD) != 0,
5892 (wParam & SCFIND_WORDSTART) != 0,
5893 (wParam & SCFIND_REGEXP) != 0,
5894 static_cast<int>(wParam),
5895 &lengthFound);
5896 } else {
5897 pos = pdoc->FindText(searchAnchor, 0, txt,
5898 (wParam & SCFIND_MATCHCASE) != 0,
5899 (wParam & SCFIND_WHOLEWORD) != 0,
5900 (wParam & SCFIND_WORDSTART) != 0,
5901 (wParam & SCFIND_REGEXP) != 0,
5902 static_cast<int>(wParam),
5903 &lengthFound);
5905 if (pos != -1) {
5906 SetSelection(pos, pos + lengthFound);
5909 return pos;
5912 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
5913 std::string ret(s);
5914 for (size_t i=0; i<ret.size(); i++) {
5915 switch (caseMapping) {
5916 case cmUpper:
5917 if (ret[i] >= 'a' && ret[i] <= 'z')
5918 ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
5919 break;
5920 case cmLower:
5921 if (ret[i] >= 'A' && ret[i] <= 'Z')
5922 ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
5923 break;
5926 return ret;
5930 * Search for text in the target range of the document.
5931 * @return The position of the found text, -1 if not found.
5933 long Editor::SearchInTarget(const char *text, int length) {
5934 int lengthFound = length;
5936 if (!pdoc->HasCaseFolder())
5937 pdoc->SetCaseFolder(CaseFolderForEncoding());
5938 int pos = pdoc->FindText(targetStart, targetEnd, text,
5939 (searchFlags & SCFIND_MATCHCASE) != 0,
5940 (searchFlags & SCFIND_WHOLEWORD) != 0,
5941 (searchFlags & SCFIND_WORDSTART) != 0,
5942 (searchFlags & SCFIND_REGEXP) != 0,
5943 searchFlags,
5944 &lengthFound);
5945 if (pos != -1) {
5946 targetStart = pos;
5947 targetEnd = pos + lengthFound;
5949 return pos;
5952 void Editor::GoToLine(int lineNo) {
5953 if (lineNo > pdoc->LinesTotal())
5954 lineNo = pdoc->LinesTotal();
5955 if (lineNo < 0)
5956 lineNo = 0;
5957 SetEmptySelection(pdoc->LineStart(lineNo));
5958 ShowCaretAtCurrentPosition();
5959 EnsureCaretVisible();
5962 static bool Close(Point pt1, Point pt2) {
5963 if (abs(pt1.x - pt2.x) > 3)
5964 return false;
5965 if (abs(pt1.y - pt2.y) > 3)
5966 return false;
5967 return true;
5970 std::string Editor::RangeText(int start, int end) const {
5971 if (start < end) {
5972 int len = end - start;
5973 std::string ret(len, '\0');
5974 for (int i = 0; i < len; i++) {
5975 ret[i] = pdoc->CharAt(start + i);
5977 return ret;
5979 return std::string();
5982 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
5983 if (sel.Empty()) {
5984 if (allowLineCopy) {
5985 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
5986 int start = pdoc->LineStart(currentLine);
5987 int end = pdoc->LineEnd(currentLine);
5989 std::string text = RangeText(start, end);
5990 if (pdoc->eolMode != SC_EOL_LF)
5991 text.push_back('\r');
5992 if (pdoc->eolMode != SC_EOL_CR)
5993 text.push_back('\n');
5994 ss->Copy(text, pdoc->dbcsCodePage,
5995 vs.styles[STYLE_DEFAULT].characterSet, false, true);
5997 } else {
5998 std::string text;
5999 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
6000 if (sel.selType == Selection::selRectangle)
6001 std::sort(rangesInOrder.begin(), rangesInOrder.end());
6002 for (size_t r=0; r<rangesInOrder.size(); r++) {
6003 SelectionRange current = rangesInOrder[r];
6004 text.append(RangeText(current.Start().Position(), current.End().Position()));
6005 if (sel.selType == Selection::selRectangle) {
6006 if (pdoc->eolMode != SC_EOL_LF)
6007 text.push_back('\r');
6008 if (pdoc->eolMode != SC_EOL_CR)
6009 text.push_back('\n');
6012 ss->Copy(text, pdoc->dbcsCodePage,
6013 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
6017 void Editor::CopyRangeToClipboard(int start, int end) {
6018 start = pdoc->ClampPositionIntoDocument(start);
6019 end = pdoc->ClampPositionIntoDocument(end);
6020 SelectionText selectedText;
6021 std::string text = RangeText(start, end);
6022 selectedText.Copy(text,
6023 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
6024 CopyToClipboard(selectedText);
6027 void Editor::CopyText(int length, const char *text) {
6028 SelectionText selectedText;
6029 selectedText.Copy(std::string(text, length),
6030 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
6031 CopyToClipboard(selectedText);
6034 void Editor::SetDragPosition(SelectionPosition newPos) {
6035 if (newPos.Position() >= 0) {
6036 newPos = MovePositionOutsideChar(newPos, 1);
6037 posDrop = newPos;
6039 if (!(posDrag == newPos)) {
6040 caret.on = true;
6041 SetTicking(true);
6042 InvalidateCaret();
6043 posDrag = newPos;
6044 InvalidateCaret();
6048 void Editor::DisplayCursor(Window::Cursor c) {
6049 if (cursorMode == SC_CURSORNORMAL)
6050 wMain.SetCursor(c);
6051 else
6052 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
6055 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
6056 int xMove = static_cast<int>(ptStart.x - ptNow.x);
6057 int yMove = static_cast<int>(ptStart.y - ptNow.y);
6058 int distanceSquared = xMove * xMove + yMove * yMove;
6059 return distanceSquared > 16;
6062 void Editor::StartDrag() {
6063 // Always handled by subclasses
6064 //SetMouseCapture(true);
6065 //DisplayCursor(Window::cursorArrow);
6068 void Editor::DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular) {
6069 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
6070 if (inDragDrop == ddDragging)
6071 dropWentOutside = false;
6073 bool positionWasInSelection = PositionInSelection(position.Position());
6075 bool positionOnEdgeOfSelection =
6076 (position == SelectionStart()) || (position == SelectionEnd());
6078 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
6079 (positionOnEdgeOfSelection && !moving)) {
6081 SelectionPosition selStart = SelectionStart();
6082 SelectionPosition selEnd = SelectionEnd();
6084 UndoGroup ug(pdoc);
6086 SelectionPosition positionAfterDeletion = position;
6087 if ((inDragDrop == ddDragging) && moving) {
6088 // Remove dragged out text
6089 if (rectangular || sel.selType == Selection::selLines) {
6090 for (size_t r=0; r<sel.Count(); r++) {
6091 if (position >= sel.Range(r).Start()) {
6092 if (position > sel.Range(r).End()) {
6093 positionAfterDeletion.Add(-sel.Range(r).Length());
6094 } else {
6095 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
6099 } else {
6100 if (position > selStart) {
6101 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
6104 ClearSelection();
6106 position = positionAfterDeletion;
6108 std::string convertedText = Document::TransformLineEnds(value, lengthValue, pdoc->eolMode);
6110 if (rectangular) {
6111 PasteRectangular(position, convertedText.c_str(), static_cast<int>(convertedText.length()));
6112 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
6113 SetEmptySelection(position);
6114 } else {
6115 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
6116 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
6117 const int lengthInserted = pdoc->InsertString(
6118 position.Position(), convertedText.c_str(), static_cast<int>(convertedText.length()));
6119 if (lengthInserted > 0) {
6120 SelectionPosition posAfterInsertion = position;
6121 posAfterInsertion.Add(lengthInserted);
6122 SetSelection(posAfterInsertion, position);
6125 } else if (inDragDrop == ddDragging) {
6126 SetEmptySelection(position);
6130 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
6131 DropAt(position, value, strlen(value), moving, rectangular);
6135 * @return true if given position is inside the selection,
6137 bool Editor::PositionInSelection(int pos) {
6138 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
6139 for (size_t r=0; r<sel.Count(); r++) {
6140 if (sel.Range(r).Contains(pos))
6141 return true;
6143 return false;
6146 bool Editor::PointInSelection(Point pt) {
6147 SelectionPosition pos = SPositionFromLocation(pt, false, true);
6148 Point ptPos = LocationFromPosition(pos);
6149 for (size_t r=0; r<sel.Count(); r++) {
6150 SelectionRange range = sel.Range(r);
6151 if (range.Contains(pos)) {
6152 bool hit = true;
6153 if (pos == range.Start()) {
6154 // see if just before selection
6155 if (pt.x < ptPos.x) {
6156 hit = false;
6159 if (pos == range.End()) {
6160 // see if just after selection
6161 if (pt.x > ptPos.x) {
6162 hit = false;
6165 if (hit)
6166 return true;
6169 return false;
6172 bool Editor::PointInSelMargin(Point pt) {
6173 // Really means: "Point in a margin"
6174 if (vs.fixedColumnWidth > 0) { // There is a margin
6175 PRectangle rcSelMargin = GetClientRectangle();
6176 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart - vs.leftMarginWidth);
6177 rcSelMargin.left = static_cast<XYPOSITION>(vs.textStart - vs.fixedColumnWidth);
6178 return rcSelMargin.Contains(pt);
6179 } else {
6180 return false;
6184 Window::Cursor Editor::GetMarginCursor(Point pt) const {
6185 int x = 0;
6186 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
6187 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
6188 return static_cast<Window::Cursor>(vs.ms[margin].cursor);
6189 x += vs.ms[margin].width;
6191 return Window::cursorReverseArrow;
6194 void Editor::TrimAndSetSelection(int currentPos_, int anchor_) {
6195 sel.TrimSelection(SelectionRange(currentPos_, anchor_));
6196 SetSelection(currentPos_, anchor_);
6199 void Editor::LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine) {
6200 int selCurrentPos, selAnchorPos;
6201 if (wholeLine) {
6202 int lineCurrent_ = pdoc->LineFromPosition(lineCurrentPos_);
6203 int lineAnchor_ = pdoc->LineFromPosition(lineAnchorPos_);
6204 if (lineAnchorPos_ < lineCurrentPos_) {
6205 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
6206 selAnchorPos = pdoc->LineStart(lineAnchor_);
6207 } else if (lineAnchorPos_ > lineCurrentPos_) {
6208 selCurrentPos = pdoc->LineStart(lineCurrent_);
6209 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
6210 } else { // Same line, select it
6211 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
6212 selAnchorPos = pdoc->LineStart(lineAnchor_);
6214 } else {
6215 if (lineAnchorPos_ < lineCurrentPos_) {
6216 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
6217 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6218 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6219 } else if (lineAnchorPos_ > lineCurrentPos_) {
6220 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
6221 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6222 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
6223 } else { // Same line, select it
6224 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6225 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6226 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6229 TrimAndSetSelection(selCurrentPos, selAnchorPos);
6232 void Editor::WordSelection(int pos) {
6233 if (pos < wordSelectAnchorStartPos) {
6234 // Extend backward to the word containing pos.
6235 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
6236 // This ensures that a series of empty lines isn't counted as a single "word".
6237 if (!pdoc->IsLineEndPosition(pos))
6238 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
6239 TrimAndSetSelection(pos, wordSelectAnchorEndPos);
6240 } else if (pos > wordSelectAnchorEndPos) {
6241 // Extend forward to the word containing the character to the left of pos.
6242 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
6243 // This ensures that a series of empty lines isn't counted as a single "word".
6244 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
6245 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
6246 TrimAndSetSelection(pos, wordSelectAnchorStartPos);
6247 } else {
6248 // Select only the anchored word
6249 if (pos >= originalAnchorPos)
6250 TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
6251 else
6252 TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
6256 void Editor::DwellEnd(bool mouseMoved) {
6257 if (mouseMoved)
6258 ticksToDwell = dwellDelay;
6259 else
6260 ticksToDwell = SC_TIME_FOREVER;
6261 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
6262 dwelling = false;
6263 NotifyDwelling(ptMouseLast, dwelling);
6267 void Editor::MouseLeave() {
6268 SetHotSpotRange(NULL);
6269 if (!HaveMouseCapture()) {
6270 ptMouseLast = Point(-1,-1);
6271 DwellEnd(true);
6275 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
6276 return (!rectangular && ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0))
6277 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
6280 void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) {
6281 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
6282 ptMouseLast = pt;
6283 const bool ctrl = (modifiers & SCI_CTRL) != 0;
6284 const bool shift = (modifiers & SCI_SHIFT) != 0;
6285 const bool alt = (modifiers & SCI_ALT) != 0;
6286 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
6287 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6288 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
6289 newCharPos = MovePositionOutsideChar(newCharPos, -1);
6290 inDragDrop = ddNone;
6291 sel.SetMoveExtends(false);
6293 if (NotifyMarginClick(pt, modifiers))
6294 return;
6296 NotifyIndicatorClick(true, newPos.Position(), modifiers);
6298 bool inSelMargin = PointInSelMargin(pt);
6299 // In margin ctrl+(double)click should always select everything
6300 if (ctrl && inSelMargin) {
6301 SelectAll();
6302 lastClickTime = curTime;
6303 lastClick = pt;
6304 return;
6306 if (shift && !inSelMargin) {
6307 SetSelection(newPos);
6309 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
6310 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
6311 SetMouseCapture(true);
6312 if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))
6313 SetEmptySelection(newPos.Position());
6314 bool doubleClick = false;
6315 // Stop mouse button bounce changing selection type
6316 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
6317 if (inSelMargin) {
6318 // Inside margin selection type should be either selSubLine or selWholeLine.
6319 if (selectionType == selSubLine) {
6320 // If it is selSubLine, we're inside a *double* click and word wrap is enabled,
6321 // so we switch to selWholeLine in order to select whole line.
6322 selectionType = selWholeLine;
6323 } else if (selectionType != selSubLine && selectionType != selWholeLine) {
6324 // If it is neither, reset selection type to line selection.
6325 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6327 } else {
6328 if (selectionType == selChar) {
6329 selectionType = selWord;
6330 doubleClick = true;
6331 } else if (selectionType == selWord) {
6332 // Since we ended up here, we're inside a *triple* click, which should always select
6333 // whole line regardless of word wrap being enabled or not.
6334 selectionType = selWholeLine;
6335 } else {
6336 selectionType = selChar;
6337 originalAnchorPos = sel.MainCaret();
6342 if (selectionType == selWord) {
6343 int charPos = originalAnchorPos;
6344 if (sel.MainCaret() == originalAnchorPos) {
6345 charPos = PositionFromLocation(pt, false, true);
6346 charPos = MovePositionOutsideChar(charPos, -1);
6349 int startWord, endWord;
6350 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
6351 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
6352 endWord = pdoc->ExtendWordSelect(charPos, 1);
6353 } else {
6354 // Selecting backwards, or anchor beyond last character on line. In these cases,
6355 // we select the word containing the character to the *left* of the anchor.
6356 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
6357 startWord = pdoc->ExtendWordSelect(charPos, -1);
6358 endWord = pdoc->ExtendWordSelect(startWord, 1);
6359 } else {
6360 // Anchor at start of line; select nothing to begin with.
6361 startWord = charPos;
6362 endWord = charPos;
6366 wordSelectAnchorStartPos = startWord;
6367 wordSelectAnchorEndPos = endWord;
6368 wordSelectInitialCaretPos = sel.MainCaret();
6369 WordSelection(wordSelectInitialCaretPos);
6370 } else if (selectionType == selSubLine || selectionType == selWholeLine) {
6371 lineAnchorPos = newPos.Position();
6372 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6373 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
6374 } else {
6375 SetEmptySelection(sel.MainCaret());
6377 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
6378 if (doubleClick) {
6379 NotifyDoubleClick(pt, modifiers);
6380 if (PositionIsHotspot(newCharPos.Position()))
6381 NotifyHotSpotDoubleClicked(newCharPos.Position(), modifiers);
6383 } else { // Single click
6384 if (inSelMargin) {
6385 sel.selType = Selection::selStream;
6386 if (!shift) {
6387 // Single click in margin: select whole line or only subline if word wrap is enabled
6388 lineAnchorPos = newPos.Position();
6389 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6390 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6391 } else {
6392 // Single shift+click in margin: select from line anchor to clicked line
6393 if (sel.MainAnchor() > sel.MainCaret())
6394 lineAnchorPos = sel.MainAnchor() - 1;
6395 else
6396 lineAnchorPos = sel.MainAnchor();
6397 // Reset selection type if there is an empty selection.
6398 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
6399 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
6400 // This ensures that we continue selecting in the same selection mode.
6401 if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
6402 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6403 LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
6406 SetDragPosition(SelectionPosition(invalidPosition));
6407 SetMouseCapture(true);
6408 } else {
6409 if (PointIsHotspot(pt)) {
6410 NotifyHotSpotClicked(newCharPos.Position(), modifiers);
6411 hotSpotClickPos = newCharPos.Position();
6413 if (!shift) {
6414 if (PointInSelection(pt) && !SelectionEmpty())
6415 inDragDrop = ddInitial;
6416 else
6417 inDragDrop = ddNone;
6419 SetMouseCapture(true);
6420 if (inDragDrop != ddInitial) {
6421 SetDragPosition(SelectionPosition(invalidPosition));
6422 if (!shift) {
6423 if (ctrl && multipleSelection) {
6424 SelectionRange range(newPos);
6425 sel.TentativeSelection(range);
6426 InvalidateSelection(range, true);
6427 } else {
6428 InvalidateSelection(SelectionRange(newPos), true);
6429 if (sel.Count() > 1)
6430 Redraw();
6431 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
6432 sel.Clear();
6433 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6434 SetSelection(newPos, newPos);
6437 SelectionPosition anchorCurrent = newPos;
6438 if (shift)
6439 anchorCurrent = sel.IsRectangular() ?
6440 sel.Rectangular().anchor : sel.RangeMain().anchor;
6441 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6442 selectionType = selChar;
6443 originalAnchorPos = sel.MainCaret();
6444 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
6445 SetRectangularRange();
6449 lastClickTime = curTime;
6450 lastClick = pt;
6451 lastXChosen = static_cast<int>(pt.x) + xOffset;
6452 ShowCaretAtCurrentPosition();
6455 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
6456 return ButtonDownWithModifiers(pt, curTime, ModifierFlags(shift, ctrl, alt));
6459 bool Editor::PositionIsHotspot(int position) const {
6460 return vs.styles[pdoc->StyleAt(position)].hotspot;
6463 bool Editor::PointIsHotspot(Point pt) {
6464 int pos = PositionFromLocation(pt, true, true);
6465 if (pos == INVALID_POSITION)
6466 return false;
6467 return PositionIsHotspot(pos);
6470 void Editor::SetHotSpotRange(Point *pt) {
6471 if (pt) {
6472 int pos = PositionFromLocation(*pt, false, true);
6474 // If we don't limit this to word characters then the
6475 // range can encompass more than the run range and then
6476 // the underline will not be drawn properly.
6477 Range hsNew;
6478 hsNew.start = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
6479 hsNew.end = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
6481 // Only invalidate the range if the hotspot range has changed...
6482 if (!(hsNew == hotspot)) {
6483 if (hotspot.Valid()) {
6484 InvalidateRange(hotspot.start, hotspot.end);
6486 hotspot = hsNew;
6487 InvalidateRange(hotspot.start, hotspot.end);
6489 } else {
6490 if (hotspot.Valid()) {
6491 InvalidateRange(hotspot.start, hotspot.end);
6493 hotspot = Range(invalidPosition);
6497 Range Editor::GetHotSpotRange() const {
6498 return hotspot;
6501 void Editor::ButtonMoveWithModifiers(Point pt, int modifiers) {
6502 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
6503 DwellEnd(true);
6506 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
6507 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6508 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
6510 if (inDragDrop == ddInitial) {
6511 if (DragThreshold(ptMouseLast, pt)) {
6512 SetMouseCapture(false);
6513 SetDragPosition(movePos);
6514 CopySelectionRange(&drag);
6515 StartDrag();
6517 return;
6520 ptMouseLast = pt;
6521 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
6522 if (HaveMouseCapture()) {
6524 // Slow down autoscrolling/selection
6525 autoScrollTimer.ticksToWait -= timer.tickSize;
6526 if (autoScrollTimer.ticksToWait > 0)
6527 return;
6528 autoScrollTimer.ticksToWait = autoScrollDelay;
6530 // Adjust selection
6531 if (posDrag.IsValid()) {
6532 SetDragPosition(movePos);
6533 } else {
6534 if (selectionType == selChar) {
6535 if (sel.selType == Selection::selStream && (modifiers & SCI_ALT) && mouseSelectionRectangularSwitch) {
6536 sel.selType = Selection::selRectangle;
6538 if (sel.IsRectangular()) {
6539 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
6540 SetSelection(movePos, sel.RangeMain().anchor);
6541 } else if (sel.Count() > 1) {
6542 InvalidateSelection(sel.RangeMain(), false);
6543 SelectionRange range(movePos, sel.RangeMain().anchor);
6544 sel.TentativeSelection(range);
6545 InvalidateSelection(range, true);
6546 } else {
6547 SetSelection(movePos, sel.RangeMain().anchor);
6549 } else if (selectionType == selWord) {
6550 // Continue selecting by word
6551 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
6552 // No need to do anything. Previously this case was lumped
6553 // in with "Moved forward", but that can be harmful in this
6554 // case: a handler for the NotifyDoubleClick re-adjusts
6555 // the selection for a fancier definition of "word" (for
6556 // example, in Perl it is useful to include the leading
6557 // '$', '%' or '@' on variables for word selection). In this
6558 // the ButtonMove() called via Tick() for auto-scrolling
6559 // could result in the fancier word selection adjustment
6560 // being unmade.
6561 } else {
6562 wordSelectInitialCaretPos = -1;
6563 WordSelection(movePos.Position());
6565 } else {
6566 // Continue selecting by line
6567 LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
6571 // Autoscroll
6572 PRectangle rcClient = GetClientRectangle();
6573 Point ptOrigin = GetVisibleOriginInMain();
6574 rcClient.Move(0, -ptOrigin.y);
6575 int lineMove = DisplayFromPosition(movePos.Position());
6576 if (pt.y > rcClient.bottom) {
6577 ScrollTo(lineMove - LinesOnScreen() + 1);
6578 Redraw();
6579 } else if (pt.y < rcClient.top) {
6580 ScrollTo(lineMove);
6581 Redraw();
6583 EnsureCaretVisible(false, false, true);
6585 if (hotspot.Valid() && !PointIsHotspot(pt))
6586 SetHotSpotRange(NULL);
6588 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,true) != hotSpotClickPos) {
6589 if (inDragDrop == ddNone) {
6590 DisplayCursor(Window::cursorText);
6592 hotSpotClickPos = INVALID_POSITION;
6595 } else {
6596 if (vs.fixedColumnWidth > 0) { // There is a margin
6597 if (PointInSelMargin(pt)) {
6598 DisplayCursor(GetMarginCursor(pt));
6599 SetHotSpotRange(NULL);
6600 return; // No need to test for selection
6603 // Display regular (drag) cursor over selection
6604 if (PointInSelection(pt) && !SelectionEmpty()) {
6605 DisplayCursor(Window::cursorArrow);
6606 } else if (PointIsHotspot(pt)) {
6607 DisplayCursor(Window::cursorHand);
6608 SetHotSpotRange(&pt);
6609 } else {
6610 DisplayCursor(Window::cursorText);
6611 SetHotSpotRange(NULL);
6616 void Editor::ButtonMove(Point pt) {
6617 ButtonMoveWithModifiers(pt, 0);
6620 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
6621 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
6622 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
6623 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6624 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6625 if (inDragDrop == ddInitial) {
6626 inDragDrop = ddNone;
6627 SetEmptySelection(newPos);
6628 selectionType = selChar;
6629 originalAnchorPos = sel.MainCaret();
6631 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
6632 hotSpotClickPos = INVALID_POSITION;
6633 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
6634 newCharPos = MovePositionOutsideChar(newCharPos, -1);
6635 NotifyHotSpotReleaseClick(newCharPos.Position(), ctrl ? SCI_CTRL : 0);
6637 if (HaveMouseCapture()) {
6638 if (PointInSelMargin(pt)) {
6639 DisplayCursor(GetMarginCursor(pt));
6640 } else {
6641 DisplayCursor(Window::cursorText);
6642 SetHotSpotRange(NULL);
6644 ptMouseLast = pt;
6645 SetMouseCapture(false);
6646 NotifyIndicatorClick(false, newPos.Position(), 0);
6647 if (inDragDrop == ddDragging) {
6648 SelectionPosition selStart = SelectionStart();
6649 SelectionPosition selEnd = SelectionEnd();
6650 if (selStart < selEnd) {
6651 if (drag.Length()) {
6652 const int length = static_cast<int>(drag.Length());
6653 if (ctrl) {
6654 const int lengthInserted = pdoc->InsertString(
6655 newPos.Position(), drag.Data(), length);
6656 if (lengthInserted > 0) {
6657 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
6659 } else if (newPos < selStart) {
6660 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
6661 const int lengthInserted = pdoc->InsertString(
6662 newPos.Position(), drag.Data(), length);
6663 if (lengthInserted > 0) {
6664 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
6666 } else if (newPos > selEnd) {
6667 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
6668 newPos.Add(-static_cast<int>(drag.Length()));
6669 const int lengthInserted = pdoc->InsertString(
6670 newPos.Position(), drag.Data(), length);
6671 if (lengthInserted > 0) {
6672 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
6674 } else {
6675 SetEmptySelection(newPos.Position());
6677 drag.Clear();
6679 selectionType = selChar;
6681 } else {
6682 if (selectionType == selChar) {
6683 if (sel.Count() > 1) {
6684 sel.RangeMain() =
6685 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
6686 InvalidateSelection(sel.RangeMain(), true);
6687 } else {
6688 SetSelection(newPos, sel.RangeMain().anchor);
6691 sel.CommitTentative();
6693 SetRectangularRange();
6694 lastClickTime = curTime;
6695 lastClick = pt;
6696 lastXChosen = static_cast<int>(pt.x) + xOffset;
6697 if (sel.selType == Selection::selStream) {
6698 SetLastXChosen();
6700 inDragDrop = ddNone;
6701 EnsureCaretVisible(false);
6705 // Called frequently to perform background UI including
6706 // caret blinking and automatic scrolling.
6707 void Editor::Tick() {
6708 if (HaveMouseCapture()) {
6709 // Auto scroll
6710 ButtonMove(ptMouseLast);
6712 if (caret.period > 0) {
6713 timer.ticksToWait -= timer.tickSize;
6714 if (timer.ticksToWait <= 0) {
6715 caret.on = !caret.on;
6716 timer.ticksToWait = caret.period;
6717 if (caret.active) {
6718 InvalidateCaret();
6722 if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) {
6723 scrollWidth = lineWidthMaxSeen;
6724 SetScrollBars();
6726 if ((dwellDelay < SC_TIME_FOREVER) &&
6727 (ticksToDwell > 0) &&
6728 (!HaveMouseCapture()) &&
6729 (ptMouseLast.y >= 0)) {
6730 ticksToDwell -= timer.tickSize;
6731 if (ticksToDwell <= 0) {
6732 dwelling = true;
6733 NotifyDwelling(ptMouseLast, dwelling);
6738 bool Editor::Idle() {
6740 bool idleDone;
6742 bool wrappingDone = !Wrapping();
6744 if (!wrappingDone) {
6745 // Wrap lines during idle.
6746 WrapLines(wsIdle);
6747 // No more wrapping
6748 if (!wrapPending.NeedsWrap())
6749 wrappingDone = true;
6752 // Add more idle things to do here, but make sure idleDone is
6753 // set correctly before the function returns. returning
6754 // false will stop calling this idle function until SetIdle() is
6755 // called again.
6757 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
6759 return !idleDone;
6762 void Editor::SetFocusState(bool focusState) {
6763 hasFocus = focusState;
6764 NotifyFocus(hasFocus);
6765 if (hasFocus) {
6766 ShowCaretAtCurrentPosition();
6767 } else {
6768 CancelModes();
6769 DropCaret();
6773 int Editor::PositionAfterArea(PRectangle rcArea) const {
6774 // The start of the document line after the display line after the area
6775 // This often means that the line after a modification is restyled which helps
6776 // detect multiline comment additions and heals single line comments
6777 int lineAfter = TopLineOfMain() + static_cast<int>(rcArea.bottom - 1) / vs.lineHeight + 1;
6778 if (lineAfter < cs.LinesDisplayed())
6779 return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
6780 else
6781 return pdoc->Length();
6784 // Style to a position within the view. If this causes a change at end of last line then
6785 // affects later lines so style all the viewed text.
6786 void Editor::StyleToPositionInView(Position pos) {
6787 int endWindow = PositionAfterArea(GetClientDrawingRectangle());
6788 if (pos > endWindow)
6789 pos = endWindow;
6790 int styleAtEnd = pdoc->StyleAt(pos-1);
6791 pdoc->EnsureStyledTo(pos);
6792 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleAt(pos-1))) {
6793 // Style at end of line changed so is multi-line change like starting a comment
6794 // so require rest of window to be styled.
6795 DiscardOverdraw(); // Prepared bitmaps may be invalid
6796 // DiscardOverdraw may have truncated client drawing area so recalculate endWindow
6797 endWindow = PositionAfterArea(GetClientDrawingRectangle());
6798 pdoc->EnsureStyledTo(endWindow);
6802 void Editor::IdleWork() {
6803 // Style the line after the modification as this allows modifications that change just the
6804 // line of the modification to heal instead of propagating to the rest of the window.
6805 if (workNeeded.items & WorkNeeded::workStyle)
6806 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2));
6808 NotifyUpdateUI();
6809 workNeeded.Reset();
6812 void Editor::QueueIdleWork(WorkNeeded::workItems items, int upTo) {
6813 workNeeded.Need(items, upTo);
6816 bool Editor::PaintContains(PRectangle rc) {
6817 if (rc.Empty()) {
6818 return true;
6819 } else {
6820 return rcPaint.Contains(rc);
6824 bool Editor::PaintContainsMargin() {
6825 if (wMargin.GetID()) {
6826 // With separate margin view, paint of text view
6827 // never contains margin.
6828 return false;
6830 PRectangle rcSelMargin = GetClientRectangle();
6831 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart);
6832 return PaintContains(rcSelMargin);
6835 void Editor::CheckForChangeOutsidePaint(Range r) {
6836 if (paintState == painting && !paintingAllText) {
6837 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
6838 if (!r.Valid())
6839 return;
6841 PRectangle rcRange = RectangleFromRange(r);
6842 PRectangle rcText = GetTextRectangle();
6843 if (rcRange.top < rcText.top) {
6844 rcRange.top = rcText.top;
6846 if (rcRange.bottom > rcText.bottom) {
6847 rcRange.bottom = rcText.bottom;
6850 if (!PaintContains(rcRange)) {
6851 AbandonPaint();
6852 paintAbandonedByStyling = true;
6857 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
6858 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
6859 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
6860 CheckForChangeOutsidePaint(Range(braces[0]));
6861 CheckForChangeOutsidePaint(Range(pos0));
6862 braces[0] = pos0;
6864 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
6865 CheckForChangeOutsidePaint(Range(braces[1]));
6866 CheckForChangeOutsidePaint(Range(pos1));
6867 braces[1] = pos1;
6869 bracesMatchStyle = matchStyle;
6870 if (paintState == notPainting) {
6871 Redraw();
6876 void Editor::SetAnnotationHeights(int start, int end) {
6877 if (vs.annotationVisible) {
6878 bool changedHeight = false;
6879 for (int line=start; line<end && line<pdoc->LinesTotal(); line++) {
6880 int linesWrapped = 1;
6881 if (Wrapping()) {
6882 AutoSurface surface(this);
6883 AutoLineLayout ll(llc, RetrieveLineLayout(line));
6884 if (surface && ll) {
6885 LayoutLine(line, surface, vs, ll, wrapWidth);
6886 linesWrapped = ll->lines;
6889 if (cs.SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
6890 changedHeight = true;
6892 if (changedHeight) {
6893 Redraw();
6898 void Editor::SetDocPointer(Document *document) {
6899 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
6900 pdoc->RemoveWatcher(this, 0);
6901 pdoc->Release();
6902 if (document == NULL) {
6903 pdoc = new Document();
6904 } else {
6905 pdoc = document;
6907 pdoc->AddRef();
6909 // Ensure all positions within document
6910 sel.Clear();
6911 targetStart = 0;
6912 targetEnd = 0;
6914 braces[0] = invalidPosition;
6915 braces[1] = invalidPosition;
6917 vs.ReleaseAllExtendedStyles();
6919 SetRepresentations();
6921 // Reset the contraction state to fully shown.
6922 cs.Clear();
6923 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6924 SetAnnotationHeights(0, pdoc->LinesTotal());
6925 llc.Deallocate();
6926 NeedWrapping();
6928 pdoc->AddWatcher(this, 0);
6929 SetScrollBars();
6930 Redraw();
6933 void Editor::SetAnnotationVisible(int visible) {
6934 if (vs.annotationVisible != visible) {
6935 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
6936 vs.annotationVisible = visible;
6937 if (changedFromOrToHidden) {
6938 int dir = vs.annotationVisible ? 1 : -1;
6939 for (int line=0; line<pdoc->LinesTotal(); line++) {
6940 int annotationLines = pdoc->AnnotationLines(line);
6941 if (annotationLines > 0) {
6942 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
6946 Redraw();
6951 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
6953 int Editor::ExpandLine(int line) {
6954 int lineMaxSubord = pdoc->GetLastChild(line);
6955 line++;
6956 while (line <= lineMaxSubord) {
6957 cs.SetVisible(line, line, true);
6958 int level = pdoc->GetLevel(line);
6959 if (level & SC_FOLDLEVELHEADERFLAG) {
6960 if (cs.GetExpanded(line)) {
6961 line = ExpandLine(line);
6962 } else {
6963 line = pdoc->GetLastChild(line);
6966 line++;
6968 return lineMaxSubord;
6971 void Editor::SetFoldExpanded(int lineDoc, bool expanded) {
6972 if (cs.SetExpanded(lineDoc, expanded)) {
6973 RedrawSelMargin();
6977 void Editor::FoldLine(int line, int action) {
6978 if (line >= 0) {
6979 if (action == SC_FOLDACTION_TOGGLE) {
6980 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
6981 line = pdoc->GetFoldParent(line);
6982 if (line < 0)
6983 return;
6985 action = (cs.GetExpanded(line)) ? SC_FOLDACTION_CONTRACT : SC_FOLDACTION_EXPAND;
6988 if (action == SC_FOLDACTION_CONTRACT) {
6989 int lineMaxSubord = pdoc->GetLastChild(line);
6990 if (lineMaxSubord > line) {
6991 cs.SetExpanded(line, 0);
6992 cs.SetVisible(line + 1, lineMaxSubord, false);
6994 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
6995 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
6996 // This does not re-expand the fold
6997 EnsureCaretVisible();
7001 } else {
7002 if (!(cs.GetVisible(line))) {
7003 EnsureLineVisible(line, false);
7004 GoToLine(line);
7006 cs.SetExpanded(line, 1);
7007 ExpandLine(line);
7010 SetScrollBars();
7011 Redraw();
7015 void Editor::FoldExpand(int line, int action, int level) {
7016 bool expanding = action == SC_FOLDACTION_EXPAND;
7017 if (action == SC_FOLDACTION_TOGGLE) {
7018 expanding = !cs.GetExpanded(line);
7020 SetFoldExpanded(line, expanding);
7021 if (expanding && (cs.HiddenLines() == 0))
7022 // Nothing to do
7023 return;
7024 int lineMaxSubord = pdoc->GetLastChild(line, level & SC_FOLDLEVELNUMBERMASK);
7025 line++;
7026 cs.SetVisible(line, lineMaxSubord, expanding);
7027 while (line <= lineMaxSubord) {
7028 int levelLine = pdoc->GetLevel(line);
7029 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
7030 SetFoldExpanded(line, expanding);
7032 line++;
7034 SetScrollBars();
7035 Redraw();
7038 int Editor::ContractedFoldNext(int lineStart) const {
7039 for (int line = lineStart; line<pdoc->LinesTotal();) {
7040 if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
7041 return line;
7042 line = cs.ContractedNext(line+1);
7043 if (line < 0)
7044 return -1;
7047 return -1;
7051 * Recurse up from this line to find any folds that prevent this line from being visible
7052 * and unfold them all.
7054 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
7056 // In case in need of wrapping to ensure DisplayFromDoc works.
7057 if (lineDoc >= wrapPending.start)
7058 WrapLines(wsAll);
7060 if (!cs.GetVisible(lineDoc)) {
7061 // Back up to find a non-blank line
7062 int lookLine = lineDoc;
7063 int lookLineLevel = pdoc->GetLevel(lookLine);
7064 while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
7065 lookLineLevel = pdoc->GetLevel(--lookLine);
7067 int lineParent = pdoc->GetFoldParent(lookLine);
7068 if (lineParent < 0) {
7069 // Backed up to a top level line, so try to find parent of initial line
7070 lineParent = pdoc->GetFoldParent(lineDoc);
7072 if (lineParent >= 0) {
7073 if (lineDoc != lineParent)
7074 EnsureLineVisible(lineParent, enforcePolicy);
7075 if (!cs.GetExpanded(lineParent)) {
7076 cs.SetExpanded(lineParent, 1);
7077 ExpandLine(lineParent);
7080 SetScrollBars();
7081 Redraw();
7083 if (enforcePolicy) {
7084 int lineDisplay = cs.DisplayFromDoc(lineDoc);
7085 if (visiblePolicy & VISIBLE_SLOP) {
7086 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
7087 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
7088 SetVerticalScrollPos();
7089 Redraw();
7090 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
7091 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
7092 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
7093 SetVerticalScrollPos();
7094 Redraw();
7096 } else {
7097 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
7098 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
7099 SetVerticalScrollPos();
7100 Redraw();
7106 void Editor::FoldAll(int action) {
7107 pdoc->EnsureStyledTo(pdoc->Length());
7108 int maxLine = pdoc->LinesTotal();
7109 bool expanding = action == SC_FOLDACTION_EXPAND;
7110 if (action == SC_FOLDACTION_TOGGLE) {
7111 // Discover current state
7112 for (int lineSeek = 0; lineSeek < maxLine; lineSeek++) {
7113 if (pdoc->GetLevel(lineSeek) & SC_FOLDLEVELHEADERFLAG) {
7114 expanding = !cs.GetExpanded(lineSeek);
7115 break;
7119 if (expanding) {
7120 cs.SetVisible(0, maxLine-1, true);
7121 for (int line = 0; line < maxLine; line++) {
7122 int levelLine = pdoc->GetLevel(line);
7123 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
7124 SetFoldExpanded(line, true);
7127 } else {
7128 for (int line = 0; line < maxLine; line++) {
7129 int level = pdoc->GetLevel(line);
7130 if ((level & SC_FOLDLEVELHEADERFLAG) &&
7131 (SC_FOLDLEVELBASE == (level & SC_FOLDLEVELNUMBERMASK))) {
7132 SetFoldExpanded(line, false);
7133 int lineMaxSubord = pdoc->GetLastChild(line, -1);
7134 if (lineMaxSubord > line) {
7135 cs.SetVisible(line + 1, lineMaxSubord, false);
7140 SetScrollBars();
7141 Redraw();
7144 void Editor::FoldChanged(int line, int levelNow, int levelPrev) {
7145 if (levelNow & SC_FOLDLEVELHEADERFLAG) {
7146 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
7147 // Adding a fold point.
7148 if (cs.SetExpanded(line, true)) {
7149 RedrawSelMargin();
7151 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
7153 } else if (levelPrev & SC_FOLDLEVELHEADERFLAG) {
7154 if (!cs.GetExpanded(line)) {
7155 // Removing the fold from one that has been contracted so should expand
7156 // otherwise lines are left invisible with no way to make them visible
7157 if (cs.SetExpanded(line, true)) {
7158 RedrawSelMargin();
7160 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
7163 if (!(levelNow & SC_FOLDLEVELWHITEFLAG) &&
7164 ((levelPrev & SC_FOLDLEVELNUMBERMASK) > (levelNow & SC_FOLDLEVELNUMBERMASK))) {
7165 if (cs.HiddenLines()) {
7166 // See if should still be hidden
7167 int parentLine = pdoc->GetFoldParent(line);
7168 if ((parentLine < 0) || (cs.GetExpanded(parentLine) && cs.GetVisible(parentLine))) {
7169 cs.SetVisible(line, line, true);
7170 SetScrollBars();
7171 Redraw();
7177 void Editor::NeedShown(int pos, int len) {
7178 if (foldAutomatic & SC_AUTOMATICFOLD_SHOW) {
7179 int lineStart = pdoc->LineFromPosition(pos);
7180 int lineEnd = pdoc->LineFromPosition(pos+len);
7181 for (int line = lineStart; line <= lineEnd; line++) {
7182 EnsureLineVisible(line, false);
7184 } else {
7185 NotifyNeedShown(pos, len);
7189 int Editor::GetTag(char *tagValue, int tagNumber) {
7190 const char *text = 0;
7191 int length = 0;
7192 if ((tagNumber >= 1) && (tagNumber <= 9)) {
7193 char name[3] = "\\?";
7194 name[1] = static_cast<char>(tagNumber + '0');
7195 length = 2;
7196 text = pdoc->SubstituteByPosition(name, &length);
7198 if (tagValue) {
7199 if (text)
7200 memcpy(tagValue, text, length + 1);
7201 else
7202 *tagValue = '\0';
7204 return length;
7207 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
7208 UndoGroup ug(pdoc);
7209 if (length == -1)
7210 length = istrlen(text);
7211 if (replacePatterns) {
7212 text = pdoc->SubstituteByPosition(text, &length);
7213 if (!text) {
7214 return 0;
7217 if (targetStart != targetEnd)
7218 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
7219 targetEnd = targetStart;
7220 const int lengthInserted = pdoc->InsertString(targetStart, text, length);
7221 targetEnd = targetStart + lengthInserted;
7222 return length;
7225 bool Editor::IsUnicodeMode() const {
7226 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
7229 int Editor::CodePage() const {
7230 if (pdoc)
7231 return pdoc->dbcsCodePage;
7232 else
7233 return 0;
7236 int Editor::WrapCount(int line) {
7237 AutoSurface surface(this);
7238 AutoLineLayout ll(llc, RetrieveLineLayout(line));
7240 if (surface && ll) {
7241 LayoutLine(line, surface, vs, ll, wrapWidth);
7242 return ll->lines;
7243 } else {
7244 return 1;
7248 void Editor::AddStyledText(char *buffer, int appendLength) {
7249 // The buffer consists of alternating character bytes and style bytes
7250 int textLength = appendLength / 2;
7251 std::string text(textLength, '\0');
7252 int i;
7253 for (i = 0; i < textLength; i++) {
7254 text[i] = buffer[i*2];
7256 const int lengthInserted = pdoc->InsertString(CurrentPosition(), text.c_str(), textLength);
7257 for (i = 0; i < textLength; i++) {
7258 text[i] = buffer[i*2+1];
7260 pdoc->StartStyling(CurrentPosition(), static_cast<unsigned char>(0xff));
7261 pdoc->SetStyles(textLength, text.c_str());
7262 SetEmptySelection(sel.MainCaret() + lengthInserted);
7265 static bool ValidMargin(uptr_t wParam) {
7266 return wParam <= SC_MAX_MARGIN;
7269 static char *CharPtrFromSPtr(sptr_t lParam) {
7270 return reinterpret_cast<char *>(lParam);
7273 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7274 vs.EnsureStyle(wParam);
7275 switch (iMessage) {
7276 case SCI_STYLESETFORE:
7277 vs.styles[wParam].fore = ColourDesired(static_cast<long>(lParam));
7278 break;
7279 case SCI_STYLESETBACK:
7280 vs.styles[wParam].back = ColourDesired(static_cast<long>(lParam));
7281 break;
7282 case SCI_STYLESETBOLD:
7283 vs.styles[wParam].weight = lParam != 0 ? SC_WEIGHT_BOLD : SC_WEIGHT_NORMAL;
7284 break;
7285 case SCI_STYLESETWEIGHT:
7286 vs.styles[wParam].weight = static_cast<int>(lParam);
7287 break;
7288 case SCI_STYLESETITALIC:
7289 vs.styles[wParam].italic = lParam != 0;
7290 break;
7291 case SCI_STYLESETEOLFILLED:
7292 vs.styles[wParam].eolFilled = lParam != 0;
7293 break;
7294 case SCI_STYLESETSIZE:
7295 vs.styles[wParam].size = static_cast<int>(lParam * SC_FONT_SIZE_MULTIPLIER);
7296 break;
7297 case SCI_STYLESETSIZEFRACTIONAL:
7298 vs.styles[wParam].size = static_cast<int>(lParam);
7299 break;
7300 case SCI_STYLESETFONT:
7301 if (lParam != 0) {
7302 vs.SetStyleFontName(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7304 break;
7305 case SCI_STYLESETUNDERLINE:
7306 vs.styles[wParam].underline = lParam != 0;
7307 break;
7308 case SCI_STYLESETCASE:
7309 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
7310 break;
7311 case SCI_STYLESETCHARACTERSET:
7312 vs.styles[wParam].characterSet = static_cast<int>(lParam);
7313 pdoc->SetCaseFolder(NULL);
7314 break;
7315 case SCI_STYLESETVISIBLE:
7316 vs.styles[wParam].visible = lParam != 0;
7317 break;
7318 case SCI_STYLESETCHANGEABLE:
7319 vs.styles[wParam].changeable = lParam != 0;
7320 break;
7321 case SCI_STYLESETHOTSPOT:
7322 vs.styles[wParam].hotspot = lParam != 0;
7323 break;
7325 InvalidateStyleRedraw();
7328 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7329 vs.EnsureStyle(wParam);
7330 switch (iMessage) {
7331 case SCI_STYLEGETFORE:
7332 return vs.styles[wParam].fore.AsLong();
7333 case SCI_STYLEGETBACK:
7334 return vs.styles[wParam].back.AsLong();
7335 case SCI_STYLEGETBOLD:
7336 return vs.styles[wParam].weight > SC_WEIGHT_NORMAL;
7337 case SCI_STYLEGETWEIGHT:
7338 return vs.styles[wParam].weight;
7339 case SCI_STYLEGETITALIC:
7340 return vs.styles[wParam].italic ? 1 : 0;
7341 case SCI_STYLEGETEOLFILLED:
7342 return vs.styles[wParam].eolFilled ? 1 : 0;
7343 case SCI_STYLEGETSIZE:
7344 return vs.styles[wParam].size / SC_FONT_SIZE_MULTIPLIER;
7345 case SCI_STYLEGETSIZEFRACTIONAL:
7346 return vs.styles[wParam].size;
7347 case SCI_STYLEGETFONT:
7348 return StringResult(lParam, vs.styles[wParam].fontName);
7349 case SCI_STYLEGETUNDERLINE:
7350 return vs.styles[wParam].underline ? 1 : 0;
7351 case SCI_STYLEGETCASE:
7352 return static_cast<int>(vs.styles[wParam].caseForce);
7353 case SCI_STYLEGETCHARACTERSET:
7354 return vs.styles[wParam].characterSet;
7355 case SCI_STYLEGETVISIBLE:
7356 return vs.styles[wParam].visible ? 1 : 0;
7357 case SCI_STYLEGETCHANGEABLE:
7358 return vs.styles[wParam].changeable ? 1 : 0;
7359 case SCI_STYLEGETHOTSPOT:
7360 return vs.styles[wParam].hotspot ? 1 : 0;
7362 return 0;
7365 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
7366 const size_t len = val ? strlen(val) : 0;
7367 if (lParam) {
7368 char *ptr = CharPtrFromSPtr(lParam);
7369 if (val)
7370 memcpy(ptr, val, len+1);
7371 else
7372 *ptr = 0;
7374 return len; // Not including NUL
7377 sptr_t Editor::BytesResult(sptr_t lParam, const unsigned char *val, size_t len) {
7378 // No NUL termination: len is number of valid/displayed bytes
7379 if (lParam) {
7380 char *ptr = CharPtrFromSPtr(lParam);
7381 if (val)
7382 memcpy(ptr, val, len);
7383 else
7384 *ptr = 0;
7386 return val ? len : 0;
7389 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7390 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
7392 // Optional macro recording hook
7393 if (recordingMacro)
7394 NotifyMacroRecord(iMessage, wParam, lParam);
7396 switch (iMessage) {
7398 case SCI_GETTEXT: {
7399 if (lParam == 0)
7400 return pdoc->Length() + 1;
7401 if (wParam == 0)
7402 return 0;
7403 char *ptr = CharPtrFromSPtr(lParam);
7404 unsigned int iChar = 0;
7405 for (; iChar < wParam - 1; iChar++)
7406 ptr[iChar] = pdoc->CharAt(iChar);
7407 ptr[iChar] = '\0';
7408 return iChar;
7411 case SCI_SETTEXT: {
7412 if (lParam == 0)
7413 return 0;
7414 UndoGroup ug(pdoc);
7415 pdoc->DeleteChars(0, pdoc->Length());
7416 SetEmptySelection(0);
7417 const char *text = CharPtrFromSPtr(lParam);
7418 pdoc->InsertString(0, text, istrlen(text));
7419 return 1;
7422 case SCI_GETTEXTLENGTH:
7423 return pdoc->Length();
7425 case SCI_CUT:
7426 Cut();
7427 SetLastXChosen();
7428 break;
7430 case SCI_COPY:
7431 Copy();
7432 break;
7434 case SCI_COPYALLOWLINE:
7435 CopyAllowLine();
7436 break;
7438 case SCI_VERTICALCENTRECARET:
7439 VerticalCentreCaret();
7440 break;
7442 case SCI_MOVESELECTEDLINESUP:
7443 MoveSelectedLinesUp();
7444 break;
7446 case SCI_MOVESELECTEDLINESDOWN:
7447 MoveSelectedLinesDown();
7448 break;
7450 case SCI_COPYRANGE:
7451 CopyRangeToClipboard(static_cast<int>(wParam), static_cast<int>(lParam));
7452 break;
7454 case SCI_COPYTEXT:
7455 CopyText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7456 break;
7458 case SCI_PASTE:
7459 Paste();
7460 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
7461 SetLastXChosen();
7463 EnsureCaretVisible();
7464 break;
7466 case SCI_CLEAR:
7467 Clear();
7468 SetLastXChosen();
7469 EnsureCaretVisible();
7470 break;
7472 case SCI_UNDO:
7473 Undo();
7474 SetLastXChosen();
7475 break;
7477 case SCI_CANUNDO:
7478 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
7480 case SCI_EMPTYUNDOBUFFER:
7481 pdoc->DeleteUndoHistory();
7482 return 0;
7484 case SCI_GETFIRSTVISIBLELINE:
7485 return topLine;
7487 case SCI_SETFIRSTVISIBLELINE:
7488 ScrollTo(static_cast<int>(wParam));
7489 break;
7491 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
7492 int lineStart = pdoc->LineStart(static_cast<int>(wParam));
7493 int lineEnd = pdoc->LineStart(static_cast<int>(wParam + 1));
7494 if (lParam == 0) {
7495 return lineEnd - lineStart;
7497 char *ptr = CharPtrFromSPtr(lParam);
7498 int iPlace = 0;
7499 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
7500 ptr[iPlace++] = pdoc->CharAt(iChar);
7502 return iPlace;
7505 case SCI_GETLINECOUNT:
7506 if (pdoc->LinesTotal() == 0)
7507 return 1;
7508 else
7509 return pdoc->LinesTotal();
7511 case SCI_GETMODIFY:
7512 return !pdoc->IsSavePoint();
7514 case SCI_SETSEL: {
7515 int nStart = static_cast<int>(wParam);
7516 int nEnd = static_cast<int>(lParam);
7517 if (nEnd < 0)
7518 nEnd = pdoc->Length();
7519 if (nStart < 0)
7520 nStart = nEnd; // Remove selection
7521 InvalidateSelection(SelectionRange(nStart, nEnd));
7522 sel.Clear();
7523 sel.selType = Selection::selStream;
7524 SetSelection(nEnd, nStart);
7525 EnsureCaretVisible();
7527 break;
7529 case SCI_GETSELTEXT: {
7530 SelectionText selectedText;
7531 CopySelectionRange(&selectedText);
7532 if (lParam == 0) {
7533 return selectedText.LengthWithTerminator();
7534 } else {
7535 char *ptr = CharPtrFromSPtr(lParam);
7536 unsigned int iChar = 0;
7537 if (selectedText.Length()) {
7538 for (; iChar < selectedText.LengthWithTerminator(); iChar++)
7539 ptr[iChar] = selectedText.Data()[iChar];
7540 } else {
7541 ptr[0] = '\0';
7543 return iChar;
7547 case SCI_LINEFROMPOSITION:
7548 if (static_cast<int>(wParam) < 0)
7549 return 0;
7550 return pdoc->LineFromPosition(static_cast<int>(wParam));
7552 case SCI_POSITIONFROMLINE:
7553 if (static_cast<int>(wParam) < 0)
7554 wParam = pdoc->LineFromPosition(SelectionStart().Position());
7555 if (wParam == 0)
7556 return 0; // Even if there is no text, there is a first line that starts at 0
7557 if (static_cast<int>(wParam) > pdoc->LinesTotal())
7558 return -1;
7559 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
7560 // return -1;
7561 return pdoc->LineStart(static_cast<int>(wParam));
7563 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
7564 case SCI_LINELENGTH:
7565 if ((static_cast<int>(wParam) < 0) ||
7566 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
7567 return 0;
7568 return pdoc->LineStart(static_cast<int>(wParam) + 1) - pdoc->LineStart(static_cast<int>(wParam));
7570 case SCI_REPLACESEL: {
7571 if (lParam == 0)
7572 return 0;
7573 UndoGroup ug(pdoc);
7574 ClearSelection();
7575 char *replacement = CharPtrFromSPtr(lParam);
7576 const int lengthInserted = pdoc->InsertString(
7577 sel.MainCaret(), replacement, istrlen(replacement));
7578 SetEmptySelection(sel.MainCaret() + lengthInserted);
7579 EnsureCaretVisible();
7581 break;
7583 case SCI_SETTARGETSTART:
7584 targetStart = static_cast<int>(wParam);
7585 break;
7587 case SCI_GETTARGETSTART:
7588 return targetStart;
7590 case SCI_SETTARGETEND:
7591 targetEnd = static_cast<int>(wParam);
7592 break;
7594 case SCI_GETTARGETEND:
7595 return targetEnd;
7597 case SCI_TARGETFROMSELECTION:
7598 if (sel.MainCaret() < sel.MainAnchor()) {
7599 targetStart = sel.MainCaret();
7600 targetEnd = sel.MainAnchor();
7601 } else {
7602 targetStart = sel.MainAnchor();
7603 targetEnd = sel.MainCaret();
7605 break;
7607 case SCI_REPLACETARGET:
7608 PLATFORM_ASSERT(lParam);
7609 return ReplaceTarget(false, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7611 case SCI_REPLACETARGETRE:
7612 PLATFORM_ASSERT(lParam);
7613 return ReplaceTarget(true, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7615 case SCI_SEARCHINTARGET:
7616 PLATFORM_ASSERT(lParam);
7617 return SearchInTarget(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7619 case SCI_SETSEARCHFLAGS:
7620 searchFlags = static_cast<int>(wParam);
7621 break;
7623 case SCI_GETSEARCHFLAGS:
7624 return searchFlags;
7626 case SCI_GETTAG:
7627 return GetTag(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7629 case SCI_POSITIONBEFORE:
7630 return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) - 1, -1, true);
7632 case SCI_POSITIONAFTER:
7633 return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) + 1, 1, true);
7635 case SCI_POSITIONRELATIVE:
7636 return Platform::Clamp(pdoc->GetRelativePosition(static_cast<int>(wParam), static_cast<int>(lParam)), 0, pdoc->Length());
7638 case SCI_LINESCROLL:
7639 ScrollTo(topLine + static_cast<int>(lParam));
7640 HorizontalScrollTo(xOffset + static_cast<int>(wParam)* static_cast<int>(vs.spaceWidth));
7641 return 1;
7643 case SCI_SETXOFFSET:
7644 xOffset = static_cast<int>(wParam);
7645 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7646 SetHorizontalScrollPos();
7647 Redraw();
7648 break;
7650 case SCI_GETXOFFSET:
7651 return xOffset;
7653 case SCI_CHOOSECARETX:
7654 SetLastXChosen();
7655 break;
7657 case SCI_SCROLLCARET:
7658 EnsureCaretVisible();
7659 break;
7661 case SCI_SETREADONLY:
7662 pdoc->SetReadOnly(wParam != 0);
7663 return 1;
7665 case SCI_GETREADONLY:
7666 return pdoc->IsReadOnly();
7668 case SCI_CANPASTE:
7669 return CanPaste();
7671 case SCI_POINTXFROMPOSITION:
7672 if (lParam < 0) {
7673 return 0;
7674 } else {
7675 Point pt = LocationFromPosition(static_cast<int>(lParam));
7676 // Convert to view-relative
7677 return static_cast<int>(pt.x) - vs.textStart + vs.fixedColumnWidth;
7680 case SCI_POINTYFROMPOSITION:
7681 if (lParam < 0) {
7682 return 0;
7683 } else {
7684 Point pt = LocationFromPosition(static_cast<int>(lParam));
7685 return static_cast<int>(pt.y);
7688 case SCI_FINDTEXT:
7689 return FindText(wParam, lParam);
7691 case SCI_GETTEXTRANGE: {
7692 if (lParam == 0)
7693 return 0;
7694 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7695 int cpMax = tr->chrg.cpMax;
7696 if (cpMax == -1)
7697 cpMax = pdoc->Length();
7698 PLATFORM_ASSERT(cpMax <= pdoc->Length());
7699 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
7700 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
7701 // Spec says copied text is terminated with a NUL
7702 tr->lpstrText[len] = '\0';
7703 return len; // Not including NUL
7706 case SCI_HIDESELECTION:
7707 hideSelection = wParam != 0;
7708 Redraw();
7709 break;
7711 case SCI_FORMATRANGE:
7712 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
7714 case SCI_GETMARGINLEFT:
7715 return vs.leftMarginWidth;
7717 case SCI_GETMARGINRIGHT:
7718 return vs.rightMarginWidth;
7720 case SCI_SETMARGINLEFT:
7721 lastXChosen += static_cast<int>(lParam) - vs.leftMarginWidth;
7722 vs.leftMarginWidth = static_cast<int>(lParam);
7723 InvalidateStyleRedraw();
7724 break;
7726 case SCI_SETMARGINRIGHT:
7727 vs.rightMarginWidth = static_cast<int>(lParam);
7728 InvalidateStyleRedraw();
7729 break;
7731 // Control specific mesages
7733 case SCI_ADDTEXT: {
7734 if (lParam == 0)
7735 return 0;
7736 const int lengthInserted = pdoc->InsertString(
7737 CurrentPosition(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7738 SetEmptySelection(sel.MainCaret() + lengthInserted);
7739 return 0;
7742 case SCI_ADDSTYLEDTEXT:
7743 if (lParam)
7744 AddStyledText(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7745 return 0;
7747 case SCI_INSERTTEXT: {
7748 if (lParam == 0)
7749 return 0;
7750 int insertPos = static_cast<int>(wParam);
7751 if (static_cast<int>(wParam) == -1)
7752 insertPos = CurrentPosition();
7753 int newCurrent = CurrentPosition();
7754 char *sz = CharPtrFromSPtr(lParam);
7755 const int lengthInserted = pdoc->InsertString(insertPos, sz, istrlen(sz));
7756 if (newCurrent > insertPos)
7757 newCurrent += lengthInserted;
7758 SetEmptySelection(newCurrent);
7759 return 0;
7762 case SCI_CHANGEINSERTION:
7763 PLATFORM_ASSERT(lParam);
7764 pdoc->ChangeInsertion(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7765 return 0;
7767 case SCI_APPENDTEXT:
7768 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7769 return 0;
7771 case SCI_CLEARALL:
7772 ClearAll();
7773 return 0;
7775 case SCI_DELETERANGE:
7776 pdoc->DeleteChars(static_cast<int>(wParam), static_cast<int>(lParam));
7777 return 0;
7779 case SCI_CLEARDOCUMENTSTYLE:
7780 ClearDocumentStyle();
7781 return 0;
7783 case SCI_SETUNDOCOLLECTION:
7784 pdoc->SetUndoCollection(wParam != 0);
7785 return 0;
7787 case SCI_GETUNDOCOLLECTION:
7788 return pdoc->IsCollectingUndo();
7790 case SCI_BEGINUNDOACTION:
7791 pdoc->BeginUndoAction();
7792 return 0;
7794 case SCI_ENDUNDOACTION:
7795 pdoc->EndUndoAction();
7796 return 0;
7798 case SCI_GETCARETPERIOD:
7799 return caret.period;
7801 case SCI_SETCARETPERIOD:
7802 CaretSetPeriod(static_cast<int>(wParam));
7803 break;
7805 case SCI_GETWORDCHARS:
7806 return pdoc->GetCharsOfClass(CharClassify::ccWord, reinterpret_cast<unsigned char *>(lParam));
7808 case SCI_SETWORDCHARS: {
7809 pdoc->SetDefaultCharClasses(false);
7810 if (lParam == 0)
7811 return 0;
7812 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
7814 break;
7816 case SCI_GETWHITESPACECHARS:
7817 return pdoc->GetCharsOfClass(CharClassify::ccSpace, reinterpret_cast<unsigned char *>(lParam));
7819 case SCI_SETWHITESPACECHARS: {
7820 if (lParam == 0)
7821 return 0;
7822 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
7824 break;
7826 case SCI_GETPUNCTUATIONCHARS:
7827 return pdoc->GetCharsOfClass(CharClassify::ccPunctuation, reinterpret_cast<unsigned char *>(lParam));
7829 case SCI_SETPUNCTUATIONCHARS: {
7830 if (lParam == 0)
7831 return 0;
7832 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccPunctuation);
7834 break;
7836 case SCI_SETCHARSDEFAULT:
7837 pdoc->SetDefaultCharClasses(true);
7838 break;
7840 case SCI_GETLENGTH:
7841 return pdoc->Length();
7843 case SCI_ALLOCATE:
7844 pdoc->Allocate(static_cast<int>(wParam));
7845 break;
7847 case SCI_GETCHARAT:
7848 return pdoc->CharAt(static_cast<int>(wParam));
7850 case SCI_SETCURRENTPOS:
7851 if (sel.IsRectangular()) {
7852 sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
7853 SetRectangularRange();
7854 Redraw();
7855 } else {
7856 SetSelection(static_cast<int>(wParam), sel.MainAnchor());
7858 break;
7860 case SCI_GETCURRENTPOS:
7861 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
7863 case SCI_SETANCHOR:
7864 if (sel.IsRectangular()) {
7865 sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
7866 SetRectangularRange();
7867 Redraw();
7868 } else {
7869 SetSelection(sel.MainCaret(), static_cast<int>(wParam));
7871 break;
7873 case SCI_GETANCHOR:
7874 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
7876 case SCI_SETSELECTIONSTART:
7877 SetSelection(Platform::Maximum(sel.MainCaret(), static_cast<int>(wParam)), static_cast<int>(wParam));
7878 break;
7880 case SCI_GETSELECTIONSTART:
7881 return sel.LimitsForRectangularElseMain().start.Position();
7883 case SCI_SETSELECTIONEND:
7884 SetSelection(static_cast<int>(wParam), Platform::Minimum(sel.MainAnchor(), static_cast<int>(wParam)));
7885 break;
7887 case SCI_GETSELECTIONEND:
7888 return sel.LimitsForRectangularElseMain().end.Position();
7890 case SCI_SETEMPTYSELECTION:
7891 SetEmptySelection(static_cast<int>(wParam));
7892 break;
7894 case SCI_SETPRINTMAGNIFICATION:
7895 printParameters.magnification = static_cast<int>(wParam);
7896 break;
7898 case SCI_GETPRINTMAGNIFICATION:
7899 return printParameters.magnification;
7901 case SCI_SETPRINTCOLOURMODE:
7902 printParameters.colourMode = static_cast<int>(wParam);
7903 break;
7905 case SCI_GETPRINTCOLOURMODE:
7906 return printParameters.colourMode;
7908 case SCI_SETPRINTWRAPMODE:
7909 printParameters.wrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
7910 break;
7912 case SCI_GETPRINTWRAPMODE:
7913 return printParameters.wrapState;
7915 case SCI_GETSTYLEAT:
7916 if (static_cast<int>(wParam) >= pdoc->Length())
7917 return 0;
7918 else
7919 return pdoc->StyleAt(static_cast<int>(wParam));
7921 case SCI_REDO:
7922 Redo();
7923 break;
7925 case SCI_SELECTALL:
7926 SelectAll();
7927 break;
7929 case SCI_SETSAVEPOINT:
7930 pdoc->SetSavePoint();
7931 break;
7933 case SCI_GETSTYLEDTEXT: {
7934 if (lParam == 0)
7935 return 0;
7936 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7937 int iPlace = 0;
7938 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
7939 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
7940 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
7942 tr->lpstrText[iPlace] = '\0';
7943 tr->lpstrText[iPlace + 1] = '\0';
7944 return iPlace;
7947 case SCI_CANREDO:
7948 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
7950 case SCI_MARKERLINEFROMHANDLE:
7951 return pdoc->LineFromHandle(static_cast<int>(wParam));
7953 case SCI_MARKERDELETEHANDLE:
7954 pdoc->DeleteMarkFromHandle(static_cast<int>(wParam));
7955 break;
7957 case SCI_GETVIEWWS:
7958 return vs.viewWhitespace;
7960 case SCI_SETVIEWWS:
7961 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
7962 Redraw();
7963 break;
7965 case SCI_GETWHITESPACESIZE:
7966 return vs.whitespaceSize;
7968 case SCI_SETWHITESPACESIZE:
7969 vs.whitespaceSize = static_cast<int>(wParam);
7970 Redraw();
7971 break;
7973 case SCI_POSITIONFROMPOINT:
7974 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
7975 false, false);
7977 case SCI_POSITIONFROMPOINTCLOSE:
7978 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
7979 true, false);
7981 case SCI_CHARPOSITIONFROMPOINT:
7982 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
7983 false, true);
7985 case SCI_CHARPOSITIONFROMPOINTCLOSE:
7986 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
7987 true, true);
7989 case SCI_GOTOLINE:
7990 GoToLine(static_cast<int>(wParam));
7991 break;
7993 case SCI_GOTOPOS:
7994 SetEmptySelection(static_cast<int>(wParam));
7995 EnsureCaretVisible();
7996 break;
7998 case SCI_GETCURLINE: {
7999 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
8000 int lineStart = pdoc->LineStart(lineCurrentPos);
8001 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
8002 if (lParam == 0) {
8003 return 1 + lineEnd - lineStart;
8005 PLATFORM_ASSERT(wParam > 0);
8006 char *ptr = CharPtrFromSPtr(lParam);
8007 unsigned int iPlace = 0;
8008 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
8009 ptr[iPlace++] = pdoc->CharAt(iChar);
8011 ptr[iPlace] = '\0';
8012 return sel.MainCaret() - lineStart;
8015 case SCI_GETENDSTYLED:
8016 return pdoc->GetEndStyled();
8018 case SCI_GETEOLMODE:
8019 return pdoc->eolMode;
8021 case SCI_SETEOLMODE:
8022 pdoc->eolMode = static_cast<int>(wParam);
8023 break;
8025 case SCI_SETLINEENDTYPESALLOWED:
8026 if (pdoc->SetLineEndTypesAllowed(static_cast<int>(wParam))) {
8027 cs.Clear();
8028 cs.InsertLines(0, pdoc->LinesTotal() - 1);
8029 SetAnnotationHeights(0, pdoc->LinesTotal());
8030 InvalidateStyleRedraw();
8032 break;
8034 case SCI_GETLINEENDTYPESALLOWED:
8035 return pdoc->GetLineEndTypesAllowed();
8037 case SCI_GETLINEENDTYPESACTIVE:
8038 return pdoc->GetLineEndTypesActive();
8040 case SCI_STARTSTYLING:
8041 pdoc->StartStyling(static_cast<int>(wParam), static_cast<char>(lParam));
8042 break;
8044 case SCI_SETSTYLING:
8045 pdoc->SetStyleFor(static_cast<int>(wParam), static_cast<char>(lParam));
8046 break;
8048 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
8049 if (lParam == 0)
8050 return 0;
8051 pdoc->SetStyles(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
8052 break;
8054 case SCI_SETBUFFEREDDRAW:
8055 bufferedDraw = wParam != 0;
8056 break;
8058 case SCI_GETBUFFEREDDRAW:
8059 return bufferedDraw;
8061 case SCI_GETTWOPHASEDRAW:
8062 return twoPhaseDraw;
8064 case SCI_SETTWOPHASEDRAW:
8065 twoPhaseDraw = wParam != 0;
8066 InvalidateStyleRedraw();
8067 break;
8069 case SCI_SETFONTQUALITY:
8070 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
8071 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
8072 InvalidateStyleRedraw();
8073 break;
8075 case SCI_GETFONTQUALITY:
8076 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
8078 case SCI_SETTABWIDTH:
8079 if (wParam > 0) {
8080 pdoc->tabInChars = static_cast<int>(wParam);
8081 if (pdoc->indentInChars == 0)
8082 pdoc->actualIndentInChars = pdoc->tabInChars;
8084 InvalidateStyleRedraw();
8085 break;
8087 case SCI_GETTABWIDTH:
8088 return pdoc->tabInChars;
8090 case SCI_SETINDENT:
8091 pdoc->indentInChars = static_cast<int>(wParam);
8092 if (pdoc->indentInChars != 0)
8093 pdoc->actualIndentInChars = pdoc->indentInChars;
8094 else
8095 pdoc->actualIndentInChars = pdoc->tabInChars;
8096 InvalidateStyleRedraw();
8097 break;
8099 case SCI_GETINDENT:
8100 return pdoc->indentInChars;
8102 case SCI_SETUSETABS:
8103 pdoc->useTabs = wParam != 0;
8104 InvalidateStyleRedraw();
8105 break;
8107 case SCI_GETUSETABS:
8108 return pdoc->useTabs;
8110 case SCI_SETLINEINDENTATION:
8111 pdoc->SetLineIndentation(static_cast<int>(wParam), static_cast<int>(lParam));
8112 break;
8114 case SCI_GETLINEINDENTATION:
8115 return pdoc->GetLineIndentation(static_cast<int>(wParam));
8117 case SCI_GETLINEINDENTPOSITION:
8118 return pdoc->GetLineIndentPosition(static_cast<int>(wParam));
8120 case SCI_SETTABINDENTS:
8121 pdoc->tabIndents = wParam != 0;
8122 break;
8124 case SCI_GETTABINDENTS:
8125 return pdoc->tabIndents;
8127 case SCI_SETBACKSPACEUNINDENTS:
8128 pdoc->backspaceUnindents = wParam != 0;
8129 break;
8131 case SCI_GETBACKSPACEUNINDENTS:
8132 return pdoc->backspaceUnindents;
8134 case SCI_SETMOUSEDWELLTIME:
8135 dwellDelay = static_cast<int>(wParam);
8136 ticksToDwell = dwellDelay;
8137 break;
8139 case SCI_GETMOUSEDWELLTIME:
8140 return dwellDelay;
8142 case SCI_WORDSTARTPOSITION:
8143 return pdoc->ExtendWordSelect(static_cast<int>(wParam), -1, lParam != 0);
8145 case SCI_WORDENDPOSITION:
8146 return pdoc->ExtendWordSelect(static_cast<int>(wParam), 1, lParam != 0);
8148 case SCI_SETWRAPMODE:
8149 if (vs.SetWrapState(static_cast<int>(wParam))) {
8150 xOffset = 0;
8151 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
8152 InvalidateStyleRedraw();
8153 ReconfigureScrollBars();
8155 break;
8157 case SCI_GETWRAPMODE:
8158 return vs.wrapState;
8160 case SCI_SETWRAPVISUALFLAGS:
8161 if (vs.SetWrapVisualFlags(static_cast<int>(wParam))) {
8162 InvalidateStyleRedraw();
8163 ReconfigureScrollBars();
8165 break;
8167 case SCI_GETWRAPVISUALFLAGS:
8168 return vs.wrapVisualFlags;
8170 case SCI_SETWRAPVISUALFLAGSLOCATION:
8171 if (vs.SetWrapVisualFlagsLocation(static_cast<int>(wParam))) {
8172 InvalidateStyleRedraw();
8174 break;
8176 case SCI_GETWRAPVISUALFLAGSLOCATION:
8177 return vs.wrapVisualFlagsLocation;
8179 case SCI_SETWRAPSTARTINDENT:
8180 if (vs.SetWrapVisualStartIndent(static_cast<int>(wParam))) {
8181 InvalidateStyleRedraw();
8182 ReconfigureScrollBars();
8184 break;
8186 case SCI_GETWRAPSTARTINDENT:
8187 return vs.wrapVisualStartIndent;
8189 case SCI_SETWRAPINDENTMODE:
8190 if (vs.SetWrapIndentMode(static_cast<int>(wParam))) {
8191 InvalidateStyleRedraw();
8192 ReconfigureScrollBars();
8194 break;
8196 case SCI_GETWRAPINDENTMODE:
8197 return vs.wrapIndentMode;
8199 case SCI_SETLAYOUTCACHE:
8200 llc.SetLevel(static_cast<int>(wParam));
8201 break;
8203 case SCI_GETLAYOUTCACHE:
8204 return llc.GetLevel();
8206 case SCI_SETPOSITIONCACHE:
8207 posCache.SetSize(wParam);
8208 break;
8210 case SCI_GETPOSITIONCACHE:
8211 return posCache.GetSize();
8213 case SCI_SETSCROLLWIDTH:
8214 PLATFORM_ASSERT(wParam > 0);
8215 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
8216 lineWidthMaxSeen = 0;
8217 scrollWidth = static_cast<int>(wParam);
8218 SetScrollBars();
8220 break;
8222 case SCI_GETSCROLLWIDTH:
8223 return scrollWidth;
8225 case SCI_SETSCROLLWIDTHTRACKING:
8226 trackLineWidth = wParam != 0;
8227 break;
8229 case SCI_GETSCROLLWIDTHTRACKING:
8230 return trackLineWidth;
8232 case SCI_LINESJOIN:
8233 LinesJoin();
8234 break;
8236 case SCI_LINESSPLIT:
8237 LinesSplit(static_cast<int>(wParam));
8238 break;
8240 case SCI_TEXTWIDTH:
8241 PLATFORM_ASSERT(wParam < vs.styles.size());
8242 PLATFORM_ASSERT(lParam);
8243 return TextWidth(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
8245 case SCI_TEXTHEIGHT:
8246 return vs.lineHeight;
8248 case SCI_SETENDATLASTLINE:
8249 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
8250 if (endAtLastLine != (wParam != 0)) {
8251 endAtLastLine = wParam != 0;
8252 SetScrollBars();
8254 break;
8256 case SCI_GETENDATLASTLINE:
8257 return endAtLastLine;
8259 case SCI_SETCARETSTICKY:
8260 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
8261 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
8262 caretSticky = static_cast<int>(wParam);
8264 break;
8266 case SCI_GETCARETSTICKY:
8267 return caretSticky;
8269 case SCI_TOGGLECARETSTICKY:
8270 caretSticky = !caretSticky;
8271 break;
8273 case SCI_GETCOLUMN:
8274 return pdoc->GetColumn(static_cast<int>(wParam));
8276 case SCI_FINDCOLUMN:
8277 return pdoc->FindColumn(static_cast<int>(wParam), static_cast<int>(lParam));
8279 case SCI_SETHSCROLLBAR :
8280 if (horizontalScrollBarVisible != (wParam != 0)) {
8281 horizontalScrollBarVisible = wParam != 0;
8282 SetScrollBars();
8283 ReconfigureScrollBars();
8285 break;
8287 case SCI_GETHSCROLLBAR:
8288 return horizontalScrollBarVisible;
8290 case SCI_SETVSCROLLBAR:
8291 if (verticalScrollBarVisible != (wParam != 0)) {
8292 verticalScrollBarVisible = wParam != 0;
8293 SetScrollBars();
8294 ReconfigureScrollBars();
8295 if (verticalScrollBarVisible)
8296 SetVerticalScrollPos();
8298 break;
8300 case SCI_GETVSCROLLBAR:
8301 return verticalScrollBarVisible;
8303 case SCI_SETINDENTATIONGUIDES:
8304 vs.viewIndentationGuides = IndentView(wParam);
8305 Redraw();
8306 break;
8308 case SCI_GETINDENTATIONGUIDES:
8309 return vs.viewIndentationGuides;
8311 case SCI_SETHIGHLIGHTGUIDE:
8312 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
8313 highlightGuideColumn = static_cast<int>(wParam);
8314 Redraw();
8316 break;
8318 case SCI_GETHIGHLIGHTGUIDE:
8319 return highlightGuideColumn;
8321 case SCI_GETLINEENDPOSITION:
8322 return pdoc->LineEnd(static_cast<int>(wParam));
8324 case SCI_SETCODEPAGE:
8325 if (ValidCodePage(static_cast<int>(wParam))) {
8326 if (pdoc->SetDBCSCodePage(static_cast<int>(wParam))) {
8327 cs.Clear();
8328 cs.InsertLines(0, pdoc->LinesTotal() - 1);
8329 SetAnnotationHeights(0, pdoc->LinesTotal());
8330 InvalidateStyleRedraw();
8331 SetRepresentations();
8334 break;
8336 case SCI_GETCODEPAGE:
8337 return pdoc->dbcsCodePage;
8339 #ifdef INCLUDE_DEPRECATED_FEATURES
8340 case SCI_SETUSEPALETTE:
8341 InvalidateStyleRedraw();
8342 break;
8344 case SCI_GETUSEPALETTE:
8345 return 0;
8346 #endif
8348 // Marker definition and setting
8349 case SCI_MARKERDEFINE:
8350 if (wParam <= MARKER_MAX) {
8351 vs.markers[wParam].markType = static_cast<int>(lParam);
8352 vs.CalcLargestMarkerHeight();
8354 InvalidateStyleData();
8355 RedrawSelMargin();
8356 break;
8358 case SCI_MARKERSYMBOLDEFINED:
8359 if (wParam <= MARKER_MAX)
8360 return vs.markers[wParam].markType;
8361 else
8362 return 0;
8364 case SCI_MARKERSETFORE:
8365 if (wParam <= MARKER_MAX)
8366 vs.markers[wParam].fore = ColourDesired(static_cast<long>(lParam));
8367 InvalidateStyleData();
8368 RedrawSelMargin();
8369 break;
8370 case SCI_MARKERSETBACKSELECTED:
8371 if (wParam <= MARKER_MAX)
8372 vs.markers[wParam].backSelected = ColourDesired(static_cast<long>(lParam));
8373 InvalidateStyleData();
8374 RedrawSelMargin();
8375 break;
8376 case SCI_MARKERENABLEHIGHLIGHT:
8377 highlightDelimiter.isEnabled = wParam == 1;
8378 RedrawSelMargin();
8379 break;
8380 case SCI_MARKERSETBACK:
8381 if (wParam <= MARKER_MAX)
8382 vs.markers[wParam].back = ColourDesired(static_cast<long>(lParam));
8383 InvalidateStyleData();
8384 RedrawSelMargin();
8385 break;
8386 case SCI_MARKERSETALPHA:
8387 if (wParam <= MARKER_MAX)
8388 vs.markers[wParam].alpha = static_cast<int>(lParam);
8389 InvalidateStyleRedraw();
8390 break;
8391 case SCI_MARKERADD: {
8392 int markerID = pdoc->AddMark(static_cast<int>(wParam), static_cast<int>(lParam));
8393 return markerID;
8395 case SCI_MARKERADDSET:
8396 if (lParam != 0)
8397 pdoc->AddMarkSet(static_cast<int>(wParam), static_cast<int>(lParam));
8398 break;
8400 case SCI_MARKERDELETE:
8401 pdoc->DeleteMark(static_cast<int>(wParam), static_cast<int>(lParam));
8402 break;
8404 case SCI_MARKERDELETEALL:
8405 pdoc->DeleteAllMarks(static_cast<int>(wParam));
8406 break;
8408 case SCI_MARKERGET:
8409 return pdoc->GetMark(static_cast<int>(wParam));
8411 case SCI_MARKERNEXT:
8412 return pdoc->MarkerNext(static_cast<int>(wParam), static_cast<int>(lParam));
8414 case SCI_MARKERPREVIOUS: {
8415 for (int iLine = static_cast<int>(wParam); iLine >= 0; iLine--) {
8416 if ((pdoc->GetMark(iLine) & lParam) != 0)
8417 return iLine;
8420 return -1;
8422 case SCI_MARKERDEFINEPIXMAP:
8423 if (wParam <= MARKER_MAX) {
8424 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
8425 vs.CalcLargestMarkerHeight();
8427 InvalidateStyleData();
8428 RedrawSelMargin();
8429 break;
8431 case SCI_RGBAIMAGESETWIDTH:
8432 sizeRGBAImage.x = static_cast<XYPOSITION>(wParam);
8433 break;
8435 case SCI_RGBAIMAGESETHEIGHT:
8436 sizeRGBAImage.y = static_cast<XYPOSITION>(wParam);
8437 break;
8439 case SCI_RGBAIMAGESETSCALE:
8440 scaleRGBAImage = static_cast<float>(wParam);
8441 break;
8443 case SCI_MARKERDEFINERGBAIMAGE:
8444 if (wParam <= MARKER_MAX) {
8445 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0f, reinterpret_cast<unsigned char *>(lParam));
8446 vs.CalcLargestMarkerHeight();
8448 InvalidateStyleData();
8449 RedrawSelMargin();
8450 break;
8452 case SCI_SETMARGINTYPEN:
8453 if (ValidMargin(wParam)) {
8454 vs.ms[wParam].style = static_cast<int>(lParam);
8455 InvalidateStyleRedraw();
8457 break;
8459 case SCI_GETMARGINTYPEN:
8460 if (ValidMargin(wParam))
8461 return vs.ms[wParam].style;
8462 else
8463 return 0;
8465 case SCI_SETMARGINWIDTHN:
8466 if (ValidMargin(wParam)) {
8467 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
8468 if (vs.ms[wParam].width != lParam) {
8469 lastXChosen += static_cast<int>(lParam) - vs.ms[wParam].width;
8470 vs.ms[wParam].width = static_cast<int>(lParam);
8471 InvalidateStyleRedraw();
8474 break;
8476 case SCI_GETMARGINWIDTHN:
8477 if (ValidMargin(wParam))
8478 return vs.ms[wParam].width;
8479 else
8480 return 0;
8482 case SCI_SETMARGINMASKN:
8483 if (ValidMargin(wParam)) {
8484 vs.ms[wParam].mask = static_cast<int>(lParam);
8485 InvalidateStyleRedraw();
8487 break;
8489 case SCI_GETMARGINMASKN:
8490 if (ValidMargin(wParam))
8491 return vs.ms[wParam].mask;
8492 else
8493 return 0;
8495 case SCI_SETMARGINSENSITIVEN:
8496 if (ValidMargin(wParam)) {
8497 vs.ms[wParam].sensitive = lParam != 0;
8498 InvalidateStyleRedraw();
8500 break;
8502 case SCI_GETMARGINSENSITIVEN:
8503 if (ValidMargin(wParam))
8504 return vs.ms[wParam].sensitive ? 1 : 0;
8505 else
8506 return 0;
8508 case SCI_SETMARGINCURSORN:
8509 if (ValidMargin(wParam))
8510 vs.ms[wParam].cursor = static_cast<int>(lParam);
8511 break;
8513 case SCI_GETMARGINCURSORN:
8514 if (ValidMargin(wParam))
8515 return vs.ms[wParam].cursor;
8516 else
8517 return 0;
8519 case SCI_STYLECLEARALL:
8520 vs.ClearStyles();
8521 InvalidateStyleRedraw();
8522 break;
8524 case SCI_STYLESETFORE:
8525 case SCI_STYLESETBACK:
8526 case SCI_STYLESETBOLD:
8527 case SCI_STYLESETWEIGHT:
8528 case SCI_STYLESETITALIC:
8529 case SCI_STYLESETEOLFILLED:
8530 case SCI_STYLESETSIZE:
8531 case SCI_STYLESETSIZEFRACTIONAL:
8532 case SCI_STYLESETFONT:
8533 case SCI_STYLESETUNDERLINE:
8534 case SCI_STYLESETCASE:
8535 case SCI_STYLESETCHARACTERSET:
8536 case SCI_STYLESETVISIBLE:
8537 case SCI_STYLESETCHANGEABLE:
8538 case SCI_STYLESETHOTSPOT:
8539 StyleSetMessage(iMessage, wParam, lParam);
8540 break;
8542 case SCI_STYLEGETFORE:
8543 case SCI_STYLEGETBACK:
8544 case SCI_STYLEGETBOLD:
8545 case SCI_STYLEGETWEIGHT:
8546 case SCI_STYLEGETITALIC:
8547 case SCI_STYLEGETEOLFILLED:
8548 case SCI_STYLEGETSIZE:
8549 case SCI_STYLEGETSIZEFRACTIONAL:
8550 case SCI_STYLEGETFONT:
8551 case SCI_STYLEGETUNDERLINE:
8552 case SCI_STYLEGETCASE:
8553 case SCI_STYLEGETCHARACTERSET:
8554 case SCI_STYLEGETVISIBLE:
8555 case SCI_STYLEGETCHANGEABLE:
8556 case SCI_STYLEGETHOTSPOT:
8557 return StyleGetMessage(iMessage, wParam, lParam);
8559 case SCI_STYLERESETDEFAULT:
8560 vs.ResetDefaultStyle();
8561 InvalidateStyleRedraw();
8562 break;
8563 case SCI_SETSTYLEBITS:
8564 vs.EnsureStyle(0xff);
8565 break;
8567 case SCI_GETSTYLEBITS:
8568 return 8;
8570 case SCI_SETLINESTATE:
8571 return pdoc->SetLineState(static_cast<int>(wParam), static_cast<int>(lParam));
8573 case SCI_GETLINESTATE:
8574 return pdoc->GetLineState(static_cast<int>(wParam));
8576 case SCI_GETMAXLINESTATE:
8577 return pdoc->GetMaxLineState();
8579 case SCI_GETCARETLINEVISIBLE:
8580 return vs.showCaretLineBackground;
8581 case SCI_SETCARETLINEVISIBLE:
8582 vs.showCaretLineBackground = wParam != 0;
8583 InvalidateStyleRedraw();
8584 break;
8585 case SCI_GETCARETLINEVISIBLEALWAYS:
8586 return vs.alwaysShowCaretLineBackground;
8587 case SCI_SETCARETLINEVISIBLEALWAYS:
8588 vs.alwaysShowCaretLineBackground = wParam != 0;
8589 InvalidateStyleRedraw();
8590 break;
8592 case SCI_GETCARETLINEBACK:
8593 return vs.caretLineBackground.AsLong();
8594 case SCI_SETCARETLINEBACK:
8595 vs.caretLineBackground = static_cast<int>(wParam);
8596 InvalidateStyleRedraw();
8597 break;
8598 case SCI_GETCARETLINEBACKALPHA:
8599 return vs.caretLineAlpha;
8600 case SCI_SETCARETLINEBACKALPHA:
8601 vs.caretLineAlpha = static_cast<int>(wParam);
8602 InvalidateStyleRedraw();
8603 break;
8605 // Folding messages
8607 case SCI_VISIBLEFROMDOCLINE:
8608 return cs.DisplayFromDoc(static_cast<int>(wParam));
8610 case SCI_DOCLINEFROMVISIBLE:
8611 return cs.DocFromDisplay(static_cast<int>(wParam));
8613 case SCI_WRAPCOUNT:
8614 return WrapCount(static_cast<int>(wParam));
8616 case SCI_SETFOLDLEVEL: {
8617 int prev = pdoc->SetLevel(static_cast<int>(wParam), static_cast<int>(lParam));
8618 if (prev != static_cast<int>(lParam))
8619 RedrawSelMargin();
8620 return prev;
8623 case SCI_GETFOLDLEVEL:
8624 return pdoc->GetLevel(static_cast<int>(wParam));
8626 case SCI_GETLASTCHILD:
8627 return pdoc->GetLastChild(static_cast<int>(wParam), static_cast<int>(lParam));
8629 case SCI_GETFOLDPARENT:
8630 return pdoc->GetFoldParent(static_cast<int>(wParam));
8632 case SCI_SHOWLINES:
8633 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), true);
8634 SetScrollBars();
8635 Redraw();
8636 break;
8638 case SCI_HIDELINES:
8639 if (wParam > 0)
8640 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), false);
8641 SetScrollBars();
8642 Redraw();
8643 break;
8645 case SCI_GETLINEVISIBLE:
8646 return cs.GetVisible(static_cast<int>(wParam));
8648 case SCI_GETALLLINESVISIBLE:
8649 return cs.HiddenLines() ? 0 : 1;
8651 case SCI_SETFOLDEXPANDED:
8652 SetFoldExpanded(static_cast<int>(wParam), lParam != 0);
8653 break;
8655 case SCI_GETFOLDEXPANDED:
8656 return cs.GetExpanded(static_cast<int>(wParam));
8658 case SCI_SETAUTOMATICFOLD:
8659 foldAutomatic = static_cast<int>(wParam);
8660 break;
8662 case SCI_GETAUTOMATICFOLD:
8663 return foldAutomatic;
8665 case SCI_SETFOLDFLAGS:
8666 foldFlags = static_cast<int>(wParam);
8667 Redraw();
8668 break;
8670 case SCI_TOGGLEFOLD:
8671 FoldLine(static_cast<int>(wParam), SC_FOLDACTION_TOGGLE);
8672 break;
8674 case SCI_FOLDLINE:
8675 FoldLine(static_cast<int>(wParam), static_cast<int>(lParam));
8676 break;
8678 case SCI_FOLDCHILDREN:
8679 FoldExpand(static_cast<int>(wParam), static_cast<int>(lParam), pdoc->GetLevel(static_cast<int>(wParam)));
8680 break;
8682 case SCI_FOLDALL:
8683 FoldAll(static_cast<int>(wParam));
8684 break;
8686 case SCI_EXPANDCHILDREN:
8687 FoldExpand(static_cast<int>(wParam), SC_FOLDACTION_EXPAND, static_cast<int>(lParam));
8688 break;
8690 case SCI_CONTRACTEDFOLDNEXT:
8691 return ContractedFoldNext(static_cast<int>(wParam));
8693 case SCI_ENSUREVISIBLE:
8694 EnsureLineVisible(static_cast<int>(wParam), false);
8695 break;
8697 case SCI_ENSUREVISIBLEENFORCEPOLICY:
8698 EnsureLineVisible(static_cast<int>(wParam), true);
8699 break;
8701 case SCI_SCROLLRANGE:
8702 ScrollRange(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
8703 break;
8705 case SCI_SEARCHANCHOR:
8706 SearchAnchor();
8707 break;
8709 case SCI_SEARCHNEXT:
8710 case SCI_SEARCHPREV:
8711 return SearchText(iMessage, wParam, lParam);
8713 case SCI_SETXCARETPOLICY:
8714 caretXPolicy = static_cast<int>(wParam);
8715 caretXSlop = static_cast<int>(lParam);
8716 break;
8718 case SCI_SETYCARETPOLICY:
8719 caretYPolicy = static_cast<int>(wParam);
8720 caretYSlop = static_cast<int>(lParam);
8721 break;
8723 case SCI_SETVISIBLEPOLICY:
8724 visiblePolicy = static_cast<int>(wParam);
8725 visibleSlop = static_cast<int>(lParam);
8726 break;
8728 case SCI_LINESONSCREEN:
8729 return LinesOnScreen();
8731 case SCI_SETSELFORE:
8732 vs.selColours.fore = ColourOptional(wParam, lParam);
8733 vs.selAdditionalForeground = ColourDesired(static_cast<long>(lParam));
8734 InvalidateStyleRedraw();
8735 break;
8737 case SCI_SETSELBACK:
8738 vs.selColours.back = ColourOptional(wParam, lParam);
8739 vs.selAdditionalBackground = ColourDesired(static_cast<long>(lParam));
8740 InvalidateStyleRedraw();
8741 break;
8743 case SCI_SETSELALPHA:
8744 vs.selAlpha = static_cast<int>(wParam);
8745 vs.selAdditionalAlpha = static_cast<int>(wParam);
8746 InvalidateStyleRedraw();
8747 break;
8749 case SCI_GETSELALPHA:
8750 return vs.selAlpha;
8752 case SCI_GETSELEOLFILLED:
8753 return vs.selEOLFilled;
8755 case SCI_SETSELEOLFILLED:
8756 vs.selEOLFilled = wParam != 0;
8757 InvalidateStyleRedraw();
8758 break;
8760 case SCI_SETWHITESPACEFORE:
8761 vs.whitespaceColours.fore = ColourOptional(wParam, lParam);
8762 InvalidateStyleRedraw();
8763 break;
8765 case SCI_SETWHITESPACEBACK:
8766 vs.whitespaceColours.back = ColourOptional(wParam, lParam);
8767 InvalidateStyleRedraw();
8768 break;
8770 case SCI_SETCARETFORE:
8771 vs.caretcolour = ColourDesired(static_cast<long>(wParam));
8772 InvalidateStyleRedraw();
8773 break;
8775 case SCI_GETCARETFORE:
8776 return vs.caretcolour.AsLong();
8778 case SCI_SETCARETSTYLE:
8779 if (wParam <= CARETSTYLE_BLOCK)
8780 vs.caretStyle = static_cast<int>(wParam);
8781 else
8782 /* Default to the line caret */
8783 vs.caretStyle = CARETSTYLE_LINE;
8784 InvalidateStyleRedraw();
8785 break;
8787 case SCI_GETCARETSTYLE:
8788 return vs.caretStyle;
8790 case SCI_SETCARETWIDTH:
8791 if (static_cast<int>(wParam) <= 0)
8792 vs.caretWidth = 0;
8793 else if (wParam >= 3)
8794 vs.caretWidth = 3;
8795 else
8796 vs.caretWidth = static_cast<int>(wParam);
8797 InvalidateStyleRedraw();
8798 break;
8800 case SCI_GETCARETWIDTH:
8801 return vs.caretWidth;
8803 case SCI_ASSIGNCMDKEY:
8804 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
8805 Platform::HighShortFromLong(static_cast<long>(wParam)), static_cast<unsigned int>(lParam));
8806 break;
8808 case SCI_CLEARCMDKEY:
8809 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
8810 Platform::HighShortFromLong(static_cast<long>(wParam)), SCI_NULL);
8811 break;
8813 case SCI_CLEARALLCMDKEYS:
8814 kmap.Clear();
8815 break;
8817 case SCI_INDICSETSTYLE:
8818 if (wParam <= INDIC_MAX) {
8819 vs.indicators[wParam].style = static_cast<int>(lParam);
8820 InvalidateStyleRedraw();
8822 break;
8824 case SCI_INDICGETSTYLE:
8825 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
8827 case SCI_INDICSETFORE:
8828 if (wParam <= INDIC_MAX) {
8829 vs.indicators[wParam].fore = ColourDesired(static_cast<long>(lParam));
8830 InvalidateStyleRedraw();
8832 break;
8834 case SCI_INDICGETFORE:
8835 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.AsLong() : 0;
8837 case SCI_INDICSETUNDER:
8838 if (wParam <= INDIC_MAX) {
8839 vs.indicators[wParam].under = lParam != 0;
8840 InvalidateStyleRedraw();
8842 break;
8844 case SCI_INDICGETUNDER:
8845 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
8847 case SCI_INDICSETALPHA:
8848 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8849 vs.indicators[wParam].fillAlpha = static_cast<int>(lParam);
8850 InvalidateStyleRedraw();
8852 break;
8854 case SCI_INDICGETALPHA:
8855 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
8857 case SCI_INDICSETOUTLINEALPHA:
8858 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8859 vs.indicators[wParam].outlineAlpha = static_cast<int>(lParam);
8860 InvalidateStyleRedraw();
8862 break;
8864 case SCI_INDICGETOUTLINEALPHA:
8865 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
8867 case SCI_SETINDICATORCURRENT:
8868 pdoc->decorations.SetCurrentIndicator(static_cast<int>(wParam));
8869 break;
8870 case SCI_GETINDICATORCURRENT:
8871 return pdoc->decorations.GetCurrentIndicator();
8872 case SCI_SETINDICATORVALUE:
8873 pdoc->decorations.SetCurrentValue(static_cast<int>(wParam));
8874 break;
8875 case SCI_GETINDICATORVALUE:
8876 return pdoc->decorations.GetCurrentValue();
8878 case SCI_INDICATORFILLRANGE:
8879 pdoc->DecorationFillRange(static_cast<int>(wParam), pdoc->decorations.GetCurrentValue(), static_cast<int>(lParam));
8880 break;
8882 case SCI_INDICATORCLEARRANGE:
8883 pdoc->DecorationFillRange(static_cast<int>(wParam), 0, static_cast<int>(lParam));
8884 break;
8886 case SCI_INDICATORALLONFOR:
8887 return pdoc->decorations.AllOnFor(static_cast<int>(wParam));
8889 case SCI_INDICATORVALUEAT:
8890 return pdoc->decorations.ValueAt(static_cast<int>(wParam), static_cast<int>(lParam));
8892 case SCI_INDICATORSTART:
8893 return pdoc->decorations.Start(static_cast<int>(wParam), static_cast<int>(lParam));
8895 case SCI_INDICATOREND:
8896 return pdoc->decorations.End(static_cast<int>(wParam), static_cast<int>(lParam));
8898 case SCI_LINEDOWN:
8899 case SCI_LINEDOWNEXTEND:
8900 case SCI_PARADOWN:
8901 case SCI_PARADOWNEXTEND:
8902 case SCI_LINEUP:
8903 case SCI_LINEUPEXTEND:
8904 case SCI_PARAUP:
8905 case SCI_PARAUPEXTEND:
8906 case SCI_CHARLEFT:
8907 case SCI_CHARLEFTEXTEND:
8908 case SCI_CHARRIGHT:
8909 case SCI_CHARRIGHTEXTEND:
8910 case SCI_WORDLEFT:
8911 case SCI_WORDLEFTEXTEND:
8912 case SCI_WORDRIGHT:
8913 case SCI_WORDRIGHTEXTEND:
8914 case SCI_WORDLEFTEND:
8915 case SCI_WORDLEFTENDEXTEND:
8916 case SCI_WORDRIGHTEND:
8917 case SCI_WORDRIGHTENDEXTEND:
8918 case SCI_HOME:
8919 case SCI_HOMEEXTEND:
8920 case SCI_LINEEND:
8921 case SCI_LINEENDEXTEND:
8922 case SCI_HOMEWRAP:
8923 case SCI_HOMEWRAPEXTEND:
8924 case SCI_LINEENDWRAP:
8925 case SCI_LINEENDWRAPEXTEND:
8926 case SCI_DOCUMENTSTART:
8927 case SCI_DOCUMENTSTARTEXTEND:
8928 case SCI_DOCUMENTEND:
8929 case SCI_DOCUMENTENDEXTEND:
8930 case SCI_SCROLLTOSTART:
8931 case SCI_SCROLLTOEND:
8933 case SCI_STUTTEREDPAGEUP:
8934 case SCI_STUTTEREDPAGEUPEXTEND:
8935 case SCI_STUTTEREDPAGEDOWN:
8936 case SCI_STUTTEREDPAGEDOWNEXTEND:
8938 case SCI_PAGEUP:
8939 case SCI_PAGEUPEXTEND:
8940 case SCI_PAGEDOWN:
8941 case SCI_PAGEDOWNEXTEND:
8942 case SCI_EDITTOGGLEOVERTYPE:
8943 case SCI_CANCEL:
8944 case SCI_DELETEBACK:
8945 case SCI_TAB:
8946 case SCI_BACKTAB:
8947 case SCI_NEWLINE:
8948 case SCI_FORMFEED:
8949 case SCI_VCHOME:
8950 case SCI_VCHOMEEXTEND:
8951 case SCI_VCHOMEWRAP:
8952 case SCI_VCHOMEWRAPEXTEND:
8953 case SCI_VCHOMEDISPLAY:
8954 case SCI_VCHOMEDISPLAYEXTEND:
8955 case SCI_ZOOMIN:
8956 case SCI_ZOOMOUT:
8957 case SCI_DELWORDLEFT:
8958 case SCI_DELWORDRIGHT:
8959 case SCI_DELWORDRIGHTEND:
8960 case SCI_DELLINELEFT:
8961 case SCI_DELLINERIGHT:
8962 case SCI_LINECOPY:
8963 case SCI_LINECUT:
8964 case SCI_LINEDELETE:
8965 case SCI_LINETRANSPOSE:
8966 case SCI_LINEDUPLICATE:
8967 case SCI_LOWERCASE:
8968 case SCI_UPPERCASE:
8969 case SCI_LINESCROLLDOWN:
8970 case SCI_LINESCROLLUP:
8971 case SCI_WORDPARTLEFT:
8972 case SCI_WORDPARTLEFTEXTEND:
8973 case SCI_WORDPARTRIGHT:
8974 case SCI_WORDPARTRIGHTEXTEND:
8975 case SCI_DELETEBACKNOTLINE:
8976 case SCI_HOMEDISPLAY:
8977 case SCI_HOMEDISPLAYEXTEND:
8978 case SCI_LINEENDDISPLAY:
8979 case SCI_LINEENDDISPLAYEXTEND:
8980 case SCI_LINEDOWNRECTEXTEND:
8981 case SCI_LINEUPRECTEXTEND:
8982 case SCI_CHARLEFTRECTEXTEND:
8983 case SCI_CHARRIGHTRECTEXTEND:
8984 case SCI_HOMERECTEXTEND:
8985 case SCI_VCHOMERECTEXTEND:
8986 case SCI_LINEENDRECTEXTEND:
8987 case SCI_PAGEUPRECTEXTEND:
8988 case SCI_PAGEDOWNRECTEXTEND:
8989 case SCI_SELECTIONDUPLICATE:
8990 return KeyCommand(iMessage);
8992 case SCI_BRACEHIGHLIGHT:
8993 SetBraceHighlight(static_cast<int>(wParam), static_cast<int>(lParam), STYLE_BRACELIGHT);
8994 break;
8996 case SCI_BRACEHIGHLIGHTINDICATOR:
8997 if (lParam >= 0 && lParam <= INDIC_MAX) {
8998 vs.braceHighlightIndicatorSet = wParam != 0;
8999 vs.braceHighlightIndicator = static_cast<int>(lParam);
9001 break;
9003 case SCI_BRACEBADLIGHT:
9004 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
9005 break;
9007 case SCI_BRACEBADLIGHTINDICATOR:
9008 if (lParam >= 0 && lParam <= INDIC_MAX) {
9009 vs.braceBadLightIndicatorSet = wParam != 0;
9010 vs.braceBadLightIndicator = static_cast<int>(lParam);
9012 break;
9014 case SCI_BRACEMATCH:
9015 // wParam is position of char to find brace for,
9016 // lParam is maximum amount of text to restyle to find it
9017 return pdoc->BraceMatch(static_cast<int>(wParam), static_cast<int>(lParam));
9019 case SCI_GETVIEWEOL:
9020 return vs.viewEOL;
9022 case SCI_SETVIEWEOL:
9023 vs.viewEOL = wParam != 0;
9024 InvalidateStyleRedraw();
9025 break;
9027 case SCI_SETZOOM:
9028 vs.zoomLevel = static_cast<int>(wParam);
9029 InvalidateStyleRedraw();
9030 NotifyZoom();
9031 break;
9033 case SCI_GETZOOM:
9034 return vs.zoomLevel;
9036 case SCI_GETEDGECOLUMN:
9037 return vs.theEdge;
9039 case SCI_SETEDGECOLUMN:
9040 vs.theEdge = static_cast<int>(wParam);
9041 InvalidateStyleRedraw();
9042 break;
9044 case SCI_GETEDGEMODE:
9045 return vs.edgeState;
9047 case SCI_SETEDGEMODE:
9048 vs.edgeState = static_cast<int>(wParam);
9049 InvalidateStyleRedraw();
9050 break;
9052 case SCI_GETEDGECOLOUR:
9053 return vs.edgecolour.AsLong();
9055 case SCI_SETEDGECOLOUR:
9056 vs.edgecolour = ColourDesired(static_cast<long>(wParam));
9057 InvalidateStyleRedraw();
9058 break;
9060 case SCI_GETDOCPOINTER:
9061 return reinterpret_cast<sptr_t>(pdoc);
9063 case SCI_SETDOCPOINTER:
9064 CancelModes();
9065 SetDocPointer(reinterpret_cast<Document *>(lParam));
9066 return 0;
9068 case SCI_CREATEDOCUMENT: {
9069 Document *doc = new Document();
9070 doc->AddRef();
9071 return reinterpret_cast<sptr_t>(doc);
9074 case SCI_ADDREFDOCUMENT:
9075 (reinterpret_cast<Document *>(lParam))->AddRef();
9076 break;
9078 case SCI_RELEASEDOCUMENT:
9079 (reinterpret_cast<Document *>(lParam))->Release();
9080 break;
9082 case SCI_CREATELOADER: {
9083 Document *doc = new Document();
9084 doc->AddRef();
9085 doc->Allocate(static_cast<int>(wParam));
9086 doc->SetUndoCollection(false);
9087 return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
9090 case SCI_SETMODEVENTMASK:
9091 modEventMask = static_cast<int>(wParam);
9092 return 0;
9094 case SCI_GETMODEVENTMASK:
9095 return modEventMask;
9097 case SCI_CONVERTEOLS:
9098 pdoc->ConvertLineEnds(static_cast<int>(wParam));
9099 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
9100 return 0;
9102 case SCI_SETLENGTHFORENCODE:
9103 lengthForEncode = static_cast<int>(wParam);
9104 return 0;
9106 case SCI_SELECTIONISRECTANGLE:
9107 return sel.selType == Selection::selRectangle ? 1 : 0;
9109 case SCI_SETSELECTIONMODE: {
9110 switch (wParam) {
9111 case SC_SEL_STREAM:
9112 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
9113 sel.selType = Selection::selStream;
9114 break;
9115 case SC_SEL_RECTANGLE:
9116 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
9117 sel.selType = Selection::selRectangle;
9118 break;
9119 case SC_SEL_LINES:
9120 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
9121 sel.selType = Selection::selLines;
9122 break;
9123 case SC_SEL_THIN:
9124 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
9125 sel.selType = Selection::selThin;
9126 break;
9127 default:
9128 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
9129 sel.selType = Selection::selStream;
9131 InvalidateSelection(sel.RangeMain(), true);
9132 break;
9134 case SCI_GETSELECTIONMODE:
9135 switch (sel.selType) {
9136 case Selection::selStream:
9137 return SC_SEL_STREAM;
9138 case Selection::selRectangle:
9139 return SC_SEL_RECTANGLE;
9140 case Selection::selLines:
9141 return SC_SEL_LINES;
9142 case Selection::selThin:
9143 return SC_SEL_THIN;
9144 default: // ?!
9145 return SC_SEL_STREAM;
9147 case SCI_GETLINESELSTARTPOSITION:
9148 case SCI_GETLINESELENDPOSITION: {
9149 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(static_cast<int>(wParam))),
9150 SelectionPosition(pdoc->LineEnd(static_cast<int>(wParam))));
9151 for (size_t r=0; r<sel.Count(); r++) {
9152 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
9153 if (portion.start.IsValid()) {
9154 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
9157 return INVALID_POSITION;
9160 case SCI_SETOVERTYPE:
9161 inOverstrike = wParam != 0;
9162 break;
9164 case SCI_GETOVERTYPE:
9165 return inOverstrike ? 1 : 0;
9167 case SCI_SETFOCUS:
9168 SetFocusState(wParam != 0);
9169 break;
9171 case SCI_GETFOCUS:
9172 return hasFocus;
9174 case SCI_SETSTATUS:
9175 errorStatus = static_cast<int>(wParam);
9176 break;
9178 case SCI_GETSTATUS:
9179 return errorStatus;
9181 case SCI_SETMOUSEDOWNCAPTURES:
9182 mouseDownCaptures = wParam != 0;
9183 break;
9185 case SCI_GETMOUSEDOWNCAPTURES:
9186 return mouseDownCaptures;
9188 case SCI_SETCURSOR:
9189 cursorMode = static_cast<int>(wParam);
9190 DisplayCursor(Window::cursorText);
9191 break;
9193 case SCI_GETCURSOR:
9194 return cursorMode;
9196 case SCI_SETCONTROLCHARSYMBOL:
9197 vs.controlCharSymbol = static_cast<int>(wParam);
9198 InvalidateStyleRedraw();
9199 break;
9201 case SCI_GETCONTROLCHARSYMBOL:
9202 return vs.controlCharSymbol;
9204 case SCI_SETREPRESENTATION:
9205 reprs.SetRepresentation(reinterpret_cast<const char *>(wParam), CharPtrFromSPtr(lParam));
9206 break;
9208 case SCI_GETREPRESENTATION: {
9209 const Representation *repr = reprs.RepresentationFromCharacter(
9210 reinterpret_cast<const char *>(wParam), UTF8MaxBytes);
9211 if (repr) {
9212 return StringResult(lParam, repr->stringRep.c_str());
9214 return 0;
9217 case SCI_CLEARREPRESENTATION:
9218 reprs.ClearRepresentation(reinterpret_cast<const char *>(wParam));
9219 break;
9221 case SCI_STARTRECORD:
9222 recordingMacro = true;
9223 return 0;
9225 case SCI_STOPRECORD:
9226 recordingMacro = false;
9227 return 0;
9229 case SCI_MOVECARETINSIDEVIEW:
9230 MoveCaretInsideView();
9231 break;
9233 case SCI_SETFOLDMARGINCOLOUR:
9234 vs.foldmarginColour = ColourOptional(wParam, lParam);
9235 InvalidateStyleRedraw();
9236 break;
9238 case SCI_SETFOLDMARGINHICOLOUR:
9239 vs.foldmarginHighlightColour = ColourOptional(wParam, lParam);
9240 InvalidateStyleRedraw();
9241 break;
9243 case SCI_SETHOTSPOTACTIVEFORE:
9244 vs.hotspotColours.fore = ColourOptional(wParam, lParam);
9245 InvalidateStyleRedraw();
9246 break;
9248 case SCI_GETHOTSPOTACTIVEFORE:
9249 return vs.hotspotColours.fore.AsLong();
9251 case SCI_SETHOTSPOTACTIVEBACK:
9252 vs.hotspotColours.back = ColourOptional(wParam, lParam);
9253 InvalidateStyleRedraw();
9254 break;
9256 case SCI_GETHOTSPOTACTIVEBACK:
9257 return vs.hotspotColours.back.AsLong();
9259 case SCI_SETHOTSPOTACTIVEUNDERLINE:
9260 vs.hotspotUnderline = wParam != 0;
9261 InvalidateStyleRedraw();
9262 break;
9264 case SCI_GETHOTSPOTACTIVEUNDERLINE:
9265 return vs.hotspotUnderline ? 1 : 0;
9267 case SCI_SETHOTSPOTSINGLELINE:
9268 vs.hotspotSingleLine = wParam != 0;
9269 InvalidateStyleRedraw();
9270 break;
9272 case SCI_GETHOTSPOTSINGLELINE:
9273 return vs.hotspotSingleLine ? 1 : 0;
9275 case SCI_SETPASTECONVERTENDINGS:
9276 convertPastes = wParam != 0;
9277 break;
9279 case SCI_GETPASTECONVERTENDINGS:
9280 return convertPastes ? 1 : 0;
9282 case SCI_GETCHARACTERPOINTER:
9283 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
9285 case SCI_GETRANGEPOINTER:
9286 return reinterpret_cast<sptr_t>(pdoc->RangePointer(static_cast<int>(wParam), static_cast<int>(lParam)));
9288 case SCI_GETGAPPOSITION:
9289 return pdoc->GapPosition();
9291 case SCI_SETEXTRAASCENT:
9292 vs.extraAscent = static_cast<int>(wParam);
9293 InvalidateStyleRedraw();
9294 break;
9296 case SCI_GETEXTRAASCENT:
9297 return vs.extraAscent;
9299 case SCI_SETEXTRADESCENT:
9300 vs.extraDescent = static_cast<int>(wParam);
9301 InvalidateStyleRedraw();
9302 break;
9304 case SCI_GETEXTRADESCENT:
9305 return vs.extraDescent;
9307 case SCI_MARGINSETSTYLEOFFSET:
9308 vs.marginStyleOffset = static_cast<int>(wParam);
9309 InvalidateStyleRedraw();
9310 break;
9312 case SCI_MARGINGETSTYLEOFFSET:
9313 return vs.marginStyleOffset;
9315 case SCI_SETMARGINOPTIONS:
9316 marginOptions = static_cast<int>(wParam);
9317 break;
9319 case SCI_GETMARGINOPTIONS:
9320 return marginOptions;
9322 case SCI_MARGINSETTEXT:
9323 pdoc->MarginSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
9324 break;
9326 case SCI_MARGINGETTEXT: {
9327 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
9328 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
9331 case SCI_MARGINSETSTYLE:
9332 pdoc->MarginSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
9333 break;
9335 case SCI_MARGINGETSTYLE: {
9336 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
9337 return st.style;
9340 case SCI_MARGINSETSTYLES:
9341 pdoc->MarginSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
9342 break;
9344 case SCI_MARGINGETSTYLES: {
9345 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
9346 return BytesResult(lParam, st.styles, st.length);
9349 case SCI_MARGINTEXTCLEARALL:
9350 pdoc->MarginClearAll();
9351 break;
9353 case SCI_ANNOTATIONSETTEXT:
9354 pdoc->AnnotationSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
9355 break;
9357 case SCI_ANNOTATIONGETTEXT: {
9358 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
9359 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
9362 case SCI_ANNOTATIONGETSTYLE: {
9363 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
9364 return st.style;
9367 case SCI_ANNOTATIONSETSTYLE:
9368 pdoc->AnnotationSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
9369 break;
9371 case SCI_ANNOTATIONSETSTYLES:
9372 pdoc->AnnotationSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
9373 break;
9375 case SCI_ANNOTATIONGETSTYLES: {
9376 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
9377 return BytesResult(lParam, st.styles, st.length);
9380 case SCI_ANNOTATIONGETLINES:
9381 return pdoc->AnnotationLines(static_cast<int>(wParam));
9383 case SCI_ANNOTATIONCLEARALL:
9384 pdoc->AnnotationClearAll();
9385 break;
9387 case SCI_ANNOTATIONSETVISIBLE:
9388 SetAnnotationVisible(static_cast<int>(wParam));
9389 break;
9391 case SCI_ANNOTATIONGETVISIBLE:
9392 return vs.annotationVisible;
9394 case SCI_ANNOTATIONSETSTYLEOFFSET:
9395 vs.annotationStyleOffset = static_cast<int>(wParam);
9396 InvalidateStyleRedraw();
9397 break;
9399 case SCI_ANNOTATIONGETSTYLEOFFSET:
9400 return vs.annotationStyleOffset;
9402 case SCI_RELEASEALLEXTENDEDSTYLES:
9403 vs.ReleaseAllExtendedStyles();
9404 break;
9406 case SCI_ALLOCATEEXTENDEDSTYLES:
9407 return vs.AllocateExtendedStyles(static_cast<int>(wParam));
9409 case SCI_ADDUNDOACTION:
9410 pdoc->AddUndoAction(static_cast<int>(wParam), lParam & UNDO_MAY_COALESCE);
9411 break;
9413 case SCI_SETMOUSESELECTIONRECTANGULARSWITCH:
9414 mouseSelectionRectangularSwitch = wParam != 0;
9415 break;
9417 case SCI_GETMOUSESELECTIONRECTANGULARSWITCH:
9418 return mouseSelectionRectangularSwitch;
9420 case SCI_SETMULTIPLESELECTION:
9421 multipleSelection = wParam != 0;
9422 InvalidateCaret();
9423 break;
9425 case SCI_GETMULTIPLESELECTION:
9426 return multipleSelection;
9428 case SCI_SETADDITIONALSELECTIONTYPING:
9429 additionalSelectionTyping = wParam != 0;
9430 InvalidateCaret();
9431 break;
9433 case SCI_GETADDITIONALSELECTIONTYPING:
9434 return additionalSelectionTyping;
9436 case SCI_SETMULTIPASTE:
9437 multiPasteMode = static_cast<int>(wParam);
9438 break;
9440 case SCI_GETMULTIPASTE:
9441 return multiPasteMode;
9443 case SCI_SETADDITIONALCARETSBLINK:
9444 additionalCaretsBlink = wParam != 0;
9445 InvalidateCaret();
9446 break;
9448 case SCI_GETADDITIONALCARETSBLINK:
9449 return additionalCaretsBlink;
9451 case SCI_SETADDITIONALCARETSVISIBLE:
9452 additionalCaretsVisible = wParam != 0;
9453 InvalidateCaret();
9454 break;
9456 case SCI_GETADDITIONALCARETSVISIBLE:
9457 return additionalCaretsVisible;
9459 case SCI_GETSELECTIONS:
9460 return sel.Count();
9462 case SCI_GETSELECTIONEMPTY:
9463 return sel.Empty();
9465 case SCI_CLEARSELECTIONS:
9466 sel.Clear();
9467 Redraw();
9468 break;
9470 case SCI_SETSELECTION:
9471 sel.SetSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
9472 Redraw();
9473 break;
9475 case SCI_ADDSELECTION:
9476 sel.AddSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
9477 Redraw();
9478 break;
9480 case SCI_DROPSELECTIONN:
9481 sel.DropSelection(static_cast<int>(wParam));
9482 Redraw();
9483 break;
9485 case SCI_SETMAINSELECTION:
9486 sel.SetMain(static_cast<int>(wParam));
9487 Redraw();
9488 break;
9490 case SCI_GETMAINSELECTION:
9491 return sel.Main();
9493 case SCI_SETSELECTIONNCARET:
9494 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
9495 Redraw();
9496 break;
9498 case SCI_GETSELECTIONNCARET:
9499 return sel.Range(wParam).caret.Position();
9501 case SCI_SETSELECTIONNANCHOR:
9502 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
9503 Redraw();
9504 break;
9505 case SCI_GETSELECTIONNANCHOR:
9506 return sel.Range(wParam).anchor.Position();
9508 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
9509 sel.Range(wParam).caret.SetVirtualSpace(static_cast<int>(lParam));
9510 Redraw();
9511 break;
9513 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
9514 return sel.Range(wParam).caret.VirtualSpace();
9516 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
9517 sel.Range(wParam).anchor.SetVirtualSpace(static_cast<int>(lParam));
9518 Redraw();
9519 break;
9521 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
9522 return sel.Range(wParam).anchor.VirtualSpace();
9524 case SCI_SETSELECTIONNSTART:
9525 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
9526 Redraw();
9527 break;
9529 case SCI_GETSELECTIONNSTART:
9530 return sel.Range(wParam).Start().Position();
9532 case SCI_SETSELECTIONNEND:
9533 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
9534 Redraw();
9535 break;
9537 case SCI_GETSELECTIONNEND:
9538 return sel.Range(wParam).End().Position();
9540 case SCI_SETRECTANGULARSELECTIONCARET:
9541 if (!sel.IsRectangular())
9542 sel.Clear();
9543 sel.selType = Selection::selRectangle;
9544 sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
9545 SetRectangularRange();
9546 Redraw();
9547 break;
9549 case SCI_GETRECTANGULARSELECTIONCARET:
9550 return sel.Rectangular().caret.Position();
9552 case SCI_SETRECTANGULARSELECTIONANCHOR:
9553 if (!sel.IsRectangular())
9554 sel.Clear();
9555 sel.selType = Selection::selRectangle;
9556 sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
9557 SetRectangularRange();
9558 Redraw();
9559 break;
9561 case SCI_GETRECTANGULARSELECTIONANCHOR:
9562 return sel.Rectangular().anchor.Position();
9564 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9565 if (!sel.IsRectangular())
9566 sel.Clear();
9567 sel.selType = Selection::selRectangle;
9568 sel.Rectangular().caret.SetVirtualSpace(static_cast<int>(wParam));
9569 SetRectangularRange();
9570 Redraw();
9571 break;
9573 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9574 return sel.Rectangular().caret.VirtualSpace();
9576 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9577 if (!sel.IsRectangular())
9578 sel.Clear();
9579 sel.selType = Selection::selRectangle;
9580 sel.Rectangular().anchor.SetVirtualSpace(static_cast<int>(wParam));
9581 SetRectangularRange();
9582 Redraw();
9583 break;
9585 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9586 return sel.Rectangular().anchor.VirtualSpace();
9588 case SCI_SETVIRTUALSPACEOPTIONS:
9589 virtualSpaceOptions = static_cast<int>(wParam);
9590 break;
9592 case SCI_GETVIRTUALSPACEOPTIONS:
9593 return virtualSpaceOptions;
9595 case SCI_SETADDITIONALSELFORE:
9596 vs.selAdditionalForeground = ColourDesired(static_cast<long>(wParam));
9597 InvalidateStyleRedraw();
9598 break;
9600 case SCI_SETADDITIONALSELBACK:
9601 vs.selAdditionalBackground = ColourDesired(static_cast<long>(wParam));
9602 InvalidateStyleRedraw();
9603 break;
9605 case SCI_SETADDITIONALSELALPHA:
9606 vs.selAdditionalAlpha = static_cast<int>(wParam);
9607 InvalidateStyleRedraw();
9608 break;
9610 case SCI_GETADDITIONALSELALPHA:
9611 return vs.selAdditionalAlpha;
9613 case SCI_SETADDITIONALCARETFORE:
9614 vs.additionalCaretColour = ColourDesired(static_cast<long>(wParam));
9615 InvalidateStyleRedraw();
9616 break;
9618 case SCI_GETADDITIONALCARETFORE:
9619 return vs.additionalCaretColour.AsLong();
9621 case SCI_ROTATESELECTION:
9622 sel.RotateMain();
9623 InvalidateSelection(sel.RangeMain(), true);
9624 break;
9626 case SCI_SWAPMAINANCHORCARET:
9627 InvalidateSelection(sel.RangeMain());
9628 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
9629 break;
9631 case SCI_CHANGELEXERSTATE:
9632 pdoc->ChangeLexerState(static_cast<int>(wParam), static_cast<int>(lParam));
9633 break;
9635 case SCI_SETIDENTIFIER:
9636 SetCtrlID(static_cast<int>(wParam));
9637 break;
9639 case SCI_GETIDENTIFIER:
9640 return GetCtrlID();
9642 case SCI_SETTECHNOLOGY:
9643 // No action by default
9644 break;
9646 case SCI_GETTECHNOLOGY:
9647 return technology;
9649 case SCI_COUNTCHARACTERS:
9650 return pdoc->CountCharacters(static_cast<int>(wParam), static_cast<int>(lParam));
9652 default:
9653 return DefWndProc(iMessage, wParam, lParam);
9655 //Platform::DebugPrintf("end wnd proc\n");
9656 return 0l;