1 // Scintilla source code edit control
3 ** Main code for the edit control.
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.
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"
33 #include "Indicator.h"
35 #include "LineMarker.h"
37 #include "ViewStyle.h"
38 #include "CharClassify.h"
39 #include "Decoration.h"
40 #include "CaseFolder.h"
42 #include "UniConversion.h"
43 #include "Selection.h"
44 #include "PositionCache.h"
48 using namespace Scintilla
;
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
) {
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
) {
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;
83 active(false), on(false), period(500) {}
86 ticking(false), ticksToWait(0), tickerID(0) {}
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
]))
105 PrintParameters::PrintParameters() {
107 colourMode
= SC_PRINT_NORMAL
;
108 wrapState
= eWrapWord
;
115 technology
= SC_TECHNOLOGY_DEFAULT
;
116 scaleRGBAImage
= 100.0f
;
118 cursorMode
= SC_CURSORNORMAL
;
121 hideSelection
= false;
122 inOverstrike
= false;
123 drawOverstrikeCaret
= true;
125 mouseDownCaptures
= true;
131 dwellDelay
= SC_TIME_FOREVER
;
132 ticksToDwell
= SC_TIME_FOREVER
;
137 dropWentOutside
= false;
138 posDrag
= SelectionPosition(invalidPosition
);
139 posDrop
= SelectionPosition(invalidPosition
);
140 hotSpotClickPos
= INVALID_POSITION
;
141 selectionType
= selChar
;
145 originalAnchorPos
= 0;
146 wordSelectAnchorStartPos
= 0;
147 wordSelectAnchorEndPos
= 0;
148 wordSelectInitialCaretPos
= -1;
150 primarySelection
= true;
152 caretXPolicy
= CARET_SLOP
| CARET_EVEN
;
155 caretYPolicy
= CARET_EVEN
;
165 horizontalScrollBarVisible
= true;
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
;
183 pixmapSelPattern
= 0;
184 pixmapSelPatternOffset1
= 0;
185 pixmapIndentGuide
= 0;
186 pixmapIndentGuideHighlight
= 0;
195 lengthForEncode
= -1;
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();
213 pdoc
->AddWatcher(this, 0);
215 recordingMacro
= false;
219 wrapWidth
= LineLayout::wrapWidthInfinite
;
221 convertPastes
= true;
223 hotspot
= Range(invalidPosition
);
225 llc
.SetLevel(LineLayoutCache::llcCaret
);
226 posCache
.SetSize(0x400);
228 SetRepresentations();
232 pdoc
->RemoveWatcher(this, 0);
238 void Editor::Finalise() {
243 void Editor::SetRepresentations() {
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
]);
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 };
280 sprintf(hexits
, "x%2X", k
);
281 reprs
.SetRepresentation(hiByte
, hexits
);
286 void Editor::DropGraphics(bool freeObjects
) {
290 delete pixmapSelMargin
;
292 delete pixmapSelPattern
;
293 pixmapSelPattern
= 0;
294 delete pixmapSelPatternOffset1
;
295 pixmapSelPatternOffset1
= 0;
296 delete pixmapIndentGuide
;
297 pixmapIndentGuide
= 0;
298 delete pixmapIndentGuideHighlight
;
299 pixmapIndentGuideHighlight
= 0;
302 pixmapLine
->Release();
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() {
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() {
333 vs
.technology
= technology
;
336 llc
.Invalidate(LineLayout::llInvalid
);
340 void Editor::InvalidateStyleRedraw() {
342 InvalidateStyleData();
346 void Editor::RefreshStyleData() {
349 AutoSurface
surface(this);
351 vs
.Refresh(*surface
, pdoc
->tabInChars
);
354 SetRectangularRange();
358 Point
Editor::GetVisibleOriginInMain() {
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
;
369 ptDocument
.x
+= xOffset
;
370 ptDocument
.y
+= topLine
* vs
.lineHeight
;
375 int Editor::TopLineOfMain() const {
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
;
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;
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();
417 retVal
-= LinesOnScreen();
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
)) {
443 * Convenience class to ensure LineLayout objects are always disposed.
445 class AutoLineLayout
{
446 LineLayoutCache
&llc
;
448 AutoLineLayout
&operator=(const AutoLineLayout
&);
450 AutoLineLayout(LineLayoutCache
&llc_
, LineLayout
*ll_
) : llc(llc_
), ll(ll_
) {}
455 LineLayout
*operator->() const {
458 operator LineLayout
*() const {
461 void Set(LineLayout
*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());
473 // If not at end of line then set offset to 0
474 if (!pdoc
->IsLineEndPosition(sp
.Position()))
475 sp
.SetVirtualSpace(0);
480 Point
Editor::LocationFromPosition(SelectionPosition pos
) {
483 if (pos
.Position() == INVALID_POSITION
)
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
));
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
;
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
) {
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
);
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))
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
));
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));
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));
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
) {
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
));
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
);
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();
675 PRectangle rcSelMargin
= GetClientRectangle();
676 rcSelMargin
.right
= rcSelMargin
.left
+ vs
.fixedColumnWidth
;
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;
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
;
693 rcSelMargin
.bottom
= rcLine
.bottom
;
694 if (rcSelMargin
.Empty())
697 if (wMargin
.GetID()) {
698 Point ptOrigin
= GetVisibleOriginInMain();
699 rcSelMargin
.Move(-ptOrigin
.x
, -ptOrigin
.y
);
700 wMargin
.InvalidateRectangle(rcSelMargin
);
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();
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
);
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 {
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
) {
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
);
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
);
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())));
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();
822 if (highlightDelimiter
.NeedsDrawing(currentLine
)) {
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()) {
841 SelectionRange(SelectionPosition(currentPos_
), sel
.Rectangular().anchor
);
842 SetRectangularRange();
845 SelectionRange(SelectionPosition(currentPos_
), sel
.RangeMain().anchor
);
849 if (highlightDelimiter
.NeedsDrawing(currentLine
)) {
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
);
866 sel
.RangeMain() = rangeNew
;
867 SetRectangularRange();
870 if (highlightDelimiter
.NeedsDrawing(currentLine
)) {
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()) {
887 for (int pos
= start
; pos
< end
; pos
++) {
888 if (vs
.styles
[pdoc
->StyleAt(pos
)].IsProtected())
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())) {
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()) {
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()))
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()))
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();
952 sel
.Rectangular() = rangeMain
;
954 if (selt
!= Selection::noSel
) {
957 if (selt
!= Selection::noSel
|| sel
.MoveExtends()) {
958 SetSelection(newPos
);
960 SetEmptySelection(newPos
);
962 ShowCaretAtCurrentPosition();
964 int currentLine
= pdoc
->LineFromPosition(newPos
.Position());
966 // In case in need of wrapping to ensure DisplayFromDoc works.
967 if (currentLine
>= wrapPending
.start
)
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);
980 if (highlightDelimiter
.NeedsDrawing(currentLine
)) {
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
)) {
997 int lineDisplay
= cs
.DisplayFromDoc(lineDoc
);
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
)));
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
1031 int linesToMove
= topLine
- topLineNew
;
1032 bool performBlit
= (abs(linesToMove
) <= 10) && (paintState
== notPainting
);
1033 willRedrawAll
= !performBlit
;
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()));
1040 // Perform redraw rather than scroll if many lines would be redrawn anyway.
1042 ScrollText(linesToMove
);
1046 willRedrawAll
= false;
1051 SetVerticalScrollPos();
1056 void Editor::ScrollText(int /* linesToMove */) {
1057 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
1061 void Editor::HorizontalScrollTo(int xPos
) {
1062 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
1065 if (!Wrapping() && (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
) {
1120 if (lineDelta
> 0 && selectionEnd
== pdoc
->LineStart(pdoc
->LinesTotal() - 1)) {
1121 SetSelection(pdoc
->MovePositionOutsideChar(selectionEnd
- 1, -1), selectionEnd
);
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
);
1135 SetSelection(pdoc
->MovePositionOutsideChar(selectionStart
- 1, -1), selectionEnd
);
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
);
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
)) {
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.
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();
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()) {
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
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;
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
);
1282 yMarginB
= yMarginT
;
1284 yMarginB
= linesOnScreen
- yMarginT
- 1;
1290 yMoveT
= Platform::Clamp(caretYSlop
* 3, 1, halfScreen
);
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
);
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
;
1320 if (!bStrict
&& !bJump
) {
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
1328 newXY
.topLine
= lineCaret
- linesOnScreen
+ 1;
1330 newXY
.topLine
= lineCaret
;
1333 } else { // Strict or going out of display
1335 // Always center caret
1336 newXY
.topLine
= lineCaret
- halfScreen
;
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());
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
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;
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
);
1379 xMarginL
= xMarginR
;
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
);
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
;
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
;
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
);
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
;
1425 (bJump
&& (pt
.x
< rcClient
.left
|| pt
.x
>= rcClient
.right
))) {
1426 // Strict or going out of display
1429 newXY
.xOffset
+= static_cast<int>(pt
.x
- rcClient
.left
- halfScreen
);
1431 // Put caret on right
1432 newXY
.xOffset
+= static_cast<int>(pt
.x
- rcClient
.right
+ 1);
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
1439 newXY
.xOffset
-= static_cast<int>(rcClient
.left
- pt
.x
);
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
);
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) {
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());
1499 SetHorizontalScrollPos();
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() {
1517 caret
.active
= true;
1521 caret
.active
= false;
1527 void Editor::DropCaret() {
1528 caret
.active
= false;
1532 void Editor::CaretSetPeriod(int period
) {
1533 if (caret
.period
!= period
) {
1534 caret
.period
= period
;
1540 void Editor::InvalidateCaret() {
1541 if (posDrag
.IsValid()) {
1542 InvalidateRange(posDrag
.Position(), posDrag
.Position() + 1);
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()) {
1569 bool Editor::WrapOneLine(Surface
*surface
, int lineToWrap
) {
1570 AutoLineLayout
ll(llc
, RetrieveLineLayout(lineToWrap
));
1571 int linesWrapped
= 1;
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;
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.
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
))
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
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());
1643 AutoSurface
surface(this);
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
);
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();
1667 SetTopLine(Platform::Clamp(goodTopLine
, 0, MaxScrollPos()));
1668 SetVerticalScrollPos();
1671 return wrapOccurred
;
1674 void Editor::LinesJoin() {
1675 if (!RangeContainsProtected(targetStart
, targetEnd
)) {
1677 bool prevNonWS
= true;
1678 for (int pos
= targetStart
; pos
< targetEnd
; pos
++) {
1679 if (pdoc
->IsPositionInLineEnd(pos
)) {
1680 targetEnd
-= pdoc
->LenChar(pos
);
1683 // Ensure at least one space separating previous lines
1684 const int lengthInserted
= pdoc
->InsertString(pos
, " ", 1);
1685 targetEnd
+= lengthInserted
;
1688 prevNonWS
= pdoc
->CharAt(pos
) != ' ';
1694 const char *Editor::StringFromEOLMode(int eolMode
) {
1695 if (eolMode
== SC_EOL_CRLF
) {
1697 } else if (eolMode
== SC_EOL_CR
) {
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
);
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
)),
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
;
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
]))
1748 if (!vs
.ValidStyle(styleOffset
+ st
.style
))
1754 static int WidthStyledText(Surface
*surface
, const ViewStyle
&vs
, int styleOffset
,
1755 const char *text
, const unsigned char *styles
, size_t len
) {
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
))
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;
1771 static int WidestLineWidth(Surface
*surface
, const ViewStyle
&vs
, int styleOffset
, const StyledText
&st
) {
1774 while (start
< st
.length
) {
1775 size_t lenLine
= st
.LineLength(start
);
1777 if (st
.multipleStyles
) {
1778 widthSubLine
= WidthStyledText(surface
, vs
, styleOffset
, st
.text
+ start
, st
.styles
+ start
, lenLine
);
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;
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
);
1803 while (i
< length
) {
1805 size_t style
= st
.styles
[i
+ start
];
1806 while (end
< length
-1 && st
.styles
[start
+end
+1] == style
)
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);
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)
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()) {
1842 PRectangle rcMargin
= GetClientRectangle();
1843 Point ptOrigin
= GetVisibleOriginInMain();
1844 rcMargin
.Move(0, -ptOrigin
.y
);
1846 rcMargin
.right
= static_cast<XYPOSITION
>(vs
.fixedColumnWidth
);
1848 if (!rc
.Intersects(rcMargin
))
1853 surface
= pixmapSelMargin
;
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
);
1884 ColourDesired colour
;
1885 switch (vs
.ms
[margin
].style
) {
1886 case SC_MARGIN_BACK
:
1887 colour
= vs
.styles
[STYLE_DEFAULT
].back
;
1889 case SC_MARGIN_FORE
:
1890 colour
= vs
.styles
[STYLE_DEFAULT
].fore
;
1893 colour
= vs
.styles
[STYLE_LINENUMBER
].back
;
1896 surface
->FillRectangle(rcSelMargin
, colour
);
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
)) {
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
,
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
);
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
) {
1957 if (levelNum
< levelNextNum
) {
1958 if (cs
.GetExpanded(lineDoc
)) {
1959 if (levelNum
== SC_FOLDLEVELBASE
)
1960 marks
|= 1 << SC_MARKNUM_FOLDEROPEN
;
1962 marks
|= 1 << folderOpenMid
;
1964 if (levelNum
== SC_FOLDLEVELBASE
)
1965 marks
|= 1 << SC_MARKNUM_FOLDER
;
1967 marks
|= 1 << folderEnd
;
1969 } else if (levelNum
> SC_FOLDLEVELBASE
) {
1970 marks
|= 1 << SC_MARKNUM_FOLDERSUB
;
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;
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
;
2011 marks
|= 1 << SC_MARKNUM_FOLDERTAIL
;
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
;
2027 marks
|= 1 << SC_MARKNUM_FOLDERTAIL
;
2030 marks
|= 1 << SC_MARKNUM_FOLDERSUB
;
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
) {
2045 char number
[100] = "";
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
,
2058 int state
= pdoc
->GetLineState(lineDoc
);
2059 sprintf(number
, "%0X", state
);
2062 PRectangle rcNumber
= rcMarker
;
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
) {
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
);
2092 for (int markBit
= 0; (markBit
< 32) && marks
; markBit
++) {
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
)) {
2100 tFold
= headWithTail
? LineMarker::headWithTail
: LineMarker::head
;
2102 if (cs
.GetExpanded(lineDoc
) || headWithTail
) {
2103 tFold
= LineMarker::body
;
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
);
2119 yposScreen
+= vs
.lineHeight
;
2124 PRectangle rcBlankMargin
= rcMargin
;
2125 rcBlankMargin
.left
= rcSelMargin
.right
;
2126 surface
->FillRectangle(rcBlankMargin
, vs
.styles
[STYLE_DEFAULT
].back
);
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
);
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
) {
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
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
)));
2205 allSame
= allSame
&& (ll
->styles
[numCharsInLine
] == styleByte
); // For eolFilled
2207 ll
->validity
= LineLayout::llPositions
;
2209 ll
->validity
= LineLayout::llInvalid
;
2212 ll
->validity
= LineLayout::llInvalid
;
2215 if (ll
->validity
== LineLayout::llInvalid
) {
2216 ll
->widthLine
= LineLayout::wrapWidthInfinite
;
2218 if (vstyle
.edgeState
== EDGE_BACKGROUND
) {
2219 ll
->edgeColumn
= pdoc
->FindColumn(line
, vstyle
.theEdge
);
2220 if (ll
->edgeColumn
>= posLineStart
) {
2221 ll
->edgeColumn
-= posLineStart
;
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
];
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
;
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
;
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
2309 if ((ll
->validity
== LineLayout::llPositions
) || (ll
->widthLine
!= width
)) {
2310 ll
->widthLine
= width
;
2311 if (width
== LineLayout::wrapWidthInfinite
) {
2313 } else if (width
> ll
->positions
[ll
->numCharsInLine
]) {
2314 // Simple common case where line does not need wrapping.
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
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
2341 // Calculate line start positions based upon width.
2342 int lastGoodBreak
= 0;
2343 int lastLineStart
= 0;
2344 XYACCUMULATOR startOffset
= 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
2351 lastGoodBreak
= pdoc
->MovePositionOutsideChar(p
+ posLineStart
, -1)
2354 if (lastGoodBreak
== lastLineStart
) {
2355 // Ensure at least one character on line.
2356 lastGoodBreak
= pdoc
->MovePositionOutsideChar(lastGoodBreak
+ posLineStart
+ 1, 1)
2360 lastLineStart
= lastGoodBreak
;
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;
2370 if (vstyle
.wrapState
== eWrapChar
) {
2371 lastGoodBreak
= pdoc
->MovePositionOutsideChar(p
+ posLineStart
, -1)
2373 p
= pdoc
->MovePositionOutsideChar(p
+ 1 + posLineStart
, 1) - posLineStart
;
2375 } else if ((vstyle
.wrapState
== eWrapWord
) && (ll
->styles
[p
] != ll
->styles
[p
- 1])) {
2377 } else if (IsSpaceOrTab(ll
->chars
[p
- 1]) && !IsSpaceOrTab(ll
->chars
[p
])) {
2385 ll
->validity
= LineLayout::llLines
;
2389 ColourDesired
Editor::SelectionBackground(const ViewStyle
&vsDraw
, bool main
) const {
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);
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
)) {
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
;
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};
2459 rel
.LineTo(xa
+ 2*w
/ 3, y
- dy
);
2461 rel
.LineTo(xa
+ 2*w
/ 3, y
+ dy
);
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...
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
;
2492 surface
->FillRectangle(rcCentral
, textFore
);
2493 PRectangle rcChar
= rcCChar
;
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;
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
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;
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();
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
);
2565 const Representation
*repr
= reprs
.RepresentationFromCharacter(ll
->chars
+ eolPos
, ll
->numCharsInLine
- eolPos
);
2567 ctrlChar
= repr
->stringRep
.c_str();
2568 eolPos
= ll
->numCharsInLine
;
2570 sprintf(hexits
, "x%2X", chEOL
);
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));
2582 surface
->FillRectangle(rcSegment
, textBack
);
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));
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
);
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));
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
);
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
;
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
)];
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
) {
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
);
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
);
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
);
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
];
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
;
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
;
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') {
2886 if (drawWhitespaceBackground
&&
2887 (!inIndentation
|| vsDraw
.viewWhitespace
== wsVisibleAlways
))
2888 textBack
= vsDraw
.whitespaceColours
.back
;
2891 inIndentation
= false;
2893 surface
->FillRectangle(rcSegment
, textBack
);
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
)) {
2904 ll
->positions
[cpos
+ ts
.start
] + xStart
- static_cast<XYPOSITION
>(subLineStart
),
2906 ll
->positions
[cpos
+ ts
.start
+ 1] + xStart
- static_cast<XYPOSITION
>(subLineStart
),
2908 surface
->FillRectangle(rcSpace
, vsDraw
.whitespaceColours
.back
);
2911 inIndentation
= false;
2916 } else if (rcSegment
.left
> rcLine
.right
) {
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
);
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
);
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
);
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') {
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
;
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));
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
);
3017 DrawTextBlob(surface
, vsDraw
, rcSegment
, ts
.representation
->stringRep
.c_str(), textBack
, textFore
, twoPhaseDraw
);
3021 // Normal text display
3022 if (vsDraw
.styles
[styleMain
].visible
) {
3024 surface
->DrawTextTransparent(rcSegment
, textFont
,
3025 rcSegment
.top
+ vsDraw
.maxAscent
, ll
->chars
+ ts
.start
,
3026 i
- ts
.start
+ 1, textFore
);
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
;
3046 ll
->positions
[cpos
+ ts
.start
] + xStart
- static_cast<XYPOSITION
>(subLineStart
),
3048 ll
->positions
[cpos
+ ts
.start
+ 1] + xStart
- static_cast<XYPOSITION
>(subLineStart
),
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
;
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
));
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
);
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
) {
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
)) {
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
;
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
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
)) {
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
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
);
3199 if (vsDraw
.maskInLine
) {
3200 int marksMasked
= pdoc
->GetMark(line
) & vsDraw
.maskInLine
;
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
);
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
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
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
,
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
);
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
)
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
3380 widthOverstrikeCaret
= vsDraw
.aveCharWidth
;
3381 } else if ((posCaret
.Position() - posLineStart
) >= ll
->numCharsInLine
) { // At end of line
3383 widthOverstrikeCaret
= vsDraw
.aveCharWidth
;
3385 widthOverstrikeCaret
= ll
->positions
[offset
+ 1] - ll
->positions
[offset
];
3387 if (widthOverstrikeCaret
< 3) // Make sure its visible
3388 widthOverstrikeCaret
= 3;
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
) {
3404 rcCaret
.left
= xposCaret
;
3405 if (!caretAtEOL
&& !caretAtEOF
&& (ll
->chars
[offset
] != '\t') && !(IsControlCharacter(ll
->chars
[offset
]))) {
3406 drawBlockCaret
= true;
3407 rcCaret
.right
= xposCaret
+ widthOverstrikeCaret
;
3409 rcCaret
.right
= xposCaret
+ vsDraw
.aveCharWidth
;
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
);
3420 surface
->FillRectangle(rcCaret
, caretColour
);
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);
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
);
3453 ypos
+= screenLinePaintFirst
* vs
.lineHeight
;
3454 int yposScreen
= screenLinePaintFirst
* vs
.lineHeight
;
3456 if (NotifyUpdateUI()) {
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()) {
3468 RefreshPixMaps(surfaceWindow
); // In case pixmaps invalidated by scrollbar change
3470 PLATFORM_ASSERT(pixmapSelPattern
->Initialised());
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");
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
));
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;
3513 if (rcArea
.right
> vs
.textStart
- leftTextOverlap
) {
3515 Surface
*surface
= surfaceWindow
;
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())
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
;
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.
3564 if (lineDoc
!= lineDocPrevious
) {
3566 ll
.Set(RetrieveLineLayout(lineDoc
));
3567 LayoutLine(lineDoc
, surface
, vs
, ll
, wrapWidth
);
3568 lineDocPrevious
= lineDoc
;
3570 //durLayout += et.Duration(true);
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
;
3598 surface
->FillRectangle(rcSpacer
, vs
.styles
[STYLE_DEFAULT
].back
);
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
);
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
;
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());
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
;
3690 return ColourDesired(0xff, 0xff, 0xff);
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
) {
3704 AutoSurface
surface(pfr
->hdc
, this, SC_TECHNOLOGY_DEFAULT
);
3707 AutoSurface
surfaceMeasure(pfr
->hdcTarget
, this, SC_TECHNOLOGY_DEFAULT
);
3708 if (!surfaceMeasure
) {
3712 // Can't use measurements cached for screen
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
;
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(
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
) {
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)) {
3848 sprintf(number
, "%d" lineNumberPrintSpace
, lineDoc
+ 1);
3849 PRectangle rcNumber
= rcLine
;
3850 rcNumber
.right
= rcNumber
.left
+ lineNumberWidth
;
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
);
3862 surface
->FlushCachedState();
3864 for (int iwl
= 0; iwl
< ll
.lines
; iwl
++) {
3865 if (ypos
+ vsPrint
.lineHeight
<= pfr
->rc
.bottom
) {
3866 if (visibleLine
>= 0) {
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
;
3875 if (iwl
== ll
.lines
- 1)
3876 nPrintPos
= pdoc
->LineStart(lineDoc
+ 1);
3878 nPrintPos
+= ll
.LineStart(iwl
+ 1) - ll
.LineStart(iwl
);
3885 // Clear cache so measurements are not used for screen
3891 int Editor::TextWidth(int style
, const char *text
) {
3893 AutoSurface
surface(this);
3895 return static_cast<int>(surface
->WidthText(vs
.styles
[style
].font
, text
, istrlen(text
)));
3901 // Empty method is overridden on GTK+ to show / hide scrollbars
3902 void Editor::ReconfigureScrollBars() {}
3904 void Editor::SetScrollBars() {
3907 int nMax
= MaxScrollPos();
3908 int nPage
= LinesOnScreen();
3909 bool modified
= ModifyScrollBars(nMax
+ nPage
- 1, nPage
);
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();
3922 if (!AbandonPaint())
3925 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
3928 void Editor::ChangeSize() {
3929 DropGraphics(false);
3932 PRectangle rcTextArea
= GetClientRectangle();
3933 rcTextArea
.left
= static_cast<XYPOSITION
>(vs
.textStart
);
3934 rcTextArea
.right
-= vs
.rightMarginWidth
;
3935 if (wrapWidth
!= rcTextArea
.Width()) {
3942 int Editor::InsertSpace(int position
, unsigned int spaces
) {
3944 std::string
spaceText(spaces
, ' ');
3945 const int lengthInserted
= pdoc
->InsertString(position
, spaceText
.c_str(), spaces
);
3946 position
+= lengthInserted
;
3951 void Editor::AddChar(char ch
) {
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
) {
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
) {
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();
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
4013 AutoSurface
surface(this);
4015 if (WrapOneLine(surface
, pdoc
->LineFromPosition(positionInsert
))) {
4017 SetVerticalScrollPos();
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
))) {
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.
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
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) |
4068 // A three-byte-character lead-byte not followed by two trail-bytes
4069 // represents itself.
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
);
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();
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
);
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
);
4139 InsertPaste(text
, len
);
4144 void Editor::ClearSelection(bool retainMultipleSelections
) {
4145 if (!sel
.IsRectangular() && !retainMultipleSelections
)
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();
4163 void Editor::ClearAll() {
4166 if (0 != pdoc
->Length()) {
4167 pdoc
->DeleteChars(0, pdoc
->Length());
4169 if (!pdoc
->IsReadOnly()) {
4171 pdoc
->AnnotationClearAll();
4172 pdoc
->MarginClearAll();
4177 SetVerticalScrollPos();
4178 InvalidateStyleRedraw();
4181 void Editor::ClearDocumentStyle() {
4182 Decoration
*deco
= pdoc
->decorations
.root
;
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());
4192 pdoc
->StartStyling(0, '\377');
4193 pdoc
->SetStyleFor(pdoc
->Length(), 0);
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()) {
4212 void Editor::PasteRectangular(SelectionPosition pos
, const char *ptr
, int len
) {
4213 if (pdoc
->IsReadOnly() || SelectionContainsProtected()) {
4217 sel
.RangeMain() = SelectionRange(pos
);
4218 int line
= pdoc
->LineFromPosition(sel
.MainCaret());
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]))
4226 for (int i
= 0; i
< len
; i
++) {
4227 if (IsEOLChar(ptr
[i
])) {
4228 if ((ptr
[i
] == '\r') || (!prevCr
))
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';
4246 const int lengthInserted
= pdoc
->InsertString(sel
.MainCaret(), ptr
+ i
, 1);
4247 sel
.RangeMain().caret
.Add(lengthInserted
);
4251 SetEmptySelection(pos
);
4254 bool Editor::CanPaste() {
4255 return !pdoc
->IsReadOnly() && !SelectionContainsProtected();
4258 void Editor::Clear() {
4259 // If multiple selections, don't delete EOLS
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()));
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
4281 sel
.Range(r
).ClearVirtualSpace();
4287 sel
.RemoveDuplicates();
4290 void Editor::SelectAll() {
4292 SetSelection(0, pdoc
->Length());
4296 void Editor::Undo() {
4297 if (pdoc
->CanUndo()) {
4299 int newPos
= pdoc
->Undo();
4301 SetEmptySelection(newPos
);
4302 EnsureCaretVisible();
4306 void Editor::Redo() {
4307 if (pdoc
->CanRedo()) {
4308 int newPos
= pdoc
->Redo();
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())
4326 if (sel
.IsRectangular())
4327 allowLineStartDeletion
= false;
4328 UndoGroup
ug(pdoc
, (sel
.Count() > 1) || !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());
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
);
4350 pdoc
->DelCharBack(sel
.Range(r
).caret
.Position());
4355 sel
.Range(r
).ClearVirtualSpace();
4358 ThinRectangularRange();
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
) {
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
;
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
;
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
;
4411 void Editor::NotifySavePoint(bool isSavePoint
) {
4412 SCNotification scn
= {};
4414 scn
.nmhdr
.code
= SCN_SAVEPOINTREACHED
;
4416 scn
.nmhdr
.code
= SCN_SAVEPOINTLEFT
;
4421 void Editor::NotifyModifyAttempt() {
4422 SCNotification scn
= {};
4423 scn
.nmhdr
.code
= SCN_MODIFYATTEMPTRO
;
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
;
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
;
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
;
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
;
4472 void Editor::NotifyHotSpotReleaseClick(int position
, bool shift
, bool ctrl
, bool alt
) {
4473 NotifyHotSpotReleaseClick(position
, ModifierFlags(shift
, ctrl
, alt
));
4476 bool Editor::NotifyUpdateUI() {
4478 SCNotification scn
= {};
4479 scn
.nmhdr
.code
= SCN_UPDATEUI
;
4480 scn
.updated
= needUpdateUI
;
4488 void Editor::NotifyPainted() {
4489 SCNotification scn
= {};
4490 scn
.nmhdr
.code
= SCN_PAINTED
;
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
;
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
);
4527 int levelClick
= pdoc
->GetLevel(lineClick
);
4528 if (levelClick
& SC_FOLDLEVELHEADERFLAG
) {
4530 // Ensure all children visible
4531 FoldExpand(lineClick
, SC_FOLDACTION_EXPAND
, levelClick
);
4533 FoldExpand(lineClick
, SC_FOLDACTION_TOGGLE
, levelClick
);
4536 FoldLine(lineClick
, SC_FOLDACTION_TOGGLE
);
4542 SCNotification scn
= {};
4543 scn
.nmhdr
.code
= SCN_MARGINCLICK
;
4544 scn
.modifiers
= modifiers
;
4545 scn
.position
= position
;
4546 scn
.margin
= marginClicked
;
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
;
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
);
4575 void Editor::NotifyZoom() {
4576 SCNotification scn
= {};
4577 scn
.nmhdr
.code
= SCN_ZOOM
;
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
);
4598 NeedWrapping(lineDoc
, lineDoc
+ lines
+ 1);
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
;
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
;
4622 return startDeletion
;
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)));
4639 // Could check that change is before last visible line.
4643 if (mh
.modificationType
& SC_MOD_LEXERSTATE
) {
4644 if (paintState
== painting
) {
4645 CheckForChangeOutsidePaint(
4646 Range(mh
.position
, mh
.position
+ mh
.length
));
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
4660 InvalidateRange(mh
.position
, mh
.position
+ mh
.length
);
4663 if (mh
.modificationType
& SC_MOD_CHANGESTYLE
) {
4664 llc
.Invalidate(LineLayout::llCheckTextAndStyle
);
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
);
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
);
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
);
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
) {
4721 SetVerticalScrollPos();
4725 if (paintState
== notPainting
&& !CanDeferToLastStep(mh
)) {
4726 QueueIdleWork(WorkNeeded::workStyle
, pdoc
->Length());
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
)) {
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);
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
)) {
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
;
4773 scn
.length
= mh
.length
;
4774 scn
.linesAdded
= mh
.linesAdded
;
4776 scn
.foldLevelNow
= mh
.foldLevelNow
;
4777 scn
.foldLevelPrev
= mh
.foldLevelPrev
;
4778 scn
.token
= mh
.token
;
4779 scn
.annotationLinesAdded
= mh
.annotationLinesAdded
;
4784 void Editor::NotifyDeleted(Document
*, void *) {
4788 void Editor::NotifyMacroRecord(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
4790 // Enumerates all macroable messages
4796 case SCI_REPLACESEL
:
4798 case SCI_INSERTTEXT
:
4799 case SCI_APPENDTEXT
:
4804 case SCI_SEARCHANCHOR
:
4805 case SCI_SEARCHNEXT
:
4806 case SCI_SEARCHPREV
:
4808 case SCI_LINEDOWNEXTEND
:
4810 case SCI_PARADOWNEXTEND
:
4812 case SCI_LINEUPEXTEND
:
4814 case SCI_PARAUPEXTEND
:
4816 case SCI_CHARLEFTEXTEND
:
4818 case SCI_CHARRIGHTEXTEND
:
4820 case SCI_WORDLEFTEXTEND
:
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
:
4832 case SCI_HOMEEXTEND
:
4834 case SCI_LINEENDEXTEND
:
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
:
4848 case SCI_PAGEUPEXTEND
:
4850 case SCI_PAGEDOWNEXTEND
:
4851 case SCI_EDITTOGGLEOVERTYPE
:
4853 case SCI_DELETEBACK
:
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
:
4870 case SCI_LINEDELETE
:
4871 case SCI_LINETRANSPOSE
:
4872 case SCI_LINEDUPLICATE
:
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
:
4901 // Filter out all others like display changes. Also, newlines are redundant
4902 // with char insert messages.
4905 // printf("Filtered out %ld of macro recording\n", iMessage);
4909 // Send notification
4910 SCNotification scn
= {};
4911 scn
.nmhdr
.code
= SCN_MACRORECORD
;
4912 scn
.message
= iMessage
;
4913 scn
.wParam
= wParam
;
4914 scn
.lParam
= lParam
;
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
) {
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())))
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());
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
);
4964 SetVerticalScrollPos();
4966 MovePositionTo(newPos
, selt
);
4970 void Editor::ChangeCaseOfSelection(int caseMapping
) {
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
])
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
;
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
,
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
);
5007 current
.caret
.Add(diffSizes
);
5009 sel
.Range(r
) = current
;
5015 void Editor::LineTranspose() {
5016 int line
= pdoc
->LineFromPosition(sel
.MainCaret());
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
) {
5044 const char *eol
= "";
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();
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
;
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();
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
;
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();
5091 pdoc
->BeginUndoAction();
5095 const char *eol
= "\n";
5096 if (pdoc
->eolMode
== SC_EOL_CRLF
) {
5098 } else if (pdoc
->eolMode
== SC_EOL_CR
) {
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
5104 pdoc
->EndUndoAction();
5105 if (insertLength
> 0) {
5106 SetEmptySelection(sel
.MainCaret() + insertLength
);
5109 if (recordingMacro
) {
5113 NotifyMacroRecord(SCI_REPLACESEL
, 0, reinterpret_cast<sptr_t
>(txt
));
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
;
5131 caretToUse
= sel
.Rectangular().caret
;
5135 Point pt
= LocationFromPosition(caretToUse
);
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
)) {
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
5169 Point ptNew
= LocationFromPosition(posNew
.Position());
5170 while ((posNew
.Position() > caretToUse
.Position()) && (ptNew
.y
> newY
)) {
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();
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
)));
5193 } while (!cs
.GetVisible(lineDoc
));
5196 int Editor::StartEndDisplayLine(int pos
, bool start
) {
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))) {
5210 posRet
= ll
->LineStart(subLine
) + posLineStart
;
5212 if (subLine
== ll
->lines
- 1)
5213 posRet
= ll
->LineStart(subLine
+ 1) + posLineStart
;
5215 posRet
= ll
->LineStart(subLine
+ 1) + posLineStart
- 1;
5221 if (posRet
== INVALID_POSITION
) {
5228 int Editor::KeyCommand(unsigned int iMessage
) {
5233 case SCI_LINEDOWNEXTEND
:
5234 CursorUpOrDown(1, Selection::selStream
);
5236 case SCI_LINEDOWNRECTEXTEND
:
5237 CursorUpOrDown(1, Selection::selRectangle
);
5242 case SCI_PARADOWNEXTEND
:
5243 ParaUpOrDown(1, Selection::selStream
);
5245 case SCI_LINESCROLLDOWN
:
5246 ScrollTo(topLine
+ 1);
5247 MoveCaretInsideView(false);
5252 case SCI_LINEUPEXTEND
:
5253 CursorUpOrDown(-1, Selection::selStream
);
5255 case SCI_LINEUPRECTEXTEND
:
5256 CursorUpOrDown(-1, Selection::selRectangle
);
5261 case SCI_PARAUPEXTEND
:
5262 ParaUpOrDown(-1, Selection::selStream
);
5264 case SCI_LINESCROLLUP
:
5265 ScrollTo(topLine
- 1);
5266 MoveCaretInsideView(false);
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));
5277 MovePositionTo(MovePositionSoVisible(
5278 SelectionPosition((sel
.LimitsForRectangularElseMain().start
).Position() - 1), -1));
5281 MovePositionTo(sel
.LimitsForRectangularElseMain().start
);
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
);
5291 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel
.MainCaret() - 1), -1), Selection::selStream
);
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
);
5301 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel
.MainCaret() - 1), -1), Selection::selRectangle
);
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));
5314 MovePositionTo(MovePositionSoVisible(
5315 SelectionPosition((sel
.LimitsForRectangularElseMain().end
).Position() + 1), 1));
5318 MovePositionTo(sel
.LimitsForRectangularElseMain().end
);
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
);
5328 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel
.MainCaret() + 1), 1), Selection::selStream
);
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
);
5338 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel
.MainCaret() + 1), 1), Selection::selRectangle
);
5343 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordStart(sel
.MainCaret(), -1), -1));
5346 case SCI_WORDLEFTEXTEND
:
5347 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordStart(sel
.MainCaret(), -1), -1), Selection::selStream
);
5351 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordStart(sel
.MainCaret(), 1), 1));
5354 case SCI_WORDRIGHTEXTEND
:
5355 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordStart(sel
.MainCaret(), 1), 1), Selection::selStream
);
5359 case SCI_WORDLEFTEND
:
5360 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordEnd(sel
.MainCaret(), -1), -1));
5363 case SCI_WORDLEFTENDEXTEND
:
5364 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordEnd(sel
.MainCaret(), -1), -1), Selection::selStream
);
5367 case SCI_WORDRIGHTEND
:
5368 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordEnd(sel
.MainCaret(), 1), 1));
5371 case SCI_WORDRIGHTENDEXTEND
:
5372 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordEnd(sel
.MainCaret(), 1), 1), Selection::selStream
);
5377 MovePositionTo(pdoc
->LineStart(pdoc
->LineFromPosition(sel
.MainCaret())));
5380 case SCI_HOMEEXTEND
:
5381 MovePositionTo(pdoc
->LineStart(pdoc
->LineFromPosition(sel
.MainCaret())), Selection::selStream
);
5384 case SCI_HOMERECTEXTEND
:
5385 MovePositionTo(pdoc
->LineStart(pdoc
->LineFromPosition(sel
.MainCaret())), Selection::selRectangle
);
5389 MovePositionTo(pdoc
->LineEndPosition(sel
.MainCaret()));
5392 case SCI_LINEENDEXTEND
:
5393 MovePositionTo(pdoc
->LineEndPosition(sel
.MainCaret()), Selection::selStream
);
5396 case SCI_LINEENDRECTEXTEND
:
5397 MovePositionTo(pdoc
->LineEndPosition(sel
.MainCaret()), Selection::selRectangle
);
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
);
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
);
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
);
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
);
5436 case SCI_DOCUMENTSTART
:
5440 case SCI_DOCUMENTSTARTEXTEND
:
5441 MovePositionTo(0, Selection::selStream
);
5444 case SCI_DOCUMENTEND
:
5445 MovePositionTo(pdoc
->Length());
5448 case SCI_DOCUMENTENDEXTEND
:
5449 MovePositionTo(pdoc
->Length(), Selection::selStream
);
5452 case SCI_STUTTEREDPAGEUP
:
5453 PageMove(-1, Selection::noSel
, true);
5455 case SCI_STUTTEREDPAGEUPEXTEND
:
5456 PageMove(-1, Selection::selStream
, true);
5458 case SCI_STUTTEREDPAGEDOWN
:
5459 PageMove(1, Selection::noSel
, true);
5461 case SCI_STUTTEREDPAGEDOWNEXTEND
:
5462 PageMove(1, Selection::selStream
, true);
5467 case SCI_PAGEUPEXTEND
:
5468 PageMove(-1, Selection::selStream
);
5470 case SCI_PAGEUPRECTEXTEND
:
5471 PageMove(-1, Selection::selRectangle
);
5476 case SCI_PAGEDOWNEXTEND
:
5477 PageMove(1, Selection::selStream
);
5479 case SCI_PAGEDOWNRECTEXTEND
:
5480 PageMove(1, Selection::selRectangle
);
5482 case SCI_EDITTOGGLEOVERTYPE
:
5483 inOverstrike
= !inOverstrike
;
5485 ShowCaretAtCurrentPosition();
5486 ContainerNeedsUpdate(SC_UPDATE_CONTENT
);
5489 case SCI_CANCEL
: // Cancel any modes - handled in subclass
5490 // Also unselect text
5493 case SCI_DELETEBACK
:
5495 if ((caretSticky
== SC_CARETSTICKY_OFF
) || (caretSticky
== SC_CARETSTICKY_WHITESPACE
)) {
5498 EnsureCaretVisible();
5500 case SCI_DELETEBACKNOTLINE
:
5502 if ((caretSticky
== SC_CARETSTICKY_OFF
) || (caretSticky
== SC_CARETSTICKY_WHITESPACE
)) {
5505 EnsureCaretVisible();
5509 if (caretSticky
== SC_CARETSTICKY_OFF
) {
5512 EnsureCaretVisible();
5513 ShowCaretAtCurrentPosition(); // Avoid blinking
5517 if ((caretSticky
== SC_CARETSTICKY_OFF
) || (caretSticky
== SC_CARETSTICKY_WHITESPACE
)) {
5520 EnsureCaretVisible();
5521 ShowCaretAtCurrentPosition(); // Avoid blinking
5530 MovePositionTo(pdoc
->VCHomePosition(sel
.MainCaret()));
5533 case SCI_VCHOMEEXTEND
:
5534 MovePositionTo(pdoc
->VCHomePosition(sel
.MainCaret()), Selection::selStream
);
5537 case SCI_VCHOMERECTEXTEND
:
5538 MovePositionTo(pdoc
->VCHomePosition(sel
.MainCaret()), Selection::selRectangle
);
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
);
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
);
5562 if (vs
.zoomLevel
< 20) {
5564 InvalidateStyleRedraw();
5569 if (vs
.zoomLevel
> -10) {
5571 InvalidateStyleRedraw();
5575 case SCI_DELWORDLEFT
: {
5576 int startWord
= pdoc
->NextWordStart(sel
.MainCaret(), -1);
5577 pdoc
->DeleteChars(startWord
, sel
.MainCaret() - startWord
);
5578 sel
.RangeMain().ClearVirtualSpace();
5582 case SCI_DELWORDRIGHT
: {
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());
5591 case SCI_DELWORDRIGHTEND
: {
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());
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();
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());
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));
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
);
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
);
5637 case SCI_LINETRANSPOSE
:
5640 case SCI_LINEDUPLICATE
:
5643 case SCI_SELECTIONDUPLICATE
:
5647 ChangeCaseOfSelection(cmLower
);
5650 ChangeCaseOfSelection(cmUpper
);
5652 case SCI_WORDPARTLEFT
:
5653 MovePositionTo(MovePositionSoVisible(pdoc
->WordPartLeft(sel
.MainCaret()), -1));
5656 case SCI_WORDPARTLEFTEXTEND
:
5657 MovePositionTo(MovePositionSoVisible(pdoc
->WordPartLeft(sel
.MainCaret()), -1), Selection::selStream
);
5660 case SCI_WORDPARTRIGHT
:
5661 MovePositionTo(MovePositionSoVisible(pdoc
->WordPartRight(sel
.MainCaret()), 1));
5664 case SCI_WORDPARTRIGHTEXTEND
:
5665 MovePositionTo(MovePositionSoVisible(pdoc
->WordPartRight(sel
.MainCaret()), 1), Selection::selStream
);
5668 case SCI_HOMEDISPLAY
:
5669 MovePositionTo(MovePositionSoVisible(
5670 StartEndDisplayLine(sel
.MainCaret(), true), -1));
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
);
5683 case SCI_HOMEDISPLAYEXTEND
:
5684 MovePositionTo(MovePositionSoVisible(
5685 StartEndDisplayLine(sel
.MainCaret(), true), -1), Selection::selStream
);
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
);
5698 case SCI_LINEENDDISPLAY
:
5699 MovePositionTo(MovePositionSoVisible(
5700 StartEndDisplayLine(sel
.MainCaret(), false), 1));
5703 case SCI_LINEENDDISPLAYEXTEND
:
5704 MovePositionTo(MovePositionSoVisible(
5705 StartEndDisplayLine(sel
.MainCaret(), false), 1), Selection::selStream
);
5708 case SCI_SCROLLTOSTART
:
5711 case SCI_SCROLLTOEND
:
5712 ScrollTo(MaxScrollPos());
5718 int Editor::KeyDefault(int, int) {
5722 int Editor::KeyDownWithModifiers(int key
, int modifiers
, bool *consumed
) {
5724 int msg
= kmap
.Find(key
, modifiers
);
5728 return static_cast<int>(WndProc(msg
, 0, 0));
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
) {
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
) {
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
)) &&
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
);
5758 if (pdoc
->useTabs
) {
5759 const int lengthInserted
= pdoc
->InsertString(caretPosition
, "\t", 1);
5760 sel
.Range(r
) = SelectionRange(caretPosition
+ lengthInserted
);
5762 int numSpaces
= (pdoc
->tabInChars
) -
5763 (pdoc
->GetColumn(caretPosition
) % (pdoc
->tabInChars
));
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
);
5773 if (pdoc
->GetColumn(caretPosition
) <= pdoc
->GetLineIndentation(lineCurrentPos
) &&
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
);
5780 int newColumn
= ((pdoc
->GetColumn(caretPosition
) - 1) / pdoc
->tabInChars
) *
5784 int newPos
= caretPosition
;
5785 while (pdoc
->GetColumn(newPos
) > newColumn
)
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
));
5803 sel
.Range(r
) = SelectionRange(pdoc
->LineStart(lineCurrentPos
+ 1), pdoc
->LineStart(lineOfAnchor
));
5805 if (anchorPosOnLine
== 0)
5806 sel
.Range(r
) = SelectionRange(pdoc
->LineStart(lineCurrentPos
), pdoc
->LineStart(lineOfAnchor
));
5808 sel
.Range(r
) = SelectionRange(pdoc
->LineStart(lineCurrentPos
), pdoc
->LineStart(lineOfAnchor
+ 1));
5812 ContainerNeedsUpdate(SC_UPDATE_SELECTION
);
5815 class CaseFolderASCII
: public CaseFolderTable
{
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
),
5851 ft
->chrgText
.cpMin
= pos
;
5852 ft
->chrgText
.cpMax
= pos
+ lengthFound
;
5858 * Relocatable search support : Searches relative to current selection
5859 * point and sets the selection to the found text range with
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
);
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
),
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
),
5906 SetSelection(pos
, pos
+ lengthFound
);
5912 std::string
Editor::CaseMapString(const std::string
&s
, int caseMapping
) {
5914 for (size_t i
=0; i
<ret
.size(); i
++) {
5915 switch (caseMapping
) {
5917 if (ret
[i
] >= 'a' && ret
[i
] <= 'z')
5918 ret
[i
] = static_cast<char>(ret
[i
] - 'a' + 'A');
5921 if (ret
[i
] >= 'A' && ret
[i
] <= 'Z')
5922 ret
[i
] = static_cast<char>(ret
[i
] - 'A' + 'a');
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,
5947 targetEnd
= pos
+ lengthFound
;
5952 void Editor::GoToLine(int lineNo
) {
5953 if (lineNo
> pdoc
->LinesTotal())
5954 lineNo
= pdoc
->LinesTotal();
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)
5965 if (abs(pt1
.y
- pt2
.y
) > 3)
5970 std::string
Editor::RangeText(int start
, int end
) const {
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
);
5979 return std::string();
5982 void Editor::CopySelectionRange(SelectionText
*ss
, bool allowLineCopy
) {
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);
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);
6039 if (!(posDrag
== newPos
)) {
6048 void Editor::DisplayCursor(Window::Cursor c
) {
6049 if (cursorMode
== SC_CURSORNORMAL
)
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();
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());
6095 positionAfterDeletion
.Add(-SelectionRange(position
, sel
.Range(r
).Start()).Length());
6100 if (position
> selStart
) {
6101 positionAfterDeletion
.Add(-SelectionRange(selEnd
, selStart
).Length());
6106 position
= positionAfterDeletion
;
6108 std::string convertedText
= Document::TransformLineEnds(value
, lengthValue
, pdoc
->eolMode
);
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
);
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
))
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
)) {
6153 if (pos
== range
.Start()) {
6154 // see if just before selection
6155 if (pt
.x
< ptPos
.x
) {
6159 if (pos
== range
.End()) {
6160 // see if just after selection
6161 if (pt
.x
> ptPos
.x
) {
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
);
6184 Window::Cursor
Editor::GetMarginCursor(Point pt
) const {
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
;
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_
);
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
);
6248 // Select only the anchored word
6249 if (pos
>= originalAnchorPos
)
6250 TrimAndSetSelection(wordSelectAnchorEndPos
, wordSelectAnchorStartPos
);
6252 TrimAndSetSelection(wordSelectAnchorStartPos
, wordSelectAnchorEndPos
);
6256 void Editor::DwellEnd(bool mouseMoved
) {
6258 ticksToDwell
= dwellDelay
;
6260 ticksToDwell
= SC_TIME_FOREVER
;
6261 if (dwelling
&& (dwellDelay
< SC_TIME_FOREVER
)) {
6263 NotifyDwelling(ptMouseLast
, dwelling
);
6267 void Editor::MouseLeave() {
6268 SetHotSpotRange(NULL
);
6269 if (!HaveMouseCapture()) {
6270 ptMouseLast
= Point(-1,-1);
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);
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
))
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
) {
6302 lastClickTime
= curTime
;
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
) {
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
;
6328 if (selectionType
== selChar
) {
6329 selectionType
= selWord
;
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
;
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);
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);
6360 // Anchor at start of line; select nothing to begin with.
6361 startWord
= 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);
6375 SetEmptySelection(sel
.MainCaret());
6377 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
6379 NotifyDoubleClick(pt
, modifiers
);
6380 if (PositionIsHotspot(newCharPos
.Position()))
6381 NotifyHotSpotDoubleClicked(newCharPos
.Position(), modifiers
);
6383 } else { // Single click
6385 sel
.selType
= Selection::selStream
;
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
);
6392 // Single shift+click in margin: select from line anchor to clicked line
6393 if (sel
.MainAnchor() > sel
.MainCaret())
6394 lineAnchorPos
= sel
.MainAnchor() - 1;
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);
6409 if (PointIsHotspot(pt
)) {
6410 NotifyHotSpotClicked(newCharPos
.Position(), modifiers
);
6411 hotSpotClickPos
= newCharPos
.Position();
6414 if (PointInSelection(pt
) && !SelectionEmpty())
6415 inDragDrop
= ddInitial
;
6417 inDragDrop
= ddNone
;
6419 SetMouseCapture(true);
6420 if (inDragDrop
!= ddInitial
) {
6421 SetDragPosition(SelectionPosition(invalidPosition
));
6423 if (ctrl
&& multipleSelection
) {
6424 SelectionRange
range(newPos
);
6425 sel
.TentativeSelection(range
);
6426 InvalidateSelection(range
, true);
6428 InvalidateSelection(SelectionRange(newPos
), true);
6429 if (sel
.Count() > 1)
6431 if ((sel
.Count() > 1) || (sel
.selType
!= Selection::selStream
))
6433 sel
.selType
= alt
? Selection::selRectangle
: Selection::selStream
;
6434 SetSelection(newPos
, newPos
);
6437 SelectionPosition anchorCurrent
= newPos
;
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
;
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
)
6467 return PositionIsHotspot(pos
);
6470 void Editor::SetHotSpotRange(Point
*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.
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
);
6487 InvalidateRange(hotspot
.start
, hotspot
.end
);
6490 if (hotspot
.Valid()) {
6491 InvalidateRange(hotspot
.start
, hotspot
.end
);
6493 hotspot
= Range(invalidPosition
);
6497 Range
Editor::GetHotSpotRange() const {
6501 void Editor::ButtonMoveWithModifiers(Point pt
, int modifiers
) {
6502 if ((ptMouseLast
.x
!= pt
.x
) || (ptMouseLast
.y
!= pt
.y
)) {
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
);
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)
6528 autoScrollTimer
.ticksToWait
= autoScrollDelay
;
6531 if (posDrag
.IsValid()) {
6532 SetDragPosition(movePos
);
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);
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
6562 wordSelectInitialCaretPos
= -1;
6563 WordSelection(movePos
.Position());
6566 // Continue selecting by line
6567 LineSelection(movePos
.Position(), lineAnchorPos
, selectionType
== selWholeLine
);
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);
6579 } else if (pt
.y
< rcClient
.top
) {
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
;
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
);
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
));
6641 DisplayCursor(Window::cursorText
);
6642 SetHotSpotRange(NULL
);
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());
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
);
6675 SetEmptySelection(newPos
.Position());
6679 selectionType
= selChar
;
6682 if (selectionType
== selChar
) {
6683 if (sel
.Count() > 1) {
6685 SelectionRange(newPos
, sel
.Range(sel
.Count() - 1).anchor
);
6686 InvalidateSelection(sel
.RangeMain(), true);
6688 SetSelection(newPos
, sel
.RangeMain().anchor
);
6691 sel
.CommitTentative();
6693 SetRectangularRange();
6694 lastClickTime
= curTime
;
6696 lastXChosen
= static_cast<int>(pt
.x
) + xOffset
;
6697 if (sel
.selType
== Selection::selStream
) {
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()) {
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
;
6722 if (horizontalScrollBarVisible
&& trackLineWidth
&& (lineWidthMaxSeen
> scrollWidth
)) {
6723 scrollWidth
= lineWidthMaxSeen
;
6726 if ((dwellDelay
< SC_TIME_FOREVER
) &&
6727 (ticksToDwell
> 0) &&
6728 (!HaveMouseCapture()) &&
6729 (ptMouseLast
.y
>= 0)) {
6730 ticksToDwell
-= timer
.tickSize
;
6731 if (ticksToDwell
<= 0) {
6733 NotifyDwelling(ptMouseLast
, dwelling
);
6738 bool Editor::Idle() {
6742 bool wrappingDone
= !Wrapping();
6744 if (!wrappingDone
) {
6745 // Wrap lines during idle.
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
6757 idleDone
= wrappingDone
; // && thatDone && theOtherThingDone...
6762 void Editor::SetFocusState(bool focusState
) {
6763 hasFocus
= focusState
;
6764 NotifyFocus(hasFocus
);
6766 ShowCaretAtCurrentPosition();
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);
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
)
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));
6812 void Editor::QueueIdleWork(WorkNeeded::workItems items
, int upTo
) {
6813 workNeeded
.Need(items
, upTo
);
6816 bool Editor::PaintContains(PRectangle rc
) {
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.
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);
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
)) {
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
));
6864 if ((braces
[1] != pos1
) || (matchStyle
!= bracesMatchStyle
)) {
6865 CheckForChangeOutsidePaint(Range(braces
[1]));
6866 CheckForChangeOutsidePaint(Range(pos1
));
6869 bracesMatchStyle
= matchStyle
;
6870 if (paintState
== notPainting
) {
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;
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
) {
6898 void Editor::SetDocPointer(Document
*document
) {
6899 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
6900 pdoc
->RemoveWatcher(this, 0);
6902 if (document
== NULL
) {
6903 pdoc
= new Document();
6909 // Ensure all positions within document
6914 braces
[0] = invalidPosition
;
6915 braces
[1] = invalidPosition
;
6917 vs
.ReleaseAllExtendedStyles();
6919 SetRepresentations();
6921 // Reset the contraction state to fully shown.
6923 cs
.InsertLines(0, pdoc
->LinesTotal() - 1);
6924 SetAnnotationHeights(0, pdoc
->LinesTotal());
6928 pdoc
->AddWatcher(this, 0);
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
);
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
);
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
);
6963 line
= pdoc
->GetLastChild(line
);
6968 return lineMaxSubord
;
6971 void Editor::SetFoldExpanded(int lineDoc
, bool expanded
) {
6972 if (cs
.SetExpanded(lineDoc
, expanded
)) {
6977 void Editor::FoldLine(int line
, int action
) {
6979 if (action
== SC_FOLDACTION_TOGGLE
) {
6980 if ((pdoc
->GetLevel(line
) & SC_FOLDLEVELHEADERFLAG
) == 0) {
6981 line
= pdoc
->GetFoldParent(line
);
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();
7002 if (!(cs
.GetVisible(line
))) {
7003 EnsureLineVisible(line
, false);
7006 cs
.SetExpanded(line
, 1);
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))
7024 int lineMaxSubord
= pdoc
->GetLastChild(line
, level
& SC_FOLDLEVELNUMBERMASK
);
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
);
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
))
7042 line
= cs
.ContractedNext(line
+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
)
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
);
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();
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();
7097 if ((topLine
> lineDisplay
) || (lineDisplay
> topLine
+ LinesOnScreen() - 1) || (visiblePolicy
& VISIBLE_STRICT
)) {
7098 SetTopLine(Platform::Clamp(lineDisplay
- LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
7099 SetVerticalScrollPos();
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
);
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);
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);
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)) {
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)) {
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);
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);
7185 NotifyNeedShown(pos
, len
);
7189 int Editor::GetTag(char *tagValue
, int tagNumber
) {
7190 const char *text
= 0;
7192 if ((tagNumber
>= 1) && (tagNumber
<= 9)) {
7193 char name
[3] = "\\?";
7194 name
[1] = static_cast<char>(tagNumber
+ '0');
7196 text
= pdoc
->SubstituteByPosition(name
, &length
);
7200 memcpy(tagValue
, text
, length
+ 1);
7207 int Editor::ReplaceTarget(bool replacePatterns
, const char *text
, int length
) {
7210 length
= istrlen(text
);
7211 if (replacePatterns
) {
7212 text
= pdoc
->SubstituteByPosition(text
, &length
);
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
;
7225 bool Editor::IsUnicodeMode() const {
7226 return pdoc
&& (SC_CP_UTF8
== pdoc
->dbcsCodePage
);
7229 int Editor::CodePage() const {
7231 return pdoc
->dbcsCodePage
;
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
);
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');
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
);
7276 case SCI_STYLESETFORE
:
7277 vs
.styles
[wParam
].fore
= ColourDesired(static_cast<long>(lParam
));
7279 case SCI_STYLESETBACK
:
7280 vs
.styles
[wParam
].back
= ColourDesired(static_cast<long>(lParam
));
7282 case SCI_STYLESETBOLD
:
7283 vs
.styles
[wParam
].weight
= lParam
!= 0 ? SC_WEIGHT_BOLD
: SC_WEIGHT_NORMAL
;
7285 case SCI_STYLESETWEIGHT
:
7286 vs
.styles
[wParam
].weight
= static_cast<int>(lParam
);
7288 case SCI_STYLESETITALIC
:
7289 vs
.styles
[wParam
].italic
= lParam
!= 0;
7291 case SCI_STYLESETEOLFILLED
:
7292 vs
.styles
[wParam
].eolFilled
= lParam
!= 0;
7294 case SCI_STYLESETSIZE
:
7295 vs
.styles
[wParam
].size
= static_cast<int>(lParam
* SC_FONT_SIZE_MULTIPLIER
);
7297 case SCI_STYLESETSIZEFRACTIONAL
:
7298 vs
.styles
[wParam
].size
= static_cast<int>(lParam
);
7300 case SCI_STYLESETFONT
:
7302 vs
.SetStyleFontName(static_cast<int>(wParam
), CharPtrFromSPtr(lParam
));
7305 case SCI_STYLESETUNDERLINE
:
7306 vs
.styles
[wParam
].underline
= lParam
!= 0;
7308 case SCI_STYLESETCASE
:
7309 vs
.styles
[wParam
].caseForce
= static_cast<Style::ecaseForced
>(lParam
);
7311 case SCI_STYLESETCHARACTERSET
:
7312 vs
.styles
[wParam
].characterSet
= static_cast<int>(lParam
);
7313 pdoc
->SetCaseFolder(NULL
);
7315 case SCI_STYLESETVISIBLE
:
7316 vs
.styles
[wParam
].visible
= lParam
!= 0;
7318 case SCI_STYLESETCHANGEABLE
:
7319 vs
.styles
[wParam
].changeable
= lParam
!= 0;
7321 case SCI_STYLESETHOTSPOT
:
7322 vs
.styles
[wParam
].hotspot
= lParam
!= 0;
7325 InvalidateStyleRedraw();
7328 sptr_t
Editor::StyleGetMessage(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
7329 vs
.EnsureStyle(wParam
);
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;
7365 sptr_t
Editor::StringResult(sptr_t lParam
, const char *val
) {
7366 const size_t len
= val
? strlen(val
) : 0;
7368 char *ptr
= CharPtrFromSPtr(lParam
);
7370 memcpy(ptr
, val
, len
+1);
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
7380 char *ptr
= CharPtrFromSPtr(lParam
);
7382 memcpy(ptr
, val
, len
);
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
7394 NotifyMacroRecord(iMessage
, wParam
, lParam
);
7400 return pdoc
->Length() + 1;
7403 char *ptr
= CharPtrFromSPtr(lParam
);
7404 unsigned int iChar
= 0;
7405 for (; iChar
< wParam
- 1; iChar
++)
7406 ptr
[iChar
] = pdoc
->CharAt(iChar
);
7415 pdoc
->DeleteChars(0, pdoc
->Length());
7416 SetEmptySelection(0);
7417 const char *text
= CharPtrFromSPtr(lParam
);
7418 pdoc
->InsertString(0, text
, istrlen(text
));
7422 case SCI_GETTEXTLENGTH
:
7423 return pdoc
->Length();
7434 case SCI_COPYALLOWLINE
:
7438 case SCI_VERTICALCENTRECARET
:
7439 VerticalCentreCaret();
7442 case SCI_MOVESELECTEDLINESUP
:
7443 MoveSelectedLinesUp();
7446 case SCI_MOVESELECTEDLINESDOWN
:
7447 MoveSelectedLinesDown();
7451 CopyRangeToClipboard(static_cast<int>(wParam
), static_cast<int>(lParam
));
7455 CopyText(static_cast<int>(wParam
), CharPtrFromSPtr(lParam
));
7460 if ((caretSticky
== SC_CARETSTICKY_OFF
) || (caretSticky
== SC_CARETSTICKY_WHITESPACE
)) {
7463 EnsureCaretVisible();
7469 EnsureCaretVisible();
7478 return (pdoc
->CanUndo() && !pdoc
->IsReadOnly()) ? 1 : 0;
7480 case SCI_EMPTYUNDOBUFFER
:
7481 pdoc
->DeleteUndoHistory();
7484 case SCI_GETFIRSTVISIBLELINE
:
7487 case SCI_SETFIRSTVISIBLELINE
:
7488 ScrollTo(static_cast<int>(wParam
));
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));
7495 return lineEnd
- lineStart
;
7497 char *ptr
= CharPtrFromSPtr(lParam
);
7499 for (int iChar
= lineStart
; iChar
< lineEnd
; iChar
++) {
7500 ptr
[iPlace
++] = pdoc
->CharAt(iChar
);
7505 case SCI_GETLINECOUNT
:
7506 if (pdoc
->LinesTotal() == 0)
7509 return pdoc
->LinesTotal();
7512 return !pdoc
->IsSavePoint();
7515 int nStart
= static_cast<int>(wParam
);
7516 int nEnd
= static_cast<int>(lParam
);
7518 nEnd
= pdoc
->Length();
7520 nStart
= nEnd
; // Remove selection
7521 InvalidateSelection(SelectionRange(nStart
, nEnd
));
7523 sel
.selType
= Selection::selStream
;
7524 SetSelection(nEnd
, nStart
);
7525 EnsureCaretVisible();
7529 case SCI_GETSELTEXT
: {
7530 SelectionText selectedText
;
7531 CopySelectionRange(&selectedText
);
7533 return selectedText
.LengthWithTerminator();
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
];
7547 case SCI_LINEFROMPOSITION
:
7548 if (static_cast<int>(wParam
) < 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());
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())
7559 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
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())))
7568 return pdoc
->LineStart(static_cast<int>(wParam
) + 1) - pdoc
->LineStart(static_cast<int>(wParam
));
7570 case SCI_REPLACESEL
: {
7575 char *replacement
= CharPtrFromSPtr(lParam
);
7576 const int lengthInserted
= pdoc
->InsertString(
7577 sel
.MainCaret(), replacement
, istrlen(replacement
));
7578 SetEmptySelection(sel
.MainCaret() + lengthInserted
);
7579 EnsureCaretVisible();
7583 case SCI_SETTARGETSTART
:
7584 targetStart
= static_cast<int>(wParam
);
7587 case SCI_GETTARGETSTART
:
7590 case SCI_SETTARGETEND
:
7591 targetEnd
= static_cast<int>(wParam
);
7594 case SCI_GETTARGETEND
:
7597 case SCI_TARGETFROMSELECTION
:
7598 if (sel
.MainCaret() < sel
.MainAnchor()) {
7599 targetStart
= sel
.MainCaret();
7600 targetEnd
= sel
.MainAnchor();
7602 targetStart
= sel
.MainAnchor();
7603 targetEnd
= sel
.MainCaret();
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
);
7623 case SCI_GETSEARCHFLAGS
:
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
));
7643 case SCI_SETXOFFSET
:
7644 xOffset
= static_cast<int>(wParam
);
7645 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL
);
7646 SetHorizontalScrollPos();
7650 case SCI_GETXOFFSET
:
7653 case SCI_CHOOSECARETX
:
7657 case SCI_SCROLLCARET
:
7658 EnsureCaretVisible();
7661 case SCI_SETREADONLY
:
7662 pdoc
->SetReadOnly(wParam
!= 0);
7665 case SCI_GETREADONLY
:
7666 return pdoc
->IsReadOnly();
7671 case SCI_POINTXFROMPOSITION
:
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
:
7684 Point pt
= LocationFromPosition(static_cast<int>(lParam
));
7685 return static_cast<int>(pt
.y
);
7689 return FindText(wParam
, lParam
);
7691 case SCI_GETTEXTRANGE
: {
7694 Sci_TextRange
*tr
= reinterpret_cast<Sci_TextRange
*>(lParam
);
7695 int cpMax
= tr
->chrg
.cpMax
;
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;
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();
7726 case SCI_SETMARGINRIGHT
:
7727 vs
.rightMarginWidth
= static_cast<int>(lParam
);
7728 InvalidateStyleRedraw();
7731 // Control specific mesages
7736 const int lengthInserted
= pdoc
->InsertString(
7737 CurrentPosition(), CharPtrFromSPtr(lParam
), static_cast<int>(wParam
));
7738 SetEmptySelection(sel
.MainCaret() + lengthInserted
);
7742 case SCI_ADDSTYLEDTEXT
:
7744 AddStyledText(CharPtrFromSPtr(lParam
), static_cast<int>(wParam
));
7747 case SCI_INSERTTEXT
: {
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
);
7762 case SCI_CHANGEINSERTION
:
7763 PLATFORM_ASSERT(lParam
);
7764 pdoc
->ChangeInsertion(CharPtrFromSPtr(lParam
), static_cast<int>(wParam
));
7767 case SCI_APPENDTEXT
:
7768 pdoc
->InsertString(pdoc
->Length(), CharPtrFromSPtr(lParam
), static_cast<int>(wParam
));
7775 case SCI_DELETERANGE
:
7776 pdoc
->DeleteChars(static_cast<int>(wParam
), static_cast<int>(lParam
));
7779 case SCI_CLEARDOCUMENTSTYLE
:
7780 ClearDocumentStyle();
7783 case SCI_SETUNDOCOLLECTION
:
7784 pdoc
->SetUndoCollection(wParam
!= 0);
7787 case SCI_GETUNDOCOLLECTION
:
7788 return pdoc
->IsCollectingUndo();
7790 case SCI_BEGINUNDOACTION
:
7791 pdoc
->BeginUndoAction();
7794 case SCI_ENDUNDOACTION
:
7795 pdoc
->EndUndoAction();
7798 case SCI_GETCARETPERIOD
:
7799 return caret
.period
;
7801 case SCI_SETCARETPERIOD
:
7802 CaretSetPeriod(static_cast<int>(wParam
));
7805 case SCI_GETWORDCHARS
:
7806 return pdoc
->GetCharsOfClass(CharClassify::ccWord
, reinterpret_cast<unsigned char *>(lParam
));
7808 case SCI_SETWORDCHARS
: {
7809 pdoc
->SetDefaultCharClasses(false);
7812 pdoc
->SetCharClasses(reinterpret_cast<unsigned char *>(lParam
), CharClassify::ccWord
);
7816 case SCI_GETWHITESPACECHARS
:
7817 return pdoc
->GetCharsOfClass(CharClassify::ccSpace
, reinterpret_cast<unsigned char *>(lParam
));
7819 case SCI_SETWHITESPACECHARS
: {
7822 pdoc
->SetCharClasses(reinterpret_cast<unsigned char *>(lParam
), CharClassify::ccSpace
);
7826 case SCI_GETPUNCTUATIONCHARS
:
7827 return pdoc
->GetCharsOfClass(CharClassify::ccPunctuation
, reinterpret_cast<unsigned char *>(lParam
));
7829 case SCI_SETPUNCTUATIONCHARS
: {
7832 pdoc
->SetCharClasses(reinterpret_cast<unsigned char *>(lParam
), CharClassify::ccPunctuation
);
7836 case SCI_SETCHARSDEFAULT
:
7837 pdoc
->SetDefaultCharClasses(true);
7841 return pdoc
->Length();
7844 pdoc
->Allocate(static_cast<int>(wParam
));
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();
7856 SetSelection(static_cast<int>(wParam
), sel
.MainAnchor());
7860 case SCI_GETCURRENTPOS
:
7861 return sel
.IsRectangular() ? sel
.Rectangular().caret
.Position() : sel
.MainCaret();
7864 if (sel
.IsRectangular()) {
7865 sel
.Rectangular().anchor
.SetPosition(static_cast<int>(wParam
));
7866 SetRectangularRange();
7869 SetSelection(sel
.MainCaret(), static_cast<int>(wParam
));
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
));
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
)));
7887 case SCI_GETSELECTIONEND
:
7888 return sel
.LimitsForRectangularElseMain().end
.Position();
7890 case SCI_SETEMPTYSELECTION
:
7891 SetEmptySelection(static_cast<int>(wParam
));
7894 case SCI_SETPRINTMAGNIFICATION
:
7895 printParameters
.magnification
= static_cast<int>(wParam
);
7898 case SCI_GETPRINTMAGNIFICATION
:
7899 return printParameters
.magnification
;
7901 case SCI_SETPRINTCOLOURMODE
:
7902 printParameters
.colourMode
= static_cast<int>(wParam
);
7905 case SCI_GETPRINTCOLOURMODE
:
7906 return printParameters
.colourMode
;
7908 case SCI_SETPRINTWRAPMODE
:
7909 printParameters
.wrapState
= (wParam
== SC_WRAP_WORD
) ? eWrapWord
: eWrapNone
;
7912 case SCI_GETPRINTWRAPMODE
:
7913 return printParameters
.wrapState
;
7915 case SCI_GETSTYLEAT
:
7916 if (static_cast<int>(wParam
) >= pdoc
->Length())
7919 return pdoc
->StyleAt(static_cast<int>(wParam
));
7929 case SCI_SETSAVEPOINT
:
7930 pdoc
->SetSavePoint();
7933 case SCI_GETSTYLEDTEXT
: {
7936 Sci_TextRange
*tr
= reinterpret_cast<Sci_TextRange
*>(lParam
);
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';
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
));
7958 return vs
.viewWhitespace
;
7961 vs
.viewWhitespace
= static_cast<WhiteSpaceVisibility
>(wParam
);
7965 case SCI_GETWHITESPACESIZE
:
7966 return vs
.whitespaceSize
;
7968 case SCI_SETWHITESPACESIZE
:
7969 vs
.whitespaceSize
= static_cast<int>(wParam
);
7973 case SCI_POSITIONFROMPOINT
:
7974 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam
) - vs
.ExternalMarginWidth(), static_cast<int>(lParam
)),
7977 case SCI_POSITIONFROMPOINTCLOSE
:
7978 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam
) - vs
.ExternalMarginWidth(), static_cast<int>(lParam
)),
7981 case SCI_CHARPOSITIONFROMPOINT
:
7982 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam
) - vs
.ExternalMarginWidth(), static_cast<int>(lParam
)),
7985 case SCI_CHARPOSITIONFROMPOINTCLOSE
:
7986 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam
) - vs
.ExternalMarginWidth(), static_cast<int>(lParam
)),
7990 GoToLine(static_cast<int>(wParam
));
7994 SetEmptySelection(static_cast<int>(wParam
));
7995 EnsureCaretVisible();
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);
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
);
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
);
8025 case SCI_SETLINEENDTYPESALLOWED
:
8026 if (pdoc
->SetLineEndTypesAllowed(static_cast<int>(wParam
))) {
8028 cs
.InsertLines(0, pdoc
->LinesTotal() - 1);
8029 SetAnnotationHeights(0, pdoc
->LinesTotal());
8030 InvalidateStyleRedraw();
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
));
8044 case SCI_SETSTYLING
:
8045 pdoc
->SetStyleFor(static_cast<int>(wParam
), static_cast<char>(lParam
));
8048 case SCI_SETSTYLINGEX
: // Specify a complete styling buffer
8051 pdoc
->SetStyles(static_cast<int>(wParam
), CharPtrFromSPtr(lParam
));
8054 case SCI_SETBUFFEREDDRAW
:
8055 bufferedDraw
= wParam
!= 0;
8058 case SCI_GETBUFFEREDDRAW
:
8059 return bufferedDraw
;
8061 case SCI_GETTWOPHASEDRAW
:
8062 return twoPhaseDraw
;
8064 case SCI_SETTWOPHASEDRAW
:
8065 twoPhaseDraw
= wParam
!= 0;
8066 InvalidateStyleRedraw();
8069 case SCI_SETFONTQUALITY
:
8070 vs
.extraFontFlag
&= ~SC_EFF_QUALITY_MASK
;
8071 vs
.extraFontFlag
|= (wParam
& SC_EFF_QUALITY_MASK
);
8072 InvalidateStyleRedraw();
8075 case SCI_GETFONTQUALITY
:
8076 return (vs
.extraFontFlag
& SC_EFF_QUALITY_MASK
);
8078 case SCI_SETTABWIDTH
:
8080 pdoc
->tabInChars
= static_cast<int>(wParam
);
8081 if (pdoc
->indentInChars
== 0)
8082 pdoc
->actualIndentInChars
= pdoc
->tabInChars
;
8084 InvalidateStyleRedraw();
8087 case SCI_GETTABWIDTH
:
8088 return pdoc
->tabInChars
;
8091 pdoc
->indentInChars
= static_cast<int>(wParam
);
8092 if (pdoc
->indentInChars
!= 0)
8093 pdoc
->actualIndentInChars
= pdoc
->indentInChars
;
8095 pdoc
->actualIndentInChars
= pdoc
->tabInChars
;
8096 InvalidateStyleRedraw();
8100 return pdoc
->indentInChars
;
8102 case SCI_SETUSETABS
:
8103 pdoc
->useTabs
= wParam
!= 0;
8104 InvalidateStyleRedraw();
8107 case SCI_GETUSETABS
:
8108 return pdoc
->useTabs
;
8110 case SCI_SETLINEINDENTATION
:
8111 pdoc
->SetLineIndentation(static_cast<int>(wParam
), static_cast<int>(lParam
));
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;
8124 case SCI_GETTABINDENTS
:
8125 return pdoc
->tabIndents
;
8127 case SCI_SETBACKSPACEUNINDENTS
:
8128 pdoc
->backspaceUnindents
= wParam
!= 0;
8131 case SCI_GETBACKSPACEUNINDENTS
:
8132 return pdoc
->backspaceUnindents
;
8134 case SCI_SETMOUSEDWELLTIME
:
8135 dwellDelay
= static_cast<int>(wParam
);
8136 ticksToDwell
= dwellDelay
;
8139 case SCI_GETMOUSEDWELLTIME
:
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
))) {
8151 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL
);
8152 InvalidateStyleRedraw();
8153 ReconfigureScrollBars();
8157 case SCI_GETWRAPMODE
:
8158 return vs
.wrapState
;
8160 case SCI_SETWRAPVISUALFLAGS
:
8161 if (vs
.SetWrapVisualFlags(static_cast<int>(wParam
))) {
8162 InvalidateStyleRedraw();
8163 ReconfigureScrollBars();
8167 case SCI_GETWRAPVISUALFLAGS
:
8168 return vs
.wrapVisualFlags
;
8170 case SCI_SETWRAPVISUALFLAGSLOCATION
:
8171 if (vs
.SetWrapVisualFlagsLocation(static_cast<int>(wParam
))) {
8172 InvalidateStyleRedraw();
8176 case SCI_GETWRAPVISUALFLAGSLOCATION
:
8177 return vs
.wrapVisualFlagsLocation
;
8179 case SCI_SETWRAPSTARTINDENT
:
8180 if (vs
.SetWrapVisualStartIndent(static_cast<int>(wParam
))) {
8181 InvalidateStyleRedraw();
8182 ReconfigureScrollBars();
8186 case SCI_GETWRAPSTARTINDENT
:
8187 return vs
.wrapVisualStartIndent
;
8189 case SCI_SETWRAPINDENTMODE
:
8190 if (vs
.SetWrapIndentMode(static_cast<int>(wParam
))) {
8191 InvalidateStyleRedraw();
8192 ReconfigureScrollBars();
8196 case SCI_GETWRAPINDENTMODE
:
8197 return vs
.wrapIndentMode
;
8199 case SCI_SETLAYOUTCACHE
:
8200 llc
.SetLevel(static_cast<int>(wParam
));
8203 case SCI_GETLAYOUTCACHE
:
8204 return llc
.GetLevel();
8206 case SCI_SETPOSITIONCACHE
:
8207 posCache
.SetSize(wParam
);
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
);
8222 case SCI_GETSCROLLWIDTH
:
8225 case SCI_SETSCROLLWIDTHTRACKING
:
8226 trackLineWidth
= wParam
!= 0;
8229 case SCI_GETSCROLLWIDTHTRACKING
:
8230 return trackLineWidth
;
8236 case SCI_LINESSPLIT
:
8237 LinesSplit(static_cast<int>(wParam
));
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;
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
);
8266 case SCI_GETCARETSTICKY
:
8269 case SCI_TOGGLECARETSTICKY
:
8270 caretSticky
= !caretSticky
;
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;
8283 ReconfigureScrollBars();
8287 case SCI_GETHSCROLLBAR
:
8288 return horizontalScrollBarVisible
;
8290 case SCI_SETVSCROLLBAR
:
8291 if (verticalScrollBarVisible
!= (wParam
!= 0)) {
8292 verticalScrollBarVisible
= wParam
!= 0;
8294 ReconfigureScrollBars();
8295 if (verticalScrollBarVisible
)
8296 SetVerticalScrollPos();
8300 case SCI_GETVSCROLLBAR
:
8301 return verticalScrollBarVisible
;
8303 case SCI_SETINDENTATIONGUIDES
:
8304 vs
.viewIndentationGuides
= IndentView(wParam
);
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
);
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
))) {
8328 cs
.InsertLines(0, pdoc
->LinesTotal() - 1);
8329 SetAnnotationHeights(0, pdoc
->LinesTotal());
8330 InvalidateStyleRedraw();
8331 SetRepresentations();
8336 case SCI_GETCODEPAGE
:
8337 return pdoc
->dbcsCodePage
;
8339 #ifdef INCLUDE_DEPRECATED_FEATURES
8340 case SCI_SETUSEPALETTE
:
8341 InvalidateStyleRedraw();
8344 case SCI_GETUSEPALETTE
:
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();
8358 case SCI_MARKERSYMBOLDEFINED
:
8359 if (wParam
<= MARKER_MAX
)
8360 return vs
.markers
[wParam
].markType
;
8364 case SCI_MARKERSETFORE
:
8365 if (wParam
<= MARKER_MAX
)
8366 vs
.markers
[wParam
].fore
= ColourDesired(static_cast<long>(lParam
));
8367 InvalidateStyleData();
8370 case SCI_MARKERSETBACKSELECTED
:
8371 if (wParam
<= MARKER_MAX
)
8372 vs
.markers
[wParam
].backSelected
= ColourDesired(static_cast<long>(lParam
));
8373 InvalidateStyleData();
8376 case SCI_MARKERENABLEHIGHLIGHT
:
8377 highlightDelimiter
.isEnabled
= wParam
== 1;
8380 case SCI_MARKERSETBACK
:
8381 if (wParam
<= MARKER_MAX
)
8382 vs
.markers
[wParam
].back
= ColourDesired(static_cast<long>(lParam
));
8383 InvalidateStyleData();
8386 case SCI_MARKERSETALPHA
:
8387 if (wParam
<= MARKER_MAX
)
8388 vs
.markers
[wParam
].alpha
= static_cast<int>(lParam
);
8389 InvalidateStyleRedraw();
8391 case SCI_MARKERADD
: {
8392 int markerID
= pdoc
->AddMark(static_cast<int>(wParam
), static_cast<int>(lParam
));
8395 case SCI_MARKERADDSET
:
8397 pdoc
->AddMarkSet(static_cast<int>(wParam
), static_cast<int>(lParam
));
8400 case SCI_MARKERDELETE
:
8401 pdoc
->DeleteMark(static_cast<int>(wParam
), static_cast<int>(lParam
));
8404 case SCI_MARKERDELETEALL
:
8405 pdoc
->DeleteAllMarks(static_cast<int>(wParam
));
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)
8422 case SCI_MARKERDEFINEPIXMAP
:
8423 if (wParam
<= MARKER_MAX
) {
8424 vs
.markers
[wParam
].SetXPM(CharPtrFromSPtr(lParam
));
8425 vs
.CalcLargestMarkerHeight();
8427 InvalidateStyleData();
8431 case SCI_RGBAIMAGESETWIDTH
:
8432 sizeRGBAImage
.x
= static_cast<XYPOSITION
>(wParam
);
8435 case SCI_RGBAIMAGESETHEIGHT
:
8436 sizeRGBAImage
.y
= static_cast<XYPOSITION
>(wParam
);
8439 case SCI_RGBAIMAGESETSCALE
:
8440 scaleRGBAImage
= static_cast<float>(wParam
);
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();
8452 case SCI_SETMARGINTYPEN
:
8453 if (ValidMargin(wParam
)) {
8454 vs
.ms
[wParam
].style
= static_cast<int>(lParam
);
8455 InvalidateStyleRedraw();
8459 case SCI_GETMARGINTYPEN
:
8460 if (ValidMargin(wParam
))
8461 return vs
.ms
[wParam
].style
;
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();
8476 case SCI_GETMARGINWIDTHN
:
8477 if (ValidMargin(wParam
))
8478 return vs
.ms
[wParam
].width
;
8482 case SCI_SETMARGINMASKN
:
8483 if (ValidMargin(wParam
)) {
8484 vs
.ms
[wParam
].mask
= static_cast<int>(lParam
);
8485 InvalidateStyleRedraw();
8489 case SCI_GETMARGINMASKN
:
8490 if (ValidMargin(wParam
))
8491 return vs
.ms
[wParam
].mask
;
8495 case SCI_SETMARGINSENSITIVEN
:
8496 if (ValidMargin(wParam
)) {
8497 vs
.ms
[wParam
].sensitive
= lParam
!= 0;
8498 InvalidateStyleRedraw();
8502 case SCI_GETMARGINSENSITIVEN
:
8503 if (ValidMargin(wParam
))
8504 return vs
.ms
[wParam
].sensitive
? 1 : 0;
8508 case SCI_SETMARGINCURSORN
:
8509 if (ValidMargin(wParam
))
8510 vs
.ms
[wParam
].cursor
= static_cast<int>(lParam
);
8513 case SCI_GETMARGINCURSORN
:
8514 if (ValidMargin(wParam
))
8515 return vs
.ms
[wParam
].cursor
;
8519 case SCI_STYLECLEARALL
:
8521 InvalidateStyleRedraw();
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
);
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();
8563 case SCI_SETSTYLEBITS
:
8564 vs
.EnsureStyle(0xff);
8567 case SCI_GETSTYLEBITS
:
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();
8585 case SCI_GETCARETLINEVISIBLEALWAYS
:
8586 return vs
.alwaysShowCaretLineBackground
;
8587 case SCI_SETCARETLINEVISIBLEALWAYS
:
8588 vs
.alwaysShowCaretLineBackground
= wParam
!= 0;
8589 InvalidateStyleRedraw();
8592 case SCI_GETCARETLINEBACK
:
8593 return vs
.caretLineBackground
.AsLong();
8594 case SCI_SETCARETLINEBACK
:
8595 vs
.caretLineBackground
= static_cast<int>(wParam
);
8596 InvalidateStyleRedraw();
8598 case SCI_GETCARETLINEBACKALPHA
:
8599 return vs
.caretLineAlpha
;
8600 case SCI_SETCARETLINEBACKALPHA
:
8601 vs
.caretLineAlpha
= static_cast<int>(wParam
);
8602 InvalidateStyleRedraw();
8607 case SCI_VISIBLEFROMDOCLINE
:
8608 return cs
.DisplayFromDoc(static_cast<int>(wParam
));
8610 case SCI_DOCLINEFROMVISIBLE
:
8611 return cs
.DocFromDisplay(static_cast<int>(wParam
));
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
))
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
));
8633 cs
.SetVisible(static_cast<int>(wParam
), static_cast<int>(lParam
), true);
8640 cs
.SetVisible(static_cast<int>(wParam
), static_cast<int>(lParam
), false);
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);
8655 case SCI_GETFOLDEXPANDED
:
8656 return cs
.GetExpanded(static_cast<int>(wParam
));
8658 case SCI_SETAUTOMATICFOLD
:
8659 foldAutomatic
= static_cast<int>(wParam
);
8662 case SCI_GETAUTOMATICFOLD
:
8663 return foldAutomatic
;
8665 case SCI_SETFOLDFLAGS
:
8666 foldFlags
= static_cast<int>(wParam
);
8670 case SCI_TOGGLEFOLD
:
8671 FoldLine(static_cast<int>(wParam
), SC_FOLDACTION_TOGGLE
);
8675 FoldLine(static_cast<int>(wParam
), static_cast<int>(lParam
));
8678 case SCI_FOLDCHILDREN
:
8679 FoldExpand(static_cast<int>(wParam
), static_cast<int>(lParam
), pdoc
->GetLevel(static_cast<int>(wParam
)));
8683 FoldAll(static_cast<int>(wParam
));
8686 case SCI_EXPANDCHILDREN
:
8687 FoldExpand(static_cast<int>(wParam
), SC_FOLDACTION_EXPAND
, static_cast<int>(lParam
));
8690 case SCI_CONTRACTEDFOLDNEXT
:
8691 return ContractedFoldNext(static_cast<int>(wParam
));
8693 case SCI_ENSUREVISIBLE
:
8694 EnsureLineVisible(static_cast<int>(wParam
), false);
8697 case SCI_ENSUREVISIBLEENFORCEPOLICY
:
8698 EnsureLineVisible(static_cast<int>(wParam
), true);
8701 case SCI_SCROLLRANGE
:
8702 ScrollRange(SelectionRange(static_cast<int>(wParam
), static_cast<int>(lParam
)));
8705 case SCI_SEARCHANCHOR
:
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
);
8718 case SCI_SETYCARETPOLICY
:
8719 caretYPolicy
= static_cast<int>(wParam
);
8720 caretYSlop
= static_cast<int>(lParam
);
8723 case SCI_SETVISIBLEPOLICY
:
8724 visiblePolicy
= static_cast<int>(wParam
);
8725 visibleSlop
= static_cast<int>(lParam
);
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();
8737 case SCI_SETSELBACK
:
8738 vs
.selColours
.back
= ColourOptional(wParam
, lParam
);
8739 vs
.selAdditionalBackground
= ColourDesired(static_cast<long>(lParam
));
8740 InvalidateStyleRedraw();
8743 case SCI_SETSELALPHA
:
8744 vs
.selAlpha
= static_cast<int>(wParam
);
8745 vs
.selAdditionalAlpha
= static_cast<int>(wParam
);
8746 InvalidateStyleRedraw();
8749 case SCI_GETSELALPHA
:
8752 case SCI_GETSELEOLFILLED
:
8753 return vs
.selEOLFilled
;
8755 case SCI_SETSELEOLFILLED
:
8756 vs
.selEOLFilled
= wParam
!= 0;
8757 InvalidateStyleRedraw();
8760 case SCI_SETWHITESPACEFORE
:
8761 vs
.whitespaceColours
.fore
= ColourOptional(wParam
, lParam
);
8762 InvalidateStyleRedraw();
8765 case SCI_SETWHITESPACEBACK
:
8766 vs
.whitespaceColours
.back
= ColourOptional(wParam
, lParam
);
8767 InvalidateStyleRedraw();
8770 case SCI_SETCARETFORE
:
8771 vs
.caretcolour
= ColourDesired(static_cast<long>(wParam
));
8772 InvalidateStyleRedraw();
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
);
8782 /* Default to the line caret */
8783 vs
.caretStyle
= CARETSTYLE_LINE
;
8784 InvalidateStyleRedraw();
8787 case SCI_GETCARETSTYLE
:
8788 return vs
.caretStyle
;
8790 case SCI_SETCARETWIDTH
:
8791 if (static_cast<int>(wParam
) <= 0)
8793 else if (wParam
>= 3)
8796 vs
.caretWidth
= static_cast<int>(wParam
);
8797 InvalidateStyleRedraw();
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
));
8808 case SCI_CLEARCMDKEY
:
8809 kmap
.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam
)),
8810 Platform::HighShortFromLong(static_cast<long>(wParam
)), SCI_NULL
);
8813 case SCI_CLEARALLCMDKEYS
:
8817 case SCI_INDICSETSTYLE
:
8818 if (wParam
<= INDIC_MAX
) {
8819 vs
.indicators
[wParam
].style
= static_cast<int>(lParam
);
8820 InvalidateStyleRedraw();
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();
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();
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();
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();
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
));
8870 case SCI_GETINDICATORCURRENT
:
8871 return pdoc
->decorations
.GetCurrentIndicator();
8872 case SCI_SETINDICATORVALUE
:
8873 pdoc
->decorations
.SetCurrentValue(static_cast<int>(wParam
));
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
));
8882 case SCI_INDICATORCLEARRANGE
:
8883 pdoc
->DecorationFillRange(static_cast<int>(wParam
), 0, static_cast<int>(lParam
));
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
));
8899 case SCI_LINEDOWNEXTEND
:
8901 case SCI_PARADOWNEXTEND
:
8903 case SCI_LINEUPEXTEND
:
8905 case SCI_PARAUPEXTEND
:
8907 case SCI_CHARLEFTEXTEND
:
8909 case SCI_CHARRIGHTEXTEND
:
8911 case SCI_WORDLEFTEXTEND
:
8913 case SCI_WORDRIGHTEXTEND
:
8914 case SCI_WORDLEFTEND
:
8915 case SCI_WORDLEFTENDEXTEND
:
8916 case SCI_WORDRIGHTEND
:
8917 case SCI_WORDRIGHTENDEXTEND
:
8919 case SCI_HOMEEXTEND
:
8921 case SCI_LINEENDEXTEND
:
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
:
8939 case SCI_PAGEUPEXTEND
:
8941 case SCI_PAGEDOWNEXTEND
:
8942 case SCI_EDITTOGGLEOVERTYPE
:
8944 case SCI_DELETEBACK
:
8950 case SCI_VCHOMEEXTEND
:
8951 case SCI_VCHOMEWRAP
:
8952 case SCI_VCHOMEWRAPEXTEND
:
8953 case SCI_VCHOMEDISPLAY
:
8954 case SCI_VCHOMEDISPLAYEXTEND
:
8957 case SCI_DELWORDLEFT
:
8958 case SCI_DELWORDRIGHT
:
8959 case SCI_DELWORDRIGHTEND
:
8960 case SCI_DELLINELEFT
:
8961 case SCI_DELLINERIGHT
:
8964 case SCI_LINEDELETE
:
8965 case SCI_LINETRANSPOSE
:
8966 case SCI_LINEDUPLICATE
:
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
);
8996 case SCI_BRACEHIGHLIGHTINDICATOR
:
8997 if (lParam
>= 0 && lParam
<= INDIC_MAX
) {
8998 vs
.braceHighlightIndicatorSet
= wParam
!= 0;
8999 vs
.braceHighlightIndicator
= static_cast<int>(lParam
);
9003 case SCI_BRACEBADLIGHT
:
9004 SetBraceHighlight(static_cast<int>(wParam
), -1, STYLE_BRACEBAD
);
9007 case SCI_BRACEBADLIGHTINDICATOR
:
9008 if (lParam
>= 0 && lParam
<= INDIC_MAX
) {
9009 vs
.braceBadLightIndicatorSet
= wParam
!= 0;
9010 vs
.braceBadLightIndicator
= static_cast<int>(lParam
);
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
:
9022 case SCI_SETVIEWEOL
:
9023 vs
.viewEOL
= wParam
!= 0;
9024 InvalidateStyleRedraw();
9028 vs
.zoomLevel
= static_cast<int>(wParam
);
9029 InvalidateStyleRedraw();
9034 return vs
.zoomLevel
;
9036 case SCI_GETEDGECOLUMN
:
9039 case SCI_SETEDGECOLUMN
:
9040 vs
.theEdge
= static_cast<int>(wParam
);
9041 InvalidateStyleRedraw();
9044 case SCI_GETEDGEMODE
:
9045 return vs
.edgeState
;
9047 case SCI_SETEDGEMODE
:
9048 vs
.edgeState
= static_cast<int>(wParam
);
9049 InvalidateStyleRedraw();
9052 case SCI_GETEDGECOLOUR
:
9053 return vs
.edgecolour
.AsLong();
9055 case SCI_SETEDGECOLOUR
:
9056 vs
.edgecolour
= ColourDesired(static_cast<long>(wParam
));
9057 InvalidateStyleRedraw();
9060 case SCI_GETDOCPOINTER
:
9061 return reinterpret_cast<sptr_t
>(pdoc
);
9063 case SCI_SETDOCPOINTER
:
9065 SetDocPointer(reinterpret_cast<Document
*>(lParam
));
9068 case SCI_CREATEDOCUMENT
: {
9069 Document
*doc
= new Document();
9071 return reinterpret_cast<sptr_t
>(doc
);
9074 case SCI_ADDREFDOCUMENT
:
9075 (reinterpret_cast<Document
*>(lParam
))->AddRef();
9078 case SCI_RELEASEDOCUMENT
:
9079 (reinterpret_cast<Document
*>(lParam
))->Release();
9082 case SCI_CREATELOADER
: {
9083 Document
*doc
= new Document();
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
);
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
9102 case SCI_SETLENGTHFORENCODE
:
9103 lengthForEncode
= static_cast<int>(wParam
);
9106 case SCI_SELECTIONISRECTANGLE
:
9107 return sel
.selType
== Selection::selRectangle
? 1 : 0;
9109 case SCI_SETSELECTIONMODE
: {
9112 sel
.SetMoveExtends(!sel
.MoveExtends() || (sel
.selType
!= Selection::selStream
));
9113 sel
.selType
= Selection::selStream
;
9115 case SC_SEL_RECTANGLE
:
9116 sel
.SetMoveExtends(!sel
.MoveExtends() || (sel
.selType
!= Selection::selRectangle
));
9117 sel
.selType
= Selection::selRectangle
;
9120 sel
.SetMoveExtends(!sel
.MoveExtends() || (sel
.selType
!= Selection::selLines
));
9121 sel
.selType
= Selection::selLines
;
9124 sel
.SetMoveExtends(!sel
.MoveExtends() || (sel
.selType
!= Selection::selThin
));
9125 sel
.selType
= Selection::selThin
;
9128 sel
.SetMoveExtends(!sel
.MoveExtends() || (sel
.selType
!= Selection::selStream
));
9129 sel
.selType
= Selection::selStream
;
9131 InvalidateSelection(sel
.RangeMain(), true);
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
:
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;
9164 case SCI_GETOVERTYPE
:
9165 return inOverstrike
? 1 : 0;
9168 SetFocusState(wParam
!= 0);
9175 errorStatus
= static_cast<int>(wParam
);
9181 case SCI_SETMOUSEDOWNCAPTURES
:
9182 mouseDownCaptures
= wParam
!= 0;
9185 case SCI_GETMOUSEDOWNCAPTURES
:
9186 return mouseDownCaptures
;
9189 cursorMode
= static_cast<int>(wParam
);
9190 DisplayCursor(Window::cursorText
);
9196 case SCI_SETCONTROLCHARSYMBOL
:
9197 vs
.controlCharSymbol
= static_cast<int>(wParam
);
9198 InvalidateStyleRedraw();
9201 case SCI_GETCONTROLCHARSYMBOL
:
9202 return vs
.controlCharSymbol
;
9204 case SCI_SETREPRESENTATION
:
9205 reprs
.SetRepresentation(reinterpret_cast<const char *>(wParam
), CharPtrFromSPtr(lParam
));
9208 case SCI_GETREPRESENTATION
: {
9209 const Representation
*repr
= reprs
.RepresentationFromCharacter(
9210 reinterpret_cast<const char *>(wParam
), UTF8MaxBytes
);
9212 return StringResult(lParam
, repr
->stringRep
.c_str());
9217 case SCI_CLEARREPRESENTATION
:
9218 reprs
.ClearRepresentation(reinterpret_cast<const char *>(wParam
));
9221 case SCI_STARTRECORD
:
9222 recordingMacro
= true;
9225 case SCI_STOPRECORD
:
9226 recordingMacro
= false;
9229 case SCI_MOVECARETINSIDEVIEW
:
9230 MoveCaretInsideView();
9233 case SCI_SETFOLDMARGINCOLOUR
:
9234 vs
.foldmarginColour
= ColourOptional(wParam
, lParam
);
9235 InvalidateStyleRedraw();
9238 case SCI_SETFOLDMARGINHICOLOUR
:
9239 vs
.foldmarginHighlightColour
= ColourOptional(wParam
, lParam
);
9240 InvalidateStyleRedraw();
9243 case SCI_SETHOTSPOTACTIVEFORE
:
9244 vs
.hotspotColours
.fore
= ColourOptional(wParam
, lParam
);
9245 InvalidateStyleRedraw();
9248 case SCI_GETHOTSPOTACTIVEFORE
:
9249 return vs
.hotspotColours
.fore
.AsLong();
9251 case SCI_SETHOTSPOTACTIVEBACK
:
9252 vs
.hotspotColours
.back
= ColourOptional(wParam
, lParam
);
9253 InvalidateStyleRedraw();
9256 case SCI_GETHOTSPOTACTIVEBACK
:
9257 return vs
.hotspotColours
.back
.AsLong();
9259 case SCI_SETHOTSPOTACTIVEUNDERLINE
:
9260 vs
.hotspotUnderline
= wParam
!= 0;
9261 InvalidateStyleRedraw();
9264 case SCI_GETHOTSPOTACTIVEUNDERLINE
:
9265 return vs
.hotspotUnderline
? 1 : 0;
9267 case SCI_SETHOTSPOTSINGLELINE
:
9268 vs
.hotspotSingleLine
= wParam
!= 0;
9269 InvalidateStyleRedraw();
9272 case SCI_GETHOTSPOTSINGLELINE
:
9273 return vs
.hotspotSingleLine
? 1 : 0;
9275 case SCI_SETPASTECONVERTENDINGS
:
9276 convertPastes
= wParam
!= 0;
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();
9296 case SCI_GETEXTRAASCENT
:
9297 return vs
.extraAscent
;
9299 case SCI_SETEXTRADESCENT
:
9300 vs
.extraDescent
= static_cast<int>(wParam
);
9301 InvalidateStyleRedraw();
9304 case SCI_GETEXTRADESCENT
:
9305 return vs
.extraDescent
;
9307 case SCI_MARGINSETSTYLEOFFSET
:
9308 vs
.marginStyleOffset
= static_cast<int>(wParam
);
9309 InvalidateStyleRedraw();
9312 case SCI_MARGINGETSTYLEOFFSET
:
9313 return vs
.marginStyleOffset
;
9315 case SCI_SETMARGINOPTIONS
:
9316 marginOptions
= static_cast<int>(wParam
);
9319 case SCI_GETMARGINOPTIONS
:
9320 return marginOptions
;
9322 case SCI_MARGINSETTEXT
:
9323 pdoc
->MarginSetText(static_cast<int>(wParam
), CharPtrFromSPtr(lParam
));
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
));
9335 case SCI_MARGINGETSTYLE
: {
9336 const StyledText st
= pdoc
->MarginStyledText(static_cast<int>(wParam
));
9340 case SCI_MARGINSETSTYLES
:
9341 pdoc
->MarginSetStyles(static_cast<int>(wParam
), reinterpret_cast<const unsigned char *>(lParam
));
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();
9353 case SCI_ANNOTATIONSETTEXT
:
9354 pdoc
->AnnotationSetText(static_cast<int>(wParam
), CharPtrFromSPtr(lParam
));
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
));
9367 case SCI_ANNOTATIONSETSTYLE
:
9368 pdoc
->AnnotationSetStyle(static_cast<int>(wParam
), static_cast<int>(lParam
));
9371 case SCI_ANNOTATIONSETSTYLES
:
9372 pdoc
->AnnotationSetStyles(static_cast<int>(wParam
), reinterpret_cast<const unsigned char *>(lParam
));
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();
9387 case SCI_ANNOTATIONSETVISIBLE
:
9388 SetAnnotationVisible(static_cast<int>(wParam
));
9391 case SCI_ANNOTATIONGETVISIBLE
:
9392 return vs
.annotationVisible
;
9394 case SCI_ANNOTATIONSETSTYLEOFFSET
:
9395 vs
.annotationStyleOffset
= static_cast<int>(wParam
);
9396 InvalidateStyleRedraw();
9399 case SCI_ANNOTATIONGETSTYLEOFFSET
:
9400 return vs
.annotationStyleOffset
;
9402 case SCI_RELEASEALLEXTENDEDSTYLES
:
9403 vs
.ReleaseAllExtendedStyles();
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
);
9413 case SCI_SETMOUSESELECTIONRECTANGULARSWITCH
:
9414 mouseSelectionRectangularSwitch
= wParam
!= 0;
9417 case SCI_GETMOUSESELECTIONRECTANGULARSWITCH
:
9418 return mouseSelectionRectangularSwitch
;
9420 case SCI_SETMULTIPLESELECTION
:
9421 multipleSelection
= wParam
!= 0;
9425 case SCI_GETMULTIPLESELECTION
:
9426 return multipleSelection
;
9428 case SCI_SETADDITIONALSELECTIONTYPING
:
9429 additionalSelectionTyping
= wParam
!= 0;
9433 case SCI_GETADDITIONALSELECTIONTYPING
:
9434 return additionalSelectionTyping
;
9436 case SCI_SETMULTIPASTE
:
9437 multiPasteMode
= static_cast<int>(wParam
);
9440 case SCI_GETMULTIPASTE
:
9441 return multiPasteMode
;
9443 case SCI_SETADDITIONALCARETSBLINK
:
9444 additionalCaretsBlink
= wParam
!= 0;
9448 case SCI_GETADDITIONALCARETSBLINK
:
9449 return additionalCaretsBlink
;
9451 case SCI_SETADDITIONALCARETSVISIBLE
:
9452 additionalCaretsVisible
= wParam
!= 0;
9456 case SCI_GETADDITIONALCARETSVISIBLE
:
9457 return additionalCaretsVisible
;
9459 case SCI_GETSELECTIONS
:
9462 case SCI_GETSELECTIONEMPTY
:
9465 case SCI_CLEARSELECTIONS
:
9470 case SCI_SETSELECTION
:
9471 sel
.SetSelection(SelectionRange(static_cast<int>(wParam
), static_cast<int>(lParam
)));
9475 case SCI_ADDSELECTION
:
9476 sel
.AddSelection(SelectionRange(static_cast<int>(wParam
), static_cast<int>(lParam
)));
9480 case SCI_DROPSELECTIONN
:
9481 sel
.DropSelection(static_cast<int>(wParam
));
9485 case SCI_SETMAINSELECTION
:
9486 sel
.SetMain(static_cast<int>(wParam
));
9490 case SCI_GETMAINSELECTION
:
9493 case SCI_SETSELECTIONNCARET
:
9494 sel
.Range(wParam
).caret
.SetPosition(static_cast<int>(lParam
));
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
));
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
));
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
));
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
));
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
));
9537 case SCI_GETSELECTIONNEND
:
9538 return sel
.Range(wParam
).End().Position();
9540 case SCI_SETRECTANGULARSELECTIONCARET
:
9541 if (!sel
.IsRectangular())
9543 sel
.selType
= Selection::selRectangle
;
9544 sel
.Rectangular().caret
.SetPosition(static_cast<int>(wParam
));
9545 SetRectangularRange();
9549 case SCI_GETRECTANGULARSELECTIONCARET
:
9550 return sel
.Rectangular().caret
.Position();
9552 case SCI_SETRECTANGULARSELECTIONANCHOR
:
9553 if (!sel
.IsRectangular())
9555 sel
.selType
= Selection::selRectangle
;
9556 sel
.Rectangular().anchor
.SetPosition(static_cast<int>(wParam
));
9557 SetRectangularRange();
9561 case SCI_GETRECTANGULARSELECTIONANCHOR
:
9562 return sel
.Rectangular().anchor
.Position();
9564 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE
:
9565 if (!sel
.IsRectangular())
9567 sel
.selType
= Selection::selRectangle
;
9568 sel
.Rectangular().caret
.SetVirtualSpace(static_cast<int>(wParam
));
9569 SetRectangularRange();
9573 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE
:
9574 return sel
.Rectangular().caret
.VirtualSpace();
9576 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE
:
9577 if (!sel
.IsRectangular())
9579 sel
.selType
= Selection::selRectangle
;
9580 sel
.Rectangular().anchor
.SetVirtualSpace(static_cast<int>(wParam
));
9581 SetRectangularRange();
9585 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE
:
9586 return sel
.Rectangular().anchor
.VirtualSpace();
9588 case SCI_SETVIRTUALSPACEOPTIONS
:
9589 virtualSpaceOptions
= static_cast<int>(wParam
);
9592 case SCI_GETVIRTUALSPACEOPTIONS
:
9593 return virtualSpaceOptions
;
9595 case SCI_SETADDITIONALSELFORE
:
9596 vs
.selAdditionalForeground
= ColourDesired(static_cast<long>(wParam
));
9597 InvalidateStyleRedraw();
9600 case SCI_SETADDITIONALSELBACK
:
9601 vs
.selAdditionalBackground
= ColourDesired(static_cast<long>(wParam
));
9602 InvalidateStyleRedraw();
9605 case SCI_SETADDITIONALSELALPHA
:
9606 vs
.selAdditionalAlpha
= static_cast<int>(wParam
);
9607 InvalidateStyleRedraw();
9610 case SCI_GETADDITIONALSELALPHA
:
9611 return vs
.selAdditionalAlpha
;
9613 case SCI_SETADDITIONALCARETFORE
:
9614 vs
.additionalCaretColour
= ColourDesired(static_cast<long>(wParam
));
9615 InvalidateStyleRedraw();
9618 case SCI_GETADDITIONALCARETFORE
:
9619 return vs
.additionalCaretColour
.AsLong();
9621 case SCI_ROTATESELECTION
:
9623 InvalidateSelection(sel
.RangeMain(), true);
9626 case SCI_SWAPMAINANCHORCARET
:
9627 InvalidateSelection(sel
.RangeMain());
9628 sel
.RangeMain() = SelectionRange(sel
.RangeMain().anchor
, sel
.RangeMain().caret
);
9631 case SCI_CHANGELEXERSTATE
:
9632 pdoc
->ChangeLexerState(static_cast<int>(wParam
), static_cast<int>(lParam
));
9635 case SCI_SETIDENTIFIER
:
9636 SetCtrlID(static_cast<int>(wParam
));
9639 case SCI_GETIDENTIFIER
:
9642 case SCI_SETTECHNOLOGY
:
9643 // No action by default
9646 case SCI_GETTECHNOLOGY
:
9649 case SCI_COUNTCHARACTERS
:
9650 return pdoc
->CountCharacters(static_cast<int>(wParam
), static_cast<int>(lParam
));
9653 return DefWndProc(iMessage
, wParam
, lParam
);
9655 //Platform::DebugPrintf("end wnd proc\n");