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.
18 #include <string_view>
22 #include <forward_list>
33 #include "ScintillaTypes.h"
34 #include "ScintillaMessages.h"
35 #include "ScintillaStructures.h"
39 #include "Debugging.h"
43 #include "CharacterType.h"
44 #include "CharacterCategoryMap.h"
46 #include "UniqueString.h"
47 #include "SplitVector.h"
48 #include "Partitioning.h"
49 #include "RunStyles.h"
50 #include "ContractionState.h"
51 #include "CellBuffer.h"
54 #include "Indicator.h"
55 #include "LineMarker.h"
57 #include "ViewStyle.h"
58 #include "CharClassify.h"
59 #include "Decoration.h"
60 #include "CaseFolder.h"
62 #include "UniConversion.h"
64 #include "Selection.h"
65 #include "PositionCache.h"
66 #include "EditModel.h"
67 #include "MarginView.h"
70 #include "ElapsedPeriod.h"
72 using namespace Scintilla
;
73 using namespace Scintilla::Internal
;
78 return whether this modification represents an operation that
79 may reasonably be deferred (not done now OR [possibly] at all)
81 constexpr bool CanDeferToLastStep(const DocModification
&mh
) noexcept
{
82 if (FlagSet(mh
.modificationType
, (ModificationFlags::BeforeInsert
| ModificationFlags::BeforeDelete
)))
83 return true; // CAN skip
84 if (!FlagSet(mh
.modificationType
, (ModificationFlags::Undo
| ModificationFlags::Redo
)))
85 return false; // MUST do
86 if (FlagSet(mh
.modificationType
, ModificationFlags::MultiStepUndoRedo
))
87 return true; // CAN skip
88 return false; // PRESUMABLY must do
91 constexpr bool CanEliminate(const DocModification
&mh
) noexcept
{
93 FlagSet(mh
.modificationType
, (ModificationFlags::BeforeInsert
| ModificationFlags::BeforeDelete
));
97 return whether this modification represents the FINAL step
98 in a [possibly lengthy] multi-step Undo/Redo sequence
100 constexpr bool IsLastStep(const DocModification
&mh
) noexcept
{
102 FlagSet(mh
.modificationType
, (ModificationFlags::Undo
| ModificationFlags::Redo
))
103 && (FlagSet(mh
.modificationType
, ModificationFlags::MultiStepUndoRedo
))
104 && (FlagSet(mh
.modificationType
, ModificationFlags::LastStepInUndoRedo
))
105 && (FlagSet(mh
.modificationType
, ModificationFlags::MultilineUndoRedo
));
110 Timer::Timer() noexcept
:
111 ticking(false), ticksToWait(0), tickerID
{} {}
113 Idler::Idler() noexcept
:
114 state(false), idlerID(nullptr) {}
116 static constexpr bool IsAllSpacesOrTabs(std::string_view sv
) noexcept
{
117 for (const char ch
: sv
) {
118 // This is safe because IsSpaceOrTab() will return false for null terminators
119 if (!IsSpaceOrTab(ch
))
125 Editor::Editor() : durationWrapOneByte(0.000001, 0.00000001, 0.00001) {
129 technology
= Technology::Default
;
130 scaleRGBAImage
= 100.0f
;
132 cursorMode
= CursorShape::Normal
;
134 errorStatus
= Status::Ok
;
135 mouseDownCaptures
= true;
136 mouseWheelCaptures
= true;
139 doubleClickCloseThreshold
= Point(3, 3);
140 dwellDelay
= TimeForever
;
141 ticksToDwell
= TimeForever
;
145 inDragDrop
= DragDrop::none
;
146 dropWentOutside
= false;
147 posDrop
= SelectionPosition(Sci::invalidPosition
);
148 hotSpotClickPos
= Sci::invalidPosition
;
149 selectionUnit
= TextUnit::character
;
153 originalAnchorPos
= 0;
154 wordSelectAnchorStartPos
= 0;
155 wordSelectAnchorEndPos
= 0;
156 wordSelectInitialCaretPos
= -1;
158 caretPolicies
.x
= { CaretPolicy::Slop
| CaretPolicy::Even
, 50 };
159 caretPolicies
.y
= { CaretPolicy::Even
, 0 };
161 visiblePolicy
= { 0, 0 };
166 horizontalScrollBarVisible
= true;
168 verticalScrollBarVisible
= true;
169 endAtLastLine
= true;
170 caretSticky
= CaretSticky::Off
;
171 marginOptions
= MarginOption::None
;
172 mouseSelectionRectangularSwitch
= false;
173 multipleSelection
= false;
174 additionalSelectionTyping
= false;
175 multiPasteMode
= MultiPaste::Once
;
176 virtualSpaceOptions
= VirtualSpace::None
;
178 targetRange
= SelectionSegment();
179 searchFlags
= FindOption::None
;
184 lengthForEncode
= -1;
186 needUpdateUI
= Update::None
;
187 ContainerNeedsUpdate(Update::Content
);
189 paintState
= PaintState::notPainting
;
190 paintAbandonedByStyling
= false;
191 paintingAllText
= false;
192 willRedrawAll
= false;
193 idleStyling
= IdleStyling::None
;
194 needIdleStyling
= false;
196 modEventMask
= ModificationFlags::EventMaskAll
;
197 commandEvents
= true;
199 pdoc
->AddWatcher(this, nullptr);
201 recordingMacro
= false;
202 foldAutomatic
= AutomaticFold::None
;
204 convertPastes
= true;
206 SetRepresentations();
210 pdoc
->RemoveWatcher(this, nullptr);
213 void Editor::Finalise() {
218 void Editor::SetRepresentations() {
219 reprs
.SetDefaultRepresentations(pdoc
->dbcsCodePage
);
222 void Editor::DropGraphics() noexcept
{
223 marginView
.DropGraphics();
227 void Editor::InvalidateStyleData() noexcept
{
229 vs
.technology
= technology
;
231 view
.llc
.Invalidate(LineLayout::ValidLevel::invalid
);
232 view
.posCache
->Clear();
235 void Editor::InvalidateStyleRedraw() {
237 InvalidateStyleData();
241 void Editor::RefreshStyleData() {
244 AutoSurface
surface(this);
246 vs
.Refresh(*surface
, pdoc
->tabInChars
);
249 SetRectangularRange();
253 bool Editor::HasMarginWindow() const noexcept
{
254 return wMargin
.Created();
257 Point
Editor::GetVisibleOriginInMain() const {
261 PointDocument
Editor::DocumentPointFromView(Point ptView
) const {
262 PointDocument
ptDocument(ptView
);
263 if (HasMarginWindow()) {
264 const Point ptOrigin
= GetVisibleOriginInMain();
265 ptDocument
.x
+= ptOrigin
.x
;
266 ptDocument
.y
+= ptOrigin
.y
;
268 ptDocument
.x
+= xOffset
;
269 ptDocument
.y
+= topLine
* vs
.lineHeight
;
274 Sci::Line
Editor::TopLineOfMain() const noexcept
{
275 if (HasMarginWindow())
281 Point
Editor::ClientSize() const {
282 const PRectangle rcClient
= GetClientRectangle();
283 return Point(rcClient
.Width(), rcClient
.Height());
286 PRectangle
Editor::GetClientRectangle() const {
287 return wMain
.GetClientPosition();
290 PRectangle
Editor::GetClientDrawingRectangle() {
291 return GetClientRectangle();
294 PRectangle
Editor::GetTextRectangle() const {
295 PRectangle rc
= GetClientRectangle();
296 rc
.left
+= vs
.textStart
;
297 rc
.right
-= vs
.rightMarginWidth
;
301 Sci::Line
Editor::LinesOnScreen() const {
302 const Point sizeClient
= ClientSize();
303 const int htClient
= static_cast<int>(sizeClient
.y
);
304 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
305 return htClient
/ vs
.lineHeight
;
308 Sci::Line
Editor::LinesToScroll() const {
309 const Sci::Line retVal
= LinesOnScreen() - 1;
316 Sci::Line
Editor::MaxScrollPos() const {
317 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
318 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
319 Sci::Line retVal
= pcs
->LinesDisplayed();
321 retVal
-= LinesOnScreen();
332 SelectionPosition
Editor::ClampPositionIntoDocument(SelectionPosition sp
) const {
333 if (sp
.Position() < 0) {
334 return SelectionPosition(0);
335 } else if (sp
.Position() > pdoc
->Length()) {
336 return SelectionPosition(pdoc
->Length());
338 // If not at end of line then set offset to 0
339 if (!pdoc
->IsLineEndPosition(sp
.Position()))
340 sp
.SetVirtualSpace(0);
345 Point
Editor::LocationFromPosition(SelectionPosition pos
, PointEnd pe
) {
346 const PRectangle rcClient
= GetTextRectangle();
348 AutoSurface
surface(this);
349 return view
.LocationFromPosition(surface
, *this, pos
, topLine
, vs
, pe
, rcClient
);
352 Point
Editor::LocationFromPosition(Sci::Position pos
, PointEnd pe
) {
353 return LocationFromPosition(SelectionPosition(pos
), pe
);
356 int Editor::XFromPosition(SelectionPosition sp
) {
357 const Point pt
= LocationFromPosition(sp
);
358 return static_cast<int>(pt
.x
) - vs
.textStart
+ xOffset
;
361 SelectionPosition
Editor::SPositionFromLocation(Point pt
, bool canReturnInvalid
, bool charPosition
, bool virtualSpace
) {
363 AutoSurface
surface(this);
365 PRectangle rcClient
= GetTextRectangle();
366 // May be in scroll view coordinates so translate back to main view
367 const Point ptOrigin
= GetVisibleOriginInMain();
368 rcClient
.Move(-ptOrigin
.x
, -ptOrigin
.y
);
370 if (canReturnInvalid
) {
371 if (!rcClient
.Contains(pt
))
372 return SelectionPosition(Sci::invalidPosition
);
373 if (pt
.x
< vs
.textStart
)
374 return SelectionPosition(Sci::invalidPosition
);
376 return SelectionPosition(Sci::invalidPosition
);
378 const PointDocument ptdoc
= DocumentPointFromView(pt
);
379 return view
.SPositionFromLocation(surface
, *this, ptdoc
, canReturnInvalid
,
380 charPosition
, virtualSpace
, vs
, rcClient
);
383 Sci::Position
Editor::PositionFromLocation(Point pt
, bool canReturnInvalid
, bool charPosition
) {
384 return SPositionFromLocation(pt
, canReturnInvalid
, charPosition
, false).Position();
388 * Find the document position corresponding to an x coordinate on a particular document line.
389 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
390 * This method is used for rectangular selections and does not work on wrapped lines.
392 SelectionPosition
Editor::SPositionFromLineX(Sci::Line lineDoc
, int x
) {
394 if (lineDoc
>= pdoc
->LinesTotal())
395 return SelectionPosition(pdoc
->Length());
396 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
397 AutoSurface
surface(this);
398 return view
.SPositionFromLineX(surface
, *this, lineDoc
, x
, vs
);
401 Sci::Position
Editor::PositionFromLineX(Sci::Line lineDoc
, int x
) {
402 return SPositionFromLineX(lineDoc
, x
).Position();
405 Sci::Line
Editor::LineFromLocation(Point pt
) const noexcept
{
406 return pcs
->DocFromDisplay(static_cast<int>(pt
.y
) / vs
.lineHeight
+ topLine
);
409 void Editor::SetTopLine(Sci::Line topLineNew
) {
410 if ((topLine
!= topLineNew
) && (topLineNew
>= 0)) {
411 topLine
= topLineNew
;
412 ContainerNeedsUpdate(Update::VScroll
);
414 posTopLine
= pdoc
->LineStart(pcs
->DocFromDisplay(topLine
));
418 * If painting then abandon the painting because a wider redraw is needed.
419 * @return true if calling code should stop drawing.
421 bool Editor::AbandonPaint() {
422 if ((paintState
== PaintState::painting
) && !paintingAllText
) {
423 paintState
= PaintState::abandoned
;
425 return paintState
== PaintState::abandoned
;
428 void Editor::RedrawRect(PRectangle rc
) {
429 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
431 // Clip the redraw rectangle into the client area
432 const PRectangle rcClient
= GetClientRectangle();
433 if (rc
.top
< rcClient
.top
)
434 rc
.top
= rcClient
.top
;
435 if (rc
.bottom
> rcClient
.bottom
)
436 rc
.bottom
= rcClient
.bottom
;
437 if (rc
.left
< rcClient
.left
)
438 rc
.left
= rcClient
.left
;
439 if (rc
.right
> rcClient
.right
)
440 rc
.right
= rcClient
.right
;
442 if ((rc
.bottom
> rc
.top
) && (rc
.right
> rc
.left
)) {
443 wMain
.InvalidateRectangle(rc
);
447 void Editor::DiscardOverdraw() {
448 // Overridden on platforms that may draw outside visible area.
451 void Editor::Redraw() {
452 if (redrawPendingText
) {
455 //Platform::DebugPrintf("Redraw all\n");
456 const PRectangle rcClient
= GetClientRectangle();
457 wMain
.InvalidateRectangle(rcClient
);
458 if (HasMarginWindow()) {
459 wMargin
.InvalidateAll();
460 } else if (paintState
== PaintState::notPainting
) {
461 redrawPendingText
= true;
465 void Editor::RedrawSelMargin(Sci::Line line
, bool allAfter
) {
466 const bool markersInText
= vs
.maskInLine
|| vs
.maskDrawInText
;
467 if (!HasMarginWindow() || markersInText
) { // May affect text area so may need to abandon and retry
468 if (AbandonPaint()) {
472 if (HasMarginWindow() && markersInText
) {
476 if (redrawPendingMargin
) {
479 PRectangle rcMarkers
= GetClientRectangle();
480 if (!markersInText
) {
481 // Normal case: just draw the margin
482 rcMarkers
.right
= rcMarkers
.left
+ vs
.fixedColumnWidth
;
484 const PRectangle rcMarkersFull
= rcMarkers
;
486 PRectangle rcLine
= RectangleFromRange(Range(pdoc
->LineStart(line
)), 0);
488 // Inflate line rectangle if there are image markers with height larger than line height
489 if (vs
.largestMarkerHeight
> vs
.lineHeight
) {
490 const int delta
= (vs
.largestMarkerHeight
- vs
.lineHeight
+ 1) / 2;
492 rcLine
.bottom
+= delta
;
493 if (rcLine
.top
< rcMarkers
.top
)
494 rcLine
.top
= rcMarkers
.top
;
495 if (rcLine
.bottom
> rcMarkers
.bottom
)
496 rcLine
.bottom
= rcMarkers
.bottom
;
499 rcMarkers
.top
= rcLine
.top
;
501 rcMarkers
.bottom
= rcLine
.bottom
;
502 if (rcMarkers
.Empty())
505 if (HasMarginWindow()) {
506 const Point ptOrigin
= GetVisibleOriginInMain();
507 rcMarkers
.Move(-ptOrigin
.x
, -ptOrigin
.y
);
508 wMargin
.InvalidateRectangle(rcMarkers
);
510 wMain
.InvalidateRectangle(rcMarkers
);
511 if (rcMarkers
== rcMarkersFull
) {
512 redrawPendingMargin
= true;
517 PRectangle
Editor::RectangleFromRange(Range r
, int overlap
) {
518 const Sci::Line minLine
= pcs
->DisplayFromDoc(
519 pdoc
->SciLineFromPosition(r
.First()));
520 const Sci::Line maxLine
= pcs
->DisplayLastFromDoc(
521 pdoc
->SciLineFromPosition(r
.Last()));
522 const PRectangle rcClientDrawing
= GetClientDrawingRectangle();
524 const int leftTextOverlap
= ((xOffset
== 0) && (vs
.leftMarginWidth
> 0)) ? 1 : 0;
525 rc
.left
= static_cast<XYPOSITION
>(vs
.textStart
- leftTextOverlap
);
526 rc
.top
= static_cast<XYPOSITION
>((minLine
- TopLineOfMain()) * vs
.lineHeight
- overlap
);
527 if (rc
.top
< rcClientDrawing
.top
)
528 rc
.top
= rcClientDrawing
.top
;
529 // Extend to right of prepared area if any to prevent artifacts from caret line highlight
530 rc
.right
= rcClientDrawing
.right
;
531 rc
.bottom
= static_cast<XYPOSITION
>((maxLine
- TopLineOfMain() + 1) * vs
.lineHeight
+ overlap
);
536 void Editor::InvalidateRange(Sci::Position start
, Sci::Position end
) {
537 if (redrawPendingText
) {
540 RedrawRect(RectangleFromRange(Range(start
, end
), view
.LinesOverlap() ? vs
.lineOverlap
: 0));
543 Sci::Position
Editor::CurrentPosition() const noexcept
{
544 return sel
.MainCaret();
547 bool Editor::SelectionEmpty() const noexcept
{
551 SelectionPosition
Editor::SelectionStart() noexcept
{
552 return sel
.RangeMain().Start();
555 SelectionPosition
Editor::SelectionEnd() noexcept
{
556 return sel
.RangeMain().End();
559 void Editor::SetRectangularRange() {
560 if (sel
.IsRectangular()) {
561 const int xAnchor
= XFromPosition(sel
.Rectangular().anchor
);
562 int xCaret
= XFromPosition(sel
.Rectangular().caret
);
563 if (sel
.selType
== Selection::SelTypes::thin
) {
566 const Sci::Line lineAnchorRect
=
567 pdoc
->SciLineFromPosition(sel
.Rectangular().anchor
.Position());
568 const Sci::Line lineCaret
=
569 pdoc
->SciLineFromPosition(sel
.Rectangular().caret
.Position());
570 const int increment
= (lineCaret
> lineAnchorRect
) ? 1 : -1;
571 AutoSurface
surface(this);
572 for (Sci::Line line
=lineAnchorRect
; line
!= lineCaret
+increment
; line
+= increment
) {
573 SelectionRange
range(
574 view
.SPositionFromLineX(surface
, *this, line
, xCaret
, vs
),
575 view
.SPositionFromLineX(surface
, *this, line
, xAnchor
, vs
));
576 if (!FlagSet(virtualSpaceOptions
, VirtualSpace::RectangularSelection
))
577 range
.ClearVirtualSpace();
578 if (line
== lineAnchorRect
)
579 sel
.SetSelection(range
);
581 sel
.AddSelectionWithoutTrim(range
);
586 void Editor::ThinRectangularRange() {
587 if (sel
.IsRectangular()) {
588 sel
.selType
= Selection::SelTypes::thin
;
589 if (sel
.Rectangular().caret
< sel
.Rectangular().anchor
) {
590 sel
.Rectangular() = SelectionRange(sel
.Range(sel
.Count()-1).caret
, sel
.Range(0).anchor
);
592 sel
.Rectangular() = SelectionRange(sel
.Range(sel
.Count()-1).anchor
, sel
.Range(0).caret
);
594 SetRectangularRange();
598 void Editor::InvalidateSelection(SelectionRange newMain
, bool invalidateWholeSelection
) {
599 if (sel
.Count() > 1 || !(sel
.RangeMain().anchor
== newMain
.anchor
) || sel
.IsRectangular()) {
600 invalidateWholeSelection
= true;
602 Sci::Position firstAffected
= std::min(sel
.RangeMain().Start().Position(), newMain
.Start().Position());
603 // +1 for lastAffected ensures caret repainted
604 Sci::Position lastAffected
= std::max(newMain
.caret
.Position()+1, newMain
.anchor
.Position());
605 lastAffected
= std::max(lastAffected
, sel
.RangeMain().End().Position());
606 if (invalidateWholeSelection
) {
607 for (size_t r
=0; r
<sel
.Count(); r
++) {
608 firstAffected
= std::min(firstAffected
, sel
.Range(r
).caret
.Position());
609 firstAffected
= std::min(firstAffected
, sel
.Range(r
).anchor
.Position());
610 lastAffected
= std::max(lastAffected
, sel
.Range(r
).caret
.Position()+1);
611 lastAffected
= std::max(lastAffected
, sel
.Range(r
).anchor
.Position());
614 ContainerNeedsUpdate(Update::Selection
);
615 InvalidateRange(firstAffected
, lastAffected
);
618 void Editor::InvalidateWholeSelection() {
619 InvalidateSelection(sel
.RangeMain(), true);
622 /* For Line selection - the anchor and caret are always
623 at the beginning and end of the region lines. */
624 SelectionRange
Editor::LineSelectionRange(SelectionPosition currentPos_
, SelectionPosition anchor_
) const {
625 if (currentPos_
> anchor_
) {
626 anchor_
= SelectionPosition(
627 pdoc
->LineStart(pdoc
->LineFromPosition(anchor_
.Position())));
628 currentPos_
= SelectionPosition(
629 pdoc
->LineEnd(pdoc
->LineFromPosition(currentPos_
.Position())));
631 currentPos_
= SelectionPosition(
632 pdoc
->LineStart(pdoc
->LineFromPosition(currentPos_
.Position())));
633 anchor_
= SelectionPosition(
634 pdoc
->LineEnd(pdoc
->LineFromPosition(anchor_
.Position())));
636 return SelectionRange(currentPos_
, anchor_
);
639 void Editor::SetSelection(SelectionPosition currentPos_
, SelectionPosition anchor_
) {
640 currentPos_
= ClampPositionIntoDocument(currentPos_
);
641 anchor_
= ClampPositionIntoDocument(anchor_
);
642 const Sci::Line currentLine
= pdoc
->SciLineFromPosition(currentPos_
.Position());
643 SelectionRange
rangeNew(currentPos_
, anchor_
);
644 if (sel
.selType
== Selection::SelTypes::lines
) {
645 rangeNew
= LineSelectionRange(currentPos_
, anchor_
);
647 if (sel
.Count() > 1 || !(sel
.RangeMain() == rangeNew
)) {
648 InvalidateSelection(rangeNew
);
650 sel
.RangeMain() = rangeNew
;
651 SetRectangularRange();
653 SetHoverIndicatorPosition(sel
.MainCaret());
655 if (marginView
.highlightDelimiter
.NeedsDrawing(currentLine
)) {
658 QueueIdleWork(WorkItems::updateUI
);
661 void Editor::SetSelection(Sci::Position currentPos_
, Sci::Position anchor_
) {
662 SetSelection(SelectionPosition(currentPos_
), SelectionPosition(anchor_
));
665 // Just move the caret on the main selection
666 void Editor::SetSelection(SelectionPosition currentPos_
) {
667 currentPos_
= ClampPositionIntoDocument(currentPos_
);
668 const Sci::Line currentLine
= pdoc
->SciLineFromPosition(currentPos_
.Position());
669 if (sel
.Count() > 1 || !(sel
.RangeMain().caret
== currentPos_
)) {
670 InvalidateSelection(SelectionRange(currentPos_
));
672 if (sel
.IsRectangular()) {
674 SelectionRange(SelectionPosition(currentPos_
), sel
.Rectangular().anchor
);
675 SetRectangularRange();
676 } else if (sel
.selType
== Selection::SelTypes::lines
) {
677 sel
.RangeMain() = LineSelectionRange(currentPos_
, sel
.RangeMain().anchor
);
680 SelectionRange(SelectionPosition(currentPos_
), sel
.RangeMain().anchor
);
683 SetHoverIndicatorPosition(sel
.MainCaret());
685 if (marginView
.highlightDelimiter
.NeedsDrawing(currentLine
)) {
688 QueueIdleWork(WorkItems::updateUI
);
691 void Editor::SetEmptySelection(SelectionPosition currentPos_
) {
692 const Sci::Line currentLine
= pdoc
->SciLineFromPosition(currentPos_
.Position());
693 SelectionRange
rangeNew(ClampPositionIntoDocument(currentPos_
));
694 if (sel
.Count() > 1 || !(sel
.RangeMain() == rangeNew
)) {
695 InvalidateSelection(rangeNew
);
698 sel
.RangeMain() = rangeNew
;
699 SetRectangularRange();
701 SetHoverIndicatorPosition(sel
.MainCaret());
703 if (marginView
.highlightDelimiter
.NeedsDrawing(currentLine
)) {
706 QueueIdleWork(WorkItems::updateUI
);
709 void Editor::SetEmptySelection(Sci::Position currentPos_
) {
710 SetEmptySelection(SelectionPosition(currentPos_
));
713 void Editor::MultipleSelectAdd(AddNumber addNumber
) {
714 if (SelectionEmpty() || !multipleSelection
) {
715 // Select word at caret
716 const Sci::Position startWord
= pdoc
->ExtendWordSelect(sel
.MainCaret(), -1, true);
717 const Sci::Position endWord
= pdoc
->ExtendWordSelect(startWord
, 1, true);
718 TrimAndSetSelection(endWord
, startWord
);
722 if (!pdoc
->HasCaseFolder())
723 pdoc
->SetCaseFolder(CaseFolderForEncoding());
725 const Range
rangeMainSelection(sel
.RangeMain().Start().Position(), sel
.RangeMain().End().Position());
726 const std::string selectedText
= RangeText(rangeMainSelection
.start
, rangeMainSelection
.end
);
728 const Range
rangeTarget(targetRange
.start
.Position(), targetRange
.end
.Position());
729 std::vector
<Range
> searchRanges
;
730 // Search should be over the target range excluding the current selection so
731 // may need to search 2 ranges, after the selection then before the selection.
732 if (rangeTarget
.Overlaps(rangeMainSelection
)) {
733 // Common case is that the selection is completely within the target but
734 // may also have overlap at start or end.
735 if (rangeMainSelection
.end
< rangeTarget
.end
)
736 searchRanges
.push_back(Range(rangeMainSelection
.end
, rangeTarget
.end
));
737 if (rangeTarget
.start
< rangeMainSelection
.start
)
738 searchRanges
.push_back(Range(rangeTarget
.start
, rangeMainSelection
.start
));
741 searchRanges
.push_back(rangeTarget
);
744 for (const Range range
: searchRanges
) {
745 Sci::Position searchStart
= range
.start
;
746 const Sci::Position searchEnd
= range
.end
;
748 Sci::Position lengthFound
= selectedText
.length();
749 const Sci::Position pos
= pdoc
->FindText(searchStart
, searchEnd
,
750 selectedText
.c_str(), searchFlags
, &lengthFound
);
752 sel
.AddSelection(SelectionRange(pos
+ lengthFound
, pos
));
753 ContainerNeedsUpdate(Update::Selection
);
754 ScrollRange(sel
.RangeMain());
756 if (addNumber
== AddNumber::one
)
758 searchStart
= pos
+ lengthFound
;
767 bool Editor::RangeContainsProtected(Sci::Position start
, Sci::Position end
) const noexcept
{
768 if (vs
.ProtectionActive()) {
770 std::swap(start
, end
);
772 for (Sci::Position pos
= start
; pos
< end
; pos
++) {
773 if (vs
.styles
[pdoc
->StyleIndexAt(pos
)].IsProtected())
780 bool Editor::SelectionContainsProtected() const noexcept
{
781 for (size_t r
=0; r
<sel
.Count(); r
++) {
782 if (RangeContainsProtected(sel
.Range(r
).Start().Position(),
783 sel
.Range(r
).End().Position())) {
791 * Asks document to find a good position and then moves out of any invisible positions.
793 Sci::Position
Editor::MovePositionOutsideChar(Sci::Position pos
, Sci::Position moveDir
, bool checkLineEnd
) const {
794 return MovePositionOutsideChar(SelectionPosition(pos
), moveDir
, checkLineEnd
).Position();
797 SelectionPosition
Editor::MovePositionOutsideChar(SelectionPosition pos
, Sci::Position moveDir
, bool checkLineEnd
) const {
798 const Sci::Position posMoved
= pdoc
->MovePositionOutsideChar(pos
.Position(), moveDir
, checkLineEnd
);
799 if (posMoved
!= pos
.Position())
800 pos
.SetPosition(posMoved
);
801 if (vs
.ProtectionActive()) {
803 if ((pos
.Position() > 0) && vs
.styles
[pdoc
->StyleIndexAt(pos
.Position() - 1)].IsProtected()) {
804 while ((pos
.Position() < pdoc
->Length()) &&
805 (vs
.styles
[pdoc
->StyleIndexAt(pos
.Position())].IsProtected()))
808 } else if (moveDir
< 0) {
809 if (vs
.styles
[pdoc
->StyleIndexAt(pos
.Position())].IsProtected()) {
810 while ((pos
.Position() > 0) &&
811 (vs
.styles
[pdoc
->StyleIndexAt(pos
.Position() - 1)].IsProtected()))
819 void Editor::MovedCaret(SelectionPosition newPos
, SelectionPosition previousPos
,
820 bool ensureVisible
, CaretPolicies policies
) {
821 const Sci::Line currentLine
= pdoc
->SciLineFromPosition(newPos
.Position());
823 // In case in need of wrapping to ensure DisplayFromDoc works.
824 if (currentLine
>= wrapPending
.start
) {
825 if (WrapLines(WrapScope::wsAll
)) {
829 const XYScrollPosition newXY
= XYScrollToMakeVisible(
830 SelectionRange(posDrag
.IsValid() ? posDrag
: newPos
), XYScrollOptions::all
, policies
);
831 if (previousPos
.IsValid() && (newXY
.xOffset
== xOffset
)) {
832 // simple vertical scroll then invalidate
833 ScrollTo(newXY
.topLine
);
834 InvalidateSelection(SelectionRange(previousPos
), true);
840 ShowCaretAtCurrentPosition();
844 SetHoverIndicatorPosition(sel
.MainCaret());
845 QueueIdleWork(WorkItems::updateUI
);
847 if (marginView
.highlightDelimiter
.NeedsDrawing(currentLine
)) {
852 void Editor::MovePositionTo(SelectionPosition newPos
, Selection::SelTypes selt
, bool ensureVisible
) {
853 const SelectionPosition spCaret
= ((sel
.Count() == 1) && sel
.Empty()) ?
854 sel
.Last() : SelectionPosition(Sci::invalidPosition
);
856 const Sci::Position delta
= newPos
.Position() - sel
.MainCaret();
857 newPos
= ClampPositionIntoDocument(newPos
);
858 newPos
= MovePositionOutsideChar(newPos
, delta
);
859 if (!multipleSelection
&& sel
.IsRectangular() && (selt
== Selection::SelTypes::stream
)) {
860 // Can't turn into multiple selection so clear additional selections
861 InvalidateSelection(SelectionRange(newPos
), true);
862 sel
.DropAdditionalRanges();
864 if (!sel
.IsRectangular() && (selt
== Selection::SelTypes::rectangle
)) {
865 // Switching to rectangular
866 InvalidateSelection(sel
.RangeMain(), false);
867 SelectionRange rangeMain
= sel
.RangeMain();
869 sel
.Rectangular() = rangeMain
;
871 if (selt
!= Selection::SelTypes::none
) {
874 if (selt
!= Selection::SelTypes::none
|| sel
.MoveExtends()) {
875 SetSelection(newPos
);
877 SetEmptySelection(newPos
);
880 MovedCaret(newPos
, spCaret
, ensureVisible
, caretPolicies
);
883 void Editor::MovePositionTo(Sci::Position newPos
, Selection::SelTypes selt
, bool ensureVisible
) {
884 MovePositionTo(SelectionPosition(newPos
), selt
, ensureVisible
);
887 SelectionPosition
Editor::MovePositionSoVisible(SelectionPosition pos
, int moveDir
) {
888 pos
= ClampPositionIntoDocument(pos
);
889 pos
= MovePositionOutsideChar(pos
, moveDir
);
890 const Sci::Line lineDoc
= pdoc
->SciLineFromPosition(pos
.Position());
891 if (pcs
->GetVisible(lineDoc
)) {
894 Sci::Line lineDisplay
= pcs
->DisplayFromDoc(lineDoc
);
896 // lineDisplay is already line before fold as lines in fold use display line of line after fold
897 lineDisplay
= std::clamp
<Sci::Line
>(lineDisplay
, 0, pcs
->LinesDisplayed());
898 return SelectionPosition(
899 pdoc
->LineStart(pcs
->DocFromDisplay(lineDisplay
)));
901 lineDisplay
= std::clamp
<Sci::Line
>(lineDisplay
- 1, 0, pcs
->LinesDisplayed());
902 return SelectionPosition(
903 pdoc
->LineEnd(pcs
->DocFromDisplay(lineDisplay
)));
908 SelectionPosition
Editor::MovePositionSoVisible(Sci::Position pos
, int moveDir
) {
909 return MovePositionSoVisible(SelectionPosition(pos
), moveDir
);
912 Point
Editor::PointMainCaret() {
913 return LocationFromPosition(sel
.RangeMain().caret
);
917 * Choose the x position that the caret will try to stick to
918 * as it moves up and down.
920 void Editor::SetLastXChosen() {
921 const Point pt
= PointMainCaret();
922 lastXChosen
= static_cast<int>(pt
.x
) + xOffset
;
925 void Editor::ScrollTo(Sci::Line line
, bool moveThumb
) {
926 const Sci::Line topLineNew
= std::clamp
<Sci::Line
>(line
, 0, MaxScrollPos());
927 if (topLineNew
!= topLine
) {
928 // Try to optimise small scrolls
930 const Sci::Line linesToMove
= topLine
- topLineNew
;
931 const bool performBlit
= (std::abs(linesToMove
) <= 10) && (paintState
== PaintState::notPainting
);
932 willRedrawAll
= !performBlit
;
934 SetTopLine(topLineNew
);
935 // Optimize by styling the view as this will invalidate any needed area
936 // which could abort the initial paint if discovered later.
937 StyleAreaBounded(GetClientRectangle(), true);
939 // Perform redraw rather than scroll if many lines would be redrawn anyway.
941 ScrollText(linesToMove
);
945 willRedrawAll
= false;
950 SetVerticalScrollPos();
955 void Editor::ScrollText(Sci::Line
/* linesToMove */) {
956 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
960 void Editor::HorizontalScrollTo(int xPos
) {
961 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
964 if (!Wrapping() && (xOffset
!= xPos
)) {
966 ContainerNeedsUpdate(Update::HScroll
);
967 SetHorizontalScrollPos();
968 RedrawRect(GetClientRectangle());
972 void Editor::VerticalCentreCaret() {
973 const Sci::Line lineDoc
=
974 pdoc
->SciLineFromPosition(sel
.IsRectangular() ? sel
.Rectangular().caret
.Position() : sel
.MainCaret());
975 const Sci::Line lineDisplay
= pcs
->DisplayFromDoc(lineDoc
);
976 const Sci::Line newTop
= lineDisplay
- (LinesOnScreen() / 2);
977 if (topLine
!= newTop
) {
978 SetTopLine(newTop
> 0 ? newTop
: 0);
979 SetVerticalScrollPos();
980 RedrawRect(GetClientRectangle());
984 void Editor::MoveSelectedLines(int lineDelta
) {
986 if (sel
.IsRectangular()) {
990 // if selection doesn't start at the beginning of the line, set the new start
991 Sci::Position selectionStart
= SelectionStart().Position();
992 const Sci::Line startLine
= pdoc
->SciLineFromPosition(selectionStart
);
993 const Sci::Position beginningOfStartLine
= pdoc
->LineStart(startLine
);
994 selectionStart
= beginningOfStartLine
;
996 // if selection doesn't end at the beginning of a line greater than that of the start,
997 // then set it at the beginning of the next one
998 Sci::Position selectionEnd
= SelectionEnd().Position();
999 const Sci::Line endLine
= pdoc
->SciLineFromPosition(selectionEnd
);
1000 const Sci::Position beginningOfEndLine
= pdoc
->LineStart(endLine
);
1001 bool appendEol
= false;
1002 if (selectionEnd
> beginningOfEndLine
1003 || selectionStart
== selectionEnd
) {
1004 selectionEnd
= pdoc
->LineStart(endLine
+ 1);
1005 appendEol
= (selectionEnd
== pdoc
->Length() && pdoc
->SciLineFromPosition(selectionEnd
) == endLine
);
1008 // if there's nowhere for the selection to move
1009 // (i.e. at the beginning going up or at the end going down),
1010 // stop it right there!
1011 if ((selectionStart
== 0 && lineDelta
< 0)
1012 || (selectionEnd
== pdoc
->Length() && lineDelta
> 0)
1013 || selectionStart
== selectionEnd
) {
1019 if (lineDelta
> 0 && selectionEnd
== pdoc
->LineStart(pdoc
->LinesTotal() - 1)) {
1020 SetSelection(pdoc
->MovePositionOutsideChar(selectionEnd
- 1, -1), selectionEnd
);
1022 selectionEnd
= CurrentPosition();
1024 SetSelection(selectionStart
, selectionEnd
);
1026 const std::string selectedText
= RangeText(selectionStart
, selectionEnd
);
1028 const Point currentLocation
= LocationFromPosition(CurrentPosition());
1029 const Sci::Line currentLine
= LineFromLocation(currentLocation
);
1032 SetSelection(pdoc
->MovePositionOutsideChar(selectionStart
- 1, -1), selectionEnd
);
1035 const std::string_view eol
= pdoc
->EOLString();
1036 if (currentLine
+ lineDelta
>= pdoc
->LinesTotal())
1037 pdoc
->InsertString(pdoc
->Length(), eol
);
1038 GoToLine(currentLine
+ lineDelta
);
1040 Sci::Position selectionLength
= pdoc
->InsertString(CurrentPosition(), selectedText
);
1042 const Sci::Position lengthInserted
= pdoc
->InsertString(CurrentPosition() + selectionLength
, eol
);
1043 selectionLength
+= lengthInserted
;
1045 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength
);
1048 void Editor::MoveSelectedLinesUp() {
1049 MoveSelectedLines(-1);
1052 void Editor::MoveSelectedLinesDown() {
1053 MoveSelectedLines(1);
1056 void Editor::MoveCaretInsideView(bool ensureVisible
) {
1057 const PRectangle rcClient
= GetTextRectangle();
1058 const Point pt
= PointMainCaret();
1059 if (pt
.y
< rcClient
.top
) {
1060 MovePositionTo(SPositionFromLocation(
1061 Point::FromInts(lastXChosen
- xOffset
, static_cast<int>(rcClient
.top
)),
1062 false, false, UserVirtualSpace()),
1063 Selection::SelTypes::none
, ensureVisible
);
1064 } else if ((pt
.y
+ vs
.lineHeight
- 1) > rcClient
.bottom
) {
1065 const ptrdiff_t yOfLastLineFullyDisplayed
= static_cast<ptrdiff_t>(rcClient
.top
) + (LinesOnScreen() - 1) * vs
.lineHeight
;
1066 MovePositionTo(SPositionFromLocation(
1067 Point::FromInts(lastXChosen
- xOffset
, static_cast<int>(rcClient
.top
+ yOfLastLineFullyDisplayed
)),
1068 false, false, UserVirtualSpace()),
1069 Selection::SelTypes::none
, ensureVisible
);
1073 Sci::Line
Editor::DisplayFromPosition(Sci::Position pos
) {
1074 AutoSurface
surface(this);
1075 return view
.DisplayFromPosition(surface
, *this, pos
, vs
);
1079 * Ensure the caret is reasonably visible in context.
1081 Caret policy in Scintilla
1083 If slop is set, we can define a slop value.
1084 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1085 This zone is defined as a number of pixels near the vertical margins,
1086 and as a number of lines near the horizontal margins.
1087 By keeping the caret away from the edges, it is seen within its context,
1088 so it is likely that the identifier that the caret is on can be completely seen,
1089 and that the current line is seen with some of the lines following it which are
1090 often dependent on that line.
1092 If strict is set, the policy is enforced... strictly.
1093 The caret is centred on the display if slop is not set,
1094 and cannot go in the UZ if slop is set.
1096 If jumps is set, the display is moved more energetically
1097 so the caret can move in the same direction longer before the policy is applied again.
1098 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1100 If even is not set, instead of having symmetrical UZs,
1101 the left and bottom UZs are extended up to right and top UZs respectively.
1102 This way, we favour the displaying of useful information: the beginning of lines,
1103 where most code reside, and the lines after the caret, eg. the body of a function.
1106 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1107 | | | | | visibility or going into the UZ) display is...
1108 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1109 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1110 0 | 0 | 0 | 1 | Yes | moved by one position
1111 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1112 0 | 0 | 1 | 1 | Yes | centred on the caret
1113 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1114 0 | 1 | - | 1 | No, caret is always centred | -
1115 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1116 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1117 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1118 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1119 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1120 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1121 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1124 Editor::XYScrollPosition
Editor::XYScrollToMakeVisible(const SelectionRange
&range
,
1125 const XYScrollOptions options
, CaretPolicies policies
) {
1126 const PRectangle rcClient
= GetTextRectangle();
1127 const Point ptOrigin
= GetVisibleOriginInMain();
1128 const Point pt
= LocationFromPosition(range
.caret
) + ptOrigin
;
1129 const Point ptAnchor
= LocationFromPosition(range
.anchor
) + ptOrigin
;
1130 const Point
ptBottomCaret(pt
.x
, pt
.y
+ vs
.lineHeight
- 1);
1132 XYScrollPosition
newXY(xOffset
, topLine
);
1133 if (rcClient
.Empty()) {
1137 // Vertical positioning
1138 if (FlagSet(options
, XYScrollOptions::vertical
) &&
1139 (pt
.y
< rcClient
.top
|| ptBottomCaret
.y
>= rcClient
.bottom
|| FlagSet(policies
.y
.policy
, CaretPolicy::Strict
))) {
1140 const Sci::Line lineCaret
= DisplayFromPosition(range
.caret
.Position());
1141 const Sci::Line linesOnScreen
= LinesOnScreen();
1142 const Sci::Line halfScreen
= std::max(linesOnScreen
- 1, static_cast<Sci::Line
>(2)) / 2;
1143 const bool bSlop
= FlagSet(policies
.y
.policy
, CaretPolicy::Slop
);
1144 const bool bStrict
= FlagSet(policies
.y
.policy
, CaretPolicy::Strict
);
1145 const bool bJump
= FlagSet(policies
.y
.policy
, CaretPolicy::Jumps
);
1146 const bool bEven
= FlagSet(policies
.y
.policy
, CaretPolicy::Even
);
1148 // It should be possible to scroll the window to show the caret,
1149 // but this fails to remove the caret on GTK+
1150 if (bSlop
) { // A margin is defined
1151 Sci::Line yMoveT
= 0;
1152 Sci::Line yMoveB
= 0;
1154 Sci::Line yMarginT
= 0;
1155 Sci::Line yMarginB
= 0;
1156 if (!FlagSet(options
, XYScrollOptions::useMargin
)) {
1157 // In drag mode, avoid moves
1158 // otherwise, a double click will select several lines.
1159 yMarginT
= yMarginB
= 0;
1161 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1162 // a maximum of slightly less than half the height of the text area.
1163 yMarginT
= std::clamp
<Sci::Line
>(policies
.y
.slop
, 1, halfScreen
);
1165 yMarginB
= yMarginT
;
1167 yMarginB
= linesOnScreen
- yMarginT
- 1;
1173 yMoveT
= std::clamp
<Sci::Line
>(policies
.y
.slop
* 3, 1, halfScreen
);
1177 yMoveB
= linesOnScreen
- yMoveT
- 1;
1179 if (lineCaret
< topLine
+ yMarginT
) {
1180 // Caret goes too high
1181 newXY
.topLine
= lineCaret
- yMoveT
;
1182 } else if (lineCaret
> topLine
+ linesOnScreen
- 1 - yMarginB
) {
1183 // Caret goes too low
1184 newXY
.topLine
= lineCaret
- linesOnScreen
+ 1 + yMoveB
;
1186 } else { // Not strict
1187 yMoveT
= bJump
? policies
.y
.slop
* 3 : policies
.y
.slop
;
1188 yMoveT
= std::clamp
<Sci::Line
>(yMoveT
, 1, halfScreen
);
1192 yMoveB
= linesOnScreen
- yMoveT
- 1;
1194 if (lineCaret
< topLine
) {
1195 // Caret goes too high
1196 newXY
.topLine
= lineCaret
- yMoveT
;
1197 } else if (lineCaret
> topLine
+ linesOnScreen
- 1) {
1198 // Caret goes too low
1199 newXY
.topLine
= lineCaret
- linesOnScreen
+ 1 + yMoveB
;
1203 if (!bStrict
&& !bJump
) {
1205 if (lineCaret
< topLine
) {
1206 // Caret goes too high
1207 newXY
.topLine
= lineCaret
;
1208 } else if (lineCaret
> topLine
+ linesOnScreen
- 1) {
1209 // Caret goes too low
1211 newXY
.topLine
= lineCaret
- linesOnScreen
+ 1;
1213 newXY
.topLine
= lineCaret
;
1216 } else { // Strict or going out of display
1218 // Always centre caret
1219 newXY
.topLine
= lineCaret
- halfScreen
;
1221 // Always put caret on top of display
1222 newXY
.topLine
= lineCaret
;
1226 if (!(range
.caret
== range
.anchor
)) {
1227 const Sci::Line lineAnchor
= DisplayFromPosition(range
.anchor
.Position());
1228 if (lineAnchor
< lineCaret
) {
1229 // Shift up to show anchor or as much of range as possible
1230 newXY
.topLine
= std::min(newXY
.topLine
, lineAnchor
);
1231 newXY
.topLine
= std::max(newXY
.topLine
, lineCaret
- LinesOnScreen());
1233 // Shift down to show anchor or as much of range as possible
1234 newXY
.topLine
= std::max(newXY
.topLine
, lineAnchor
- LinesOnScreen());
1235 newXY
.topLine
= std::min(newXY
.topLine
, lineCaret
);
1238 newXY
.topLine
= std::clamp
<Sci::Line
>(newXY
.topLine
, 0, MaxScrollPos());
1241 // Horizontal positioning
1242 if (FlagSet(options
, XYScrollOptions::horizontal
) && !Wrapping()) {
1243 const int halfScreen
= std::max(static_cast<int>(rcClient
.Width()) - 4, 4) / 2;
1244 const bool bSlop
= FlagSet(policies
.x
.policy
, CaretPolicy::Slop
);
1245 const bool bStrict
= FlagSet(policies
.x
.policy
, CaretPolicy::Strict
);
1246 const bool bJump
= FlagSet(policies
.x
.policy
, CaretPolicy::Jumps
);
1247 const bool bEven
= FlagSet(policies
.x
.policy
, CaretPolicy::Even
);
1249 if (bSlop
) { // A margin is defined
1255 if (!FlagSet(options
, XYScrollOptions::useMargin
)) {
1256 // In drag mode, avoid moves unless very near of the margin
1257 // otherwise, a simple click will select text.
1258 xMarginL
= xMarginR
= 2;
1260 // xMargin must equal to caretXSlop, with a minimum of 2 and
1261 // a maximum of slightly less than half the width of the text area.
1262 xMarginR
= std::clamp(policies
.x
.slop
, 2, halfScreen
);
1264 xMarginL
= xMarginR
;
1266 xMarginL
= static_cast<int>(rcClient
.Width()) - xMarginR
- 4;
1269 if (bJump
&& bEven
) {
1270 // Jump is used only in even mode
1271 xMoveL
= xMoveR
= std::clamp(policies
.x
.slop
* 3, 1, halfScreen
);
1273 xMoveL
= xMoveR
= 0; // Not used, avoid a warning
1275 if (pt
.x
< rcClient
.left
+ xMarginL
) {
1276 // Caret is on the left of the display
1277 if (bJump
&& bEven
) {
1278 newXY
.xOffset
-= xMoveL
;
1280 // Move just enough to allow to display the caret
1281 newXY
.xOffset
-= static_cast<int>((rcClient
.left
+ xMarginL
) - pt
.x
);
1283 } else if (pt
.x
>= rcClient
.right
- xMarginR
) {
1284 // Caret is on the right of the display
1285 if (bJump
&& bEven
) {
1286 newXY
.xOffset
+= xMoveR
;
1288 // Move just enough to allow to display the caret
1289 newXY
.xOffset
+= static_cast<int>(pt
.x
- (rcClient
.right
- xMarginR
) + 1);
1292 } else { // Not strict
1293 xMoveR
= bJump
? policies
.x
.slop
* 3 : policies
.x
.slop
;
1294 xMoveR
= std::clamp(xMoveR
, 1, halfScreen
);
1298 xMoveL
= static_cast<int>(rcClient
.Width()) - xMoveR
- 4;
1300 if (pt
.x
< rcClient
.left
) {
1301 // Caret is on the left of the display
1302 newXY
.xOffset
-= xMoveL
;
1303 } else if (pt
.x
>= rcClient
.right
) {
1304 // Caret is on the right of the display
1305 newXY
.xOffset
+= xMoveR
;
1310 (bJump
&& (pt
.x
< rcClient
.left
|| pt
.x
>= rcClient
.right
))) {
1311 // Strict or going out of display
1314 newXY
.xOffset
+= static_cast<int>(pt
.x
- rcClient
.left
- halfScreen
);
1316 // Put caret on right
1317 newXY
.xOffset
+= static_cast<int>(pt
.x
- rcClient
.right
+ 1);
1320 // Move just enough to allow to display the caret
1321 if (pt
.x
< rcClient
.left
) {
1322 // Caret is on the left of the display
1324 newXY
.xOffset
-= static_cast<int>(rcClient
.left
- pt
.x
);
1326 newXY
.xOffset
+= static_cast<int>(pt
.x
- rcClient
.right
) + 1;
1328 } else if (pt
.x
>= rcClient
.right
) {
1329 // Caret is on the right of the display
1330 newXY
.xOffset
+= static_cast<int>(pt
.x
- rcClient
.right
) + 1;
1334 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1335 if (pt
.x
+ xOffset
< rcClient
.left
+ newXY
.xOffset
) {
1336 newXY
.xOffset
= static_cast<int>(pt
.x
+ xOffset
- rcClient
.left
) - 2;
1337 } else if (pt
.x
+ xOffset
>= rcClient
.right
+ newXY
.xOffset
) {
1338 newXY
.xOffset
= static_cast<int>(pt
.x
+ xOffset
- rcClient
.right
) + 2;
1339 if (vs
.IsBlockCaretStyle() || view
.imeCaretBlockOverride
) {
1340 // Ensure we can see a good portion of the block caret
1341 newXY
.xOffset
+= static_cast<int>(vs
.aveCharWidth
);
1344 if (!(range
.caret
== range
.anchor
)) {
1345 if (ptAnchor
.x
< pt
.x
) {
1346 // Shift to left to show anchor or as much of range as possible
1347 const int maxOffset
= static_cast<int>(ptAnchor
.x
+ xOffset
- rcClient
.left
) - 1;
1348 const int minOffset
= static_cast<int>(pt
.x
+ xOffset
- rcClient
.right
) + 1;
1349 newXY
.xOffset
= std::min(newXY
.xOffset
, maxOffset
);
1350 newXY
.xOffset
= std::max(newXY
.xOffset
, minOffset
);
1352 // Shift to right to show anchor or as much of range as possible
1353 const int minOffset
= static_cast<int>(ptAnchor
.x
+ xOffset
- rcClient
.right
) + 1;
1354 const int maxOffset
= static_cast<int>(pt
.x
+ xOffset
- rcClient
.left
) - 1;
1355 newXY
.xOffset
= std::max(newXY
.xOffset
, minOffset
);
1356 newXY
.xOffset
= std::min(newXY
.xOffset
, maxOffset
);
1359 if (newXY
.xOffset
< 0) {
1367 void Editor::SetXYScroll(XYScrollPosition newXY
) {
1368 if ((newXY
.topLine
!= topLine
) || (newXY
.xOffset
!= xOffset
)) {
1369 if (newXY
.topLine
!= topLine
) {
1370 SetTopLine(newXY
.topLine
);
1371 SetVerticalScrollPos();
1373 if (newXY
.xOffset
!= xOffset
) {
1374 xOffset
= newXY
.xOffset
;
1375 ContainerNeedsUpdate(Update::HScroll
);
1376 if (newXY
.xOffset
> 0) {
1377 const PRectangle rcText
= GetTextRectangle();
1378 if (horizontalScrollBarVisible
&&
1379 rcText
.Width() + xOffset
> scrollWidth
) {
1380 scrollWidth
= xOffset
+ static_cast<int>(rcText
.Width());
1384 SetHorizontalScrollPos();
1387 UpdateSystemCaret();
1391 void Editor::ScrollRange(SelectionRange range
) {
1392 SetXYScroll(XYScrollToMakeVisible(range
, XYScrollOptions::all
, caretPolicies
));
1395 void Editor::EnsureCaretVisible(bool useMargin
, bool vert
, bool horiz
) {
1396 SetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag
.IsValid() ? posDrag
: sel
.RangeMain().caret
),
1397 (useMargin
?XYScrollOptions::useMargin
:XYScrollOptions::none
)|
1398 (vert
?XYScrollOptions::vertical
:XYScrollOptions::none
)|
1399 (horiz
?XYScrollOptions::horizontal
:XYScrollOptions::none
),
1403 void Editor::ShowCaretAtCurrentPosition() {
1405 caret
.active
= true;
1407 FineTickerCancel(TickReason::caret
);
1408 if (caret
.period
> 0)
1409 FineTickerStart(TickReason::caret
, caret
.period
, caret
.period
/10);
1411 caret
.active
= false;
1413 FineTickerCancel(TickReason::caret
);
1418 void Editor::DropCaret() {
1419 caret
.active
= false;
1420 FineTickerCancel(TickReason::caret
);
1424 void Editor::CaretSetPeriod(int period
) {
1425 if (caret
.period
!= period
) {
1426 caret
.period
= period
;
1428 FineTickerCancel(TickReason::caret
);
1429 if ((caret
.active
) && (caret
.period
> 0))
1430 FineTickerStart(TickReason::caret
, caret
.period
, caret
.period
/10);
1435 void Editor::InvalidateCaret() {
1436 if (posDrag
.IsValid()) {
1437 InvalidateRange(posDrag
.Position(), posDrag
.Position() + 1);
1439 for (size_t r
=0; r
<sel
.Count(); r
++) {
1440 InvalidateRange(sel
.Range(r
).caret
.Position(), sel
.Range(r
).caret
.Position() + 1);
1443 UpdateSystemCaret();
1446 void Editor::NotifyCaretMove() {
1449 void Editor::UpdateSystemCaret() {
1452 bool Editor::Wrapping() const noexcept
{
1453 return vs
.wrap
.state
!= Wrap::None
;
1456 void Editor::NeedWrapping(Sci::Line docLineStart
, Sci::Line docLineEnd
) {
1457 //Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd);
1458 if (wrapPending
.AddRange(docLineStart
, docLineEnd
)) {
1459 view
.llc
.Invalidate(LineLayout::ValidLevel::positions
);
1461 // Wrap lines during idle.
1462 if (Wrapping() && wrapPending
.NeedsWrap()) {
1467 bool Editor::WrapOneLine(Surface
*surface
, Sci::Line lineToWrap
) {
1468 std::shared_ptr
<LineLayout
> ll
= view
.RetrieveLineLayout(lineToWrap
, *this);
1469 int linesWrapped
= 1;
1471 view
.LayoutLine(*this, surface
, vs
, ll
.get(), wrapWidth
);
1472 linesWrapped
= ll
->lines
;
1474 if (vs
.annotationVisible
!= AnnotationVisible::Hidden
) {
1475 linesWrapped
+= pdoc
->AnnotationLines(lineToWrap
);
1477 return pcs
->SetHeight(lineToWrap
, linesWrapped
);
1482 // Lines less than lengthToMultiThread are laid out in blocks in parallel.
1483 // Longer lines are multi-threaded inside LayoutLine.
1484 // This allows faster processing when lines differ greatly in length and thus time to lay out.
1485 constexpr Sci::Position lengthToMultiThread
= 4000;
1489 bool Editor::WrapBlock(Surface
*surface
, Sci::Line lineToWrap
, Sci::Line lineToWrapEnd
) {
1491 const size_t linesBeingWrapped
= static_cast<size_t>(lineToWrapEnd
- lineToWrap
);
1493 std::vector
<int> linesAfterWrap(linesBeingWrapped
);
1495 size_t threads
= std::min
<size_t>({ linesBeingWrapped
, view
.maxLayoutThreads
});
1496 if (!surface
->SupportsFeature(Supports::ThreadSafeMeasureWidths
)) {
1500 const bool multiThreaded
= threads
> 1;
1502 ElapsedPeriod epWrapping
;
1504 // Wrap all the short lines in multiple threads
1506 // If only 1 thread needed then use the main thread, else spin up multiple
1507 const std::launch policy
= multiThreaded
? std::launch::async
: std::launch::deferred
;
1509 std::atomic
<size_t> nextIndex
= 0;
1511 // Lines that are less likely to be re-examined should not be read from or written to the cache.
1512 const SignificantLines significantLines
{
1513 pdoc
->SciLineFromPosition(sel
.MainCaret()),
1514 pcs
->DocFromDisplay(topLine
),
1515 LinesOnScreen() + 1,
1516 view
.llc
.GetLevel(),
1519 // Protect the line layout cache from being accessed from multiple threads simultaneously
1520 std::mutex mutexRetrieve
;
1522 std::vector
<std::future
<void>> futures
;
1523 for (size_t th
= 0; th
< threads
; th
++) {
1524 std::future
<void> fut
= std::async(policy
,
1525 [=, &surface
, &nextIndex
, &linesAfterWrap
, &mutexRetrieve
]() {
1526 // llTemporary is reused for non-significant lines, avoiding allocation costs.
1527 std::shared_ptr
<LineLayout
> llTemporary
= std::make_shared
<LineLayout
>(-1, 200);
1529 const size_t i
= nextIndex
.fetch_add(1, std::memory_order_acq_rel
);
1530 if (i
>= linesBeingWrapped
) {
1533 const Sci::Line lineNumber
= lineToWrap
+ i
;
1534 const Range rangeLine
= pdoc
->LineRange(lineNumber
);
1535 const Sci::Position lengthLine
= rangeLine
.Length();
1536 if (lengthLine
< lengthToMultiThread
) {
1537 std::shared_ptr
<LineLayout
> ll
;
1538 if (significantLines
.LineMayCache(lineNumber
)) {
1539 std::lock_guard
<std::mutex
> guard(mutexRetrieve
);
1540 ll
= view
.RetrieveLineLayout(lineNumber
, *this);
1543 ll
->ReSet(lineNumber
, lengthLine
);
1545 view
.LayoutLine(*this, surface
, vs
, ll
.get(), wrapWidth
, multiThreaded
);
1546 linesAfterWrap
[i
] = ll
->lines
;
1550 futures
.push_back(std::move(fut
));
1552 for (const std::future
<void> &f
: futures
) {
1555 // End of multiple threads
1557 // Multiply duration by number of threads to produce (near) equivalence to duration if single threaded
1558 const double durationShortLines
= epWrapping
.Duration(true);
1559 const double durationShortLinesThreads
= durationShortLines
* threads
;
1561 // Wrap all the long lines in the main thread.
1562 // LayoutLine may then multi-thread over segments in each line.
1564 std::shared_ptr
<LineLayout
> llLarge
= std::make_shared
<LineLayout
>(-1, 200);
1565 for (size_t indexLarge
= 0; indexLarge
< linesBeingWrapped
; indexLarge
++) {
1566 const Sci::Line lineNumber
= lineToWrap
+ indexLarge
;
1567 const Range rangeLine
= pdoc
->LineRange(lineNumber
);
1568 const Sci::Position lengthLine
= rangeLine
.Length();
1569 if (lengthLine
>= lengthToMultiThread
) {
1570 std::shared_ptr
<LineLayout
> ll
;
1571 if (significantLines
.LineMayCache(lineNumber
)) {
1572 ll
= view
.RetrieveLineLayout(lineNumber
, *this);
1575 ll
->ReSet(lineNumber
, lengthLine
);
1577 view
.LayoutLine(*this, surface
, vs
, ll
.get(), wrapWidth
);
1578 linesAfterWrap
[indexLarge
] = ll
->lines
;
1582 const double durationLongLines
= epWrapping
.Duration();
1583 const size_t bytesBeingWrapped
= pdoc
->LineStart(lineToWrap
+ linesBeingWrapped
) - pdoc
->LineStart(lineToWrap
);
1585 size_t wrapsDone
= 0;
1587 for (size_t i
= 0; i
< linesBeingWrapped
; i
++) {
1588 const Sci::Line lineNumber
= lineToWrap
+ i
;
1589 int linesWrapped
= linesAfterWrap
[i
];
1590 if (vs
.annotationVisible
!= AnnotationVisible::Hidden
) {
1591 linesWrapped
+= pdoc
->AnnotationLines(lineNumber
);
1593 if (pcs
->SetHeight(lineNumber
, linesWrapped
)) {
1596 wrapPending
.Wrapped(lineNumber
);
1599 durationWrapOneByte
.AddSample(bytesBeingWrapped
, durationShortLinesThreads
+ durationLongLines
);
1601 return wrapsDone
> 0;
1604 // Perform wrapping for a subset of the lines needing wrapping.
1605 // wsAll: wrap all lines which need wrapping in this single call
1606 // wsVisible: wrap currently visible lines
1607 // wsIdle: wrap one page + 100 lines
1608 // Return true if wrapping occurred.
1609 bool Editor::WrapLines(WrapScope ws
) {
1610 Sci::Line goodTopLine
= topLine
;
1611 bool wrapOccurred
= false;
1613 if (wrapWidth
!= LineLayout::wrapWidthInfinite
) {
1614 wrapWidth
= LineLayout::wrapWidthInfinite
;
1615 for (Sci::Line lineDoc
= 0; lineDoc
< pdoc
->LinesTotal(); lineDoc
++) {
1616 int linesWrapped
= 1;
1617 if (vs
.annotationVisible
!= AnnotationVisible::Hidden
) {
1618 linesWrapped
+= pdoc
->AnnotationLines(lineDoc
);
1620 pcs
->SetHeight(lineDoc
, linesWrapped
);
1622 wrapOccurred
= true;
1624 wrapPending
.Reset();
1626 } else if (wrapPending
.NeedsWrap()) {
1627 wrapPending
.start
= std::min(wrapPending
.start
, pdoc
->LinesTotal());
1628 if (!SetIdle(true)) {
1629 // Idle processing not supported so full wrap required.
1630 ws
= WrapScope::wsAll
;
1632 // Decide where to start wrapping
1633 Sci::Line lineToWrap
= wrapPending
.start
;
1634 Sci::Line lineToWrapEnd
= std::min(wrapPending
.end
, pdoc
->LinesTotal());
1635 const Sci::Line lineDocTop
= pcs
->DocFromDisplay(topLine
);
1636 const Sci::Line subLineTop
= topLine
- pcs
->DisplayFromDoc(lineDocTop
);
1637 if (ws
== WrapScope::wsVisible
) {
1638 lineToWrap
= std::clamp(lineDocTop
-5, wrapPending
.start
, pdoc
->LinesTotal());
1639 // Priority wrap to just after visible area.
1640 // Since wrapping could reduce display lines, treat each
1641 // as taking only one display line.
1642 lineToWrapEnd
= lineDocTop
;
1643 Sci::Line lines
= LinesOnScreen() + 1;
1644 constexpr double secondsAllowed
= 0.1;
1645 const size_t actionsInAllowedTime
= std::clamp
<Sci::Line
>(
1646 durationWrapOneByte
.ActionsInAllowedTime(secondsAllowed
),
1648 const Sci::Line lineLast
= pdoc
->LineFromPositionAfter(lineToWrap
, actionsInAllowedTime
);
1649 const Sci::Line maxLine
= std::min(lineLast
, pcs
->LinesInDoc());
1650 while ((lineToWrapEnd
< maxLine
) && (lines
>0)) {
1651 if (pcs
->GetVisible(lineToWrapEnd
))
1655 // .. and if the paint window is outside pending wraps
1656 if ((lineToWrap
> wrapPending
.end
) || (lineToWrapEnd
< wrapPending
.start
)) {
1657 // Currently visible text does not need wrapping
1660 } else if (ws
== WrapScope::wsIdle
) {
1661 // Try to keep time taken by wrapping reasonable so interaction remains smooth.
1662 constexpr double secondsAllowed
= 0.01;
1663 const size_t actionsInAllowedTime
= std::clamp
<Sci::Line
>(
1664 durationWrapOneByte
.ActionsInAllowedTime(secondsAllowed
),
1666 lineToWrapEnd
= pdoc
->LineFromPositionAfter(lineToWrap
, actionsInAllowedTime
);
1668 const Sci::Line lineEndNeedWrap
= std::min(wrapPending
.end
, pdoc
->LinesTotal());
1669 lineToWrapEnd
= std::min(lineToWrapEnd
, lineEndNeedWrap
);
1671 // Ensure all lines being wrapped are styled.
1672 pdoc
->EnsureStyledTo(pdoc
->LineStart(lineToWrapEnd
));
1674 if (lineToWrap
< lineToWrapEnd
) {
1676 PRectangle rcTextArea
= GetClientRectangle();
1677 rcTextArea
.left
= static_cast<XYPOSITION
>(vs
.textStart
);
1678 rcTextArea
.right
-= vs
.rightMarginWidth
;
1679 wrapWidth
= static_cast<int>(rcTextArea
.Width());
1681 AutoSurface
surface(this);
1683 //Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
1685 wrapOccurred
= WrapBlock(surface
, lineToWrap
, lineToWrapEnd
);
1687 goodTopLine
= pcs
->DisplayFromDoc(lineDocTop
) + std::min(
1688 subLineTop
, static_cast<Sci::Line
>(pcs
->GetHeight(lineDocTop
)-1));
1692 // If wrapping is done, bring it to resting position
1693 if (wrapPending
.start
>= lineEndNeedWrap
) {
1694 wrapPending
.Reset();
1700 SetTopLine(std::clamp
<Sci::Line
>(goodTopLine
, 0, MaxScrollPos()));
1701 SetVerticalScrollPos();
1704 return wrapOccurred
;
1707 void Editor::LinesJoin() {
1708 if (!RangeContainsProtected(targetRange
.start
.Position(), targetRange
.end
.Position())) {
1710 const Sci::Line line
= pdoc
->SciLineFromPosition(targetRange
.start
.Position());
1711 for (Sci::Position pos
= pdoc
->LineEnd(line
); pos
< targetRange
.end
.Position(); pos
= pdoc
->LineEnd(line
)) {
1712 const char chPrev
= pdoc
->CharAt(pos
- 1);
1713 const Sci::Position widthChar
= pdoc
->LenChar(pos
);
1714 targetRange
.end
.Add(-widthChar
);
1715 pdoc
->DeleteChars(pos
, widthChar
);
1716 if (chPrev
!= ' ') {
1717 // Ensure at least one space separating previous lines
1718 const Sci::Position lengthInserted
= pdoc
->InsertString(pos
, " ", 1);
1719 targetRange
.end
.Add(lengthInserted
);
1725 void Editor::LinesSplit(int pixelWidth
) {
1726 if (!RangeContainsProtected(targetRange
.start
.Position(), targetRange
.end
.Position())) {
1727 if (pixelWidth
== 0) {
1728 const PRectangle rcText
= GetTextRectangle();
1729 pixelWidth
= static_cast<int>(rcText
.Width());
1731 const Sci::Line lineStart
= pdoc
->SciLineFromPosition(targetRange
.start
.Position());
1732 Sci::Line lineEnd
= pdoc
->SciLineFromPosition(targetRange
.end
.Position());
1733 const std::string_view eol
= pdoc
->EOLString();
1735 for (Sci::Line line
= lineStart
; line
<= lineEnd
; line
++) {
1736 AutoSurface
surface(this);
1737 std::shared_ptr
<LineLayout
> ll
= view
.RetrieveLineLayout(line
, *this);
1738 if (surface
&& ll
) {
1739 const Sci::Position posLineStart
= pdoc
->LineStart(line
);
1740 view
.LayoutLine(*this, surface
, vs
, ll
.get(), pixelWidth
);
1741 Sci::Position lengthInsertedTotal
= 0;
1742 for (int subLine
= 1; subLine
< ll
->lines
; subLine
++) {
1743 const Sci::Position lengthInserted
= pdoc
->InsertString(
1744 posLineStart
+ lengthInsertedTotal
+ ll
->LineStart(subLine
), eol
);
1745 targetRange
.end
.Add(lengthInserted
);
1746 lengthInsertedTotal
+= lengthInserted
;
1749 lineEnd
= pdoc
->SciLineFromPosition(targetRange
.end
.Position());
1754 void Editor::PaintSelMargin(Surface
*surfaceWindow
, const PRectangle
&rc
) {
1755 if (vs
.fixedColumnWidth
== 0)
1759 RefreshPixMaps(surfaceWindow
);
1761 // On GTK+ with Ubuntu overlay scroll bars, the surface may have been finished
1762 // at this point. The Initialised call checks for this case and sets the status
1763 // to be bad which avoids crashes in following calls.
1764 if (!surfaceWindow
->Initialised()) {
1768 PRectangle rcMargin
= GetClientRectangle();
1769 const Point ptOrigin
= GetVisibleOriginInMain();
1770 rcMargin
.Move(0, -ptOrigin
.y
);
1772 rcMargin
.right
= static_cast<XYPOSITION
>(vs
.fixedColumnWidth
);
1774 if (!rc
.Intersects(rcMargin
))
1778 if (view
.bufferedDraw
) {
1779 surface
= marginView
.pixmapSelMargin
.get();
1781 surface
= surfaceWindow
;
1783 surface
->SetMode(CurrentSurfaceMode());
1785 // Clip vertically to paint area to avoid drawing line numbers
1786 if (rcMargin
.bottom
> rc
.bottom
)
1787 rcMargin
.bottom
= rc
.bottom
;
1788 if (rcMargin
.top
< rc
.top
)
1789 rcMargin
.top
= rc
.top
;
1791 marginView
.PaintMargin(surface
, topLine
, rc
, rcMargin
, *this, vs
);
1793 if (view
.bufferedDraw
) {
1794 marginView
.pixmapSelMargin
->FlushDrawing();
1795 surfaceWindow
->Copy(rcMargin
, Point(rcMargin
.left
, rcMargin
.top
), *marginView
.pixmapSelMargin
);
1799 void Editor::RefreshPixMaps(Surface
*surfaceWindow
) {
1800 view
.RefreshPixMaps(surfaceWindow
, vs
);
1801 marginView
.RefreshPixMaps(surfaceWindow
, vs
);
1802 if (view
.bufferedDraw
) {
1803 const PRectangle rcClient
= GetClientRectangle();
1804 if (!view
.pixmapLine
) {
1805 view
.pixmapLine
= surfaceWindow
->AllocatePixMap(static_cast<int>(rcClient
.Width()), vs
.lineHeight
);
1807 if (!marginView
.pixmapSelMargin
) {
1808 marginView
.pixmapSelMargin
= surfaceWindow
->AllocatePixMap(vs
.fixedColumnWidth
,
1809 static_cast<int>(rcClient
.Height()));
1814 void Editor::Paint(Surface
*surfaceWindow
, PRectangle rcArea
) {
1815 redrawPendingText
= false;
1816 redrawPendingMargin
= false;
1818 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
1819 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
1822 if (paintState
== PaintState::abandoned
)
1823 return; // Scroll bars may have changed so need redraw
1824 RefreshPixMaps(surfaceWindow
);
1826 paintAbandonedByStyling
= false;
1828 StyleAreaBounded(rcArea
, false);
1830 const PRectangle rcClient
= GetClientRectangle();
1831 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
1832 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1834 if (NotifyUpdateUI()) {
1836 RefreshPixMaps(surfaceWindow
);
1839 // Wrap the visible lines if needed.
1840 if (WrapLines(WrapScope::wsVisible
)) {
1841 // The wrapping process has changed the height of some lines so
1842 // abandon this paint for a complete repaint.
1843 if (AbandonPaint()) {
1846 RefreshPixMaps(surfaceWindow
); // In case pixmaps invalidated by scrollbar change
1849 if (!marginView
.pixmapSelPattern
->Initialised()) {
1850 // When Direct2D is used, pixmap creation may fail with D2DERR_RECREATE_TARGET so
1851 // abandon this paint to avoid further failures.
1852 // Main drawing surface and pixmaps should be recreated by next paint.
1856 if (!view
.bufferedDraw
)
1857 surfaceWindow
->SetClip(rcArea
);
1859 if (paintState
!= PaintState::abandoned
) {
1860 if (vs
.marginInside
) {
1861 PaintSelMargin(surfaceWindow
, rcArea
);
1862 PRectangle rcRightMargin
= rcClient
;
1863 rcRightMargin
.left
= rcRightMargin
.right
- vs
.rightMarginWidth
;
1864 if (rcArea
.Intersects(rcRightMargin
)) {
1865 surfaceWindow
->FillRectangle(rcRightMargin
, vs
.styles
[StyleDefault
].back
);
1867 } else { // Else separate view so separate paint event but leftMargin included to allow overlap
1868 PRectangle rcLeftMargin
= rcArea
;
1869 rcLeftMargin
.left
= 0;
1870 rcLeftMargin
.right
= rcLeftMargin
.left
+ vs
.leftMarginWidth
;
1871 if (rcArea
.Intersects(rcLeftMargin
)) {
1872 surfaceWindow
->FillRectangle(rcLeftMargin
, vs
.styles
[StyleDefault
].back
);
1877 if (paintState
== PaintState::abandoned
) {
1878 // Either styling or NotifyUpdateUI noticed that painting is needed
1879 // outside the current painting rectangle
1880 //Platform::DebugPrintf("Abandoning paint\n");
1882 if (paintAbandonedByStyling
) {
1883 // Styling has spilled over a line end, such as occurs by starting a multiline
1884 // comment. The width of subsequent text may have changed, so rewrap.
1885 NeedWrapping(pcs
->DocFromDisplay(topLine
));
1888 if (!view
.bufferedDraw
)
1889 surfaceWindow
->PopClip();
1893 view
.PaintText(surfaceWindow
, *this, vs
, rcArea
, rcClient
);
1895 if (horizontalScrollBarVisible
&& trackLineWidth
&& (view
.lineWidthMaxSeen
> scrollWidth
)) {
1896 scrollWidth
= view
.lineWidthMaxSeen
;
1897 if (!FineTickerRunning(TickReason::widen
)) {
1898 FineTickerStart(TickReason::widen
, 50, 5);
1902 if (!view
.bufferedDraw
)
1903 surfaceWindow
->PopClip();
1908 // This is mostly copied from the Paint method but with some things omitted
1909 // such as the margin markers, line numbers, selection and caret
1910 // Should be merged back into a combined Draw method.
1911 Sci::Position
Editor::FormatRange(Scintilla::Message iMessage
, Scintilla::uptr_t wParam
, Scintilla::sptr_t lParam
) {
1914 const bool draw
= wParam
!= 0;
1915 void *ptr
= PtrFromSPtr(lParam
);
1916 if (iMessage
== Message::FormatRange
) {
1917 RangeToFormat
*pfr
= static_cast<RangeToFormat
*>(ptr
);
1918 const CharacterRangeFull chrg
{ pfr
->chrg
.cpMin
,pfr
->chrg
.cpMax
};
1919 AutoSurface
surface(pfr
->hdc
, this, Technology::Default
);
1920 AutoSurface
surfaceMeasure(pfr
->hdcTarget
, this, Technology::Default
);
1921 if (!surface
|| !surfaceMeasure
) {
1924 return view
.FormatRange(draw
, chrg
, pfr
->rc
, surface
, surfaceMeasure
, *this, vs
);
1927 RangeToFormatFull
*pfr
= static_cast<RangeToFormatFull
*>(ptr
);
1928 AutoSurface
surface(pfr
->hdc
, this, Technology::Default
);
1929 AutoSurface
surfaceMeasure(pfr
->hdcTarget
, this, Technology::Default
);
1930 if (!surface
|| !surfaceMeasure
) {
1933 return view
.FormatRange(draw
, pfr
->chrg
, pfr
->rc
, surface
, surfaceMeasure
, *this, vs
);
1937 long Editor::TextWidth(uptr_t style
, const char *text
) {
1939 AutoSurface
surface(this);
1941 return std::lround(surface
->WidthText(vs
.styles
[style
].font
.get(), text
));
1947 // Empty method is overridden on GTK+ to show / hide scrollbars
1948 void Editor::ReconfigureScrollBars() {}
1950 void Editor::ChangeScrollBars() {
1953 const Sci::Line nMax
= MaxScrollPos();
1954 const Sci::Line nPage
= LinesOnScreen();
1955 const bool modified
= ModifyScrollBars(nMax
+ nPage
- 1, nPage
);
1960 // TODO: ensure always showing as many lines as possible
1961 // May not be, if, for example, window made larger
1962 if (topLine
> MaxScrollPos()) {
1963 SetTopLine(std::clamp
<Sci::Line
>(topLine
, 0, MaxScrollPos()));
1964 SetVerticalScrollPos();
1968 if (!AbandonPaint())
1971 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
1974 void Editor::SetScrollBars() {
1975 // Overridden on GTK to defer to idle
1979 void Editor::ChangeSize() {
1983 PRectangle rcTextArea
= GetClientRectangle();
1984 rcTextArea
.left
= static_cast<XYPOSITION
>(vs
.textStart
);
1985 rcTextArea
.right
-= vs
.rightMarginWidth
;
1986 if (wrapWidth
!= rcTextArea
.Width()) {
1993 Sci::Position
Editor::RealizeVirtualSpace(Sci::Position position
, Sci::Position virtualSpace
) {
1994 if (virtualSpace
> 0) {
1995 const Sci::Line line
= pdoc
->SciLineFromPosition(position
);
1996 const Sci::Position indent
= pdoc
->GetLineIndentPosition(line
);
1997 if (indent
== position
) {
1998 return pdoc
->SetLineIndentation(line
, pdoc
->GetLineIndentation(line
) + virtualSpace
);
2000 const std::string
spaceText(virtualSpace
, ' ');
2001 const Sci::Position lengthInserted
= pdoc
->InsertString(position
, spaceText
);
2002 position
+= lengthInserted
;
2008 SelectionPosition
Editor::RealizeVirtualSpace(const SelectionPosition
&position
) {
2009 // Return the new position with no virtual space
2010 return SelectionPosition(RealizeVirtualSpace(position
.Position(), position
.VirtualSpace()));
2013 void Editor::AddChar(char ch
) {
2014 const char s
[1] {ch
};
2015 InsertCharacter(std::string_view(s
, 1), CharacterSource::DirectInput
);
2018 void Editor::FilterSelections() {
2019 if (!additionalSelectionTyping
&& (sel
.Count() > 1)) {
2020 InvalidateWholeSelection();
2021 sel
.DropAdditionalRanges();
2025 // InsertCharacter inserts a character encoded in document code page.
2026 void Editor::InsertCharacter(std::string_view sv
, CharacterSource charSource
) {
2031 bool wrapOccurred
= false;
2033 UndoGroup
ug(pdoc
, (sel
.Count() > 1) || !sel
.Empty() || inOverstrike
);
2035 // Vector elements point into selection in order to change selection.
2036 std::vector
<SelectionRange
*> selPtrs
;
2037 for (size_t r
= 0; r
< sel
.Count(); r
++) {
2038 selPtrs
.push_back(&sel
.Range(r
));
2040 // Order selections by position in document.
2041 std::sort(selPtrs
.begin(), selPtrs
.end(),
2042 [](const SelectionRange
*a
, const SelectionRange
*b
) noexcept
{return *a
< *b
;});
2044 // Loop in reverse to avoid disturbing positions of selections yet to be processed.
2045 for (std::vector
<SelectionRange
*>::reverse_iterator rit
= selPtrs
.rbegin();
2046 rit
!= selPtrs
.rend(); ++rit
) {
2047 SelectionRange
*currentSel
= *rit
;
2048 if (!RangeContainsProtected(currentSel
->Start().Position(),
2049 currentSel
->End().Position())) {
2050 Sci::Position positionInsert
= currentSel
->Start().Position();
2051 if (!currentSel
->Empty()) {
2052 if (currentSel
->Length()) {
2053 pdoc
->DeleteChars(positionInsert
, currentSel
->Length());
2054 currentSel
->ClearVirtualSpace();
2056 // Range is all virtual so collapse to start of virtual space
2057 currentSel
->MinimizeVirtualSpace();
2059 } else if (inOverstrike
) {
2060 if (positionInsert
< pdoc
->Length()) {
2061 if (!pdoc
->IsPositionInLineEnd(positionInsert
)) {
2062 pdoc
->DelChar(positionInsert
);
2063 currentSel
->ClearVirtualSpace();
2067 positionInsert
= RealizeVirtualSpace(positionInsert
, currentSel
->caret
.VirtualSpace());
2068 const Sci::Position lengthInserted
= pdoc
->InsertString(positionInsert
, sv
);
2069 if (lengthInserted
> 0) {
2070 currentSel
->caret
.SetPosition(positionInsert
+ lengthInserted
);
2071 currentSel
->anchor
.SetPosition(positionInsert
+ lengthInserted
);
2073 currentSel
->ClearVirtualSpace();
2074 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
2076 AutoSurface
surface(this);
2078 if (WrapOneLine(surface
, pdoc
->SciLineFromPosition(positionInsert
))) {
2079 wrapOccurred
= true;
2088 SetVerticalScrollPos();
2091 ThinRectangularRange();
2092 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
2093 EnsureCaretVisible();
2094 // Avoid blinking during rapid typing:
2095 ShowCaretAtCurrentPosition();
2096 if ((caretSticky
== CaretSticky::Off
) ||
2097 ((caretSticky
== CaretSticky::WhiteSpace
) && !IsAllSpacesOrTabs(sv
))) {
2101 int ch
= static_cast<unsigned char>(sv
[0]);
2102 if (pdoc
->dbcsCodePage
!= CpUtf8
) {
2103 if (sv
.length() > 1) {
2104 // DBCS code page or DBCS font character set.
2105 ch
= (ch
<< 8) | static_cast<unsigned char>(sv
[1]);
2108 if ((ch
< 0xC0) || (1 == sv
.length())) {
2109 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
2110 // characters when not in UTF-8 mode.
2111 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
2112 // characters representing themselves.
2114 unsigned int utf32
[1] = { 0 };
2115 UTF32FromUTF8(sv
, utf32
, std::size(utf32
));
2119 NotifyChar(ch
, charSource
);
2121 if (recordingMacro
&& charSource
!= CharacterSource::TentativeInput
) {
2122 std::string
copy(sv
); // ensure NUL-terminated
2123 NotifyMacroRecord(Message::ReplaceSel
, 0, reinterpret_cast<sptr_t
>(copy
.data()));
2127 void Editor::ClearBeforeTentativeStart() {
2128 // Make positions for the first composition string.
2130 UndoGroup
ug(pdoc
, (sel
.Count() > 1) || !sel
.Empty() || inOverstrike
);
2131 for (size_t r
= 0; r
<sel
.Count(); r
++) {
2132 if (!RangeContainsProtected(sel
.Range(r
).Start().Position(),
2133 sel
.Range(r
).End().Position())) {
2134 const Sci::Position positionInsert
= sel
.Range(r
).Start().Position();
2135 if (!sel
.Range(r
).Empty()) {
2136 if (sel
.Range(r
).Length()) {
2137 pdoc
->DeleteChars(positionInsert
, sel
.Range(r
).Length());
2138 sel
.Range(r
).ClearVirtualSpace();
2140 // Range is all virtual so collapse to start of virtual space
2141 sel
.Range(r
).MinimizeVirtualSpace();
2144 RealizeVirtualSpace(positionInsert
, sel
.Range(r
).caret
.VirtualSpace());
2145 sel
.Range(r
).ClearVirtualSpace();
2150 void Editor::InsertPaste(const char *text
, Sci::Position len
) {
2151 if (multiPasteMode
== MultiPaste::Once
) {
2152 SelectionPosition selStart
= sel
.Start();
2153 selStart
= RealizeVirtualSpace(selStart
);
2154 const Sci::Position lengthInserted
= pdoc
->InsertString(selStart
.Position(), text
, len
);
2155 if (lengthInserted
> 0) {
2156 SetEmptySelection(selStart
.Position() + lengthInserted
);
2160 for (size_t r
=0; r
<sel
.Count(); r
++) {
2161 if (!RangeContainsProtected(sel
.Range(r
).Start().Position(),
2162 sel
.Range(r
).End().Position())) {
2163 Sci::Position positionInsert
= sel
.Range(r
).Start().Position();
2164 if (!sel
.Range(r
).Empty()) {
2165 if (sel
.Range(r
).Length()) {
2166 pdoc
->DeleteChars(positionInsert
, sel
.Range(r
).Length());
2167 sel
.Range(r
).ClearVirtualSpace();
2169 // Range is all virtual so collapse to start of virtual space
2170 sel
.Range(r
).MinimizeVirtualSpace();
2173 positionInsert
= RealizeVirtualSpace(positionInsert
, sel
.Range(r
).caret
.VirtualSpace());
2174 const Sci::Position lengthInserted
= pdoc
->InsertString(positionInsert
, text
, len
);
2175 if (lengthInserted
> 0) {
2176 sel
.Range(r
).caret
.SetPosition(positionInsert
+ lengthInserted
);
2177 sel
.Range(r
).anchor
.SetPosition(positionInsert
+ lengthInserted
);
2179 sel
.Range(r
).ClearVirtualSpace();
2185 void Editor::InsertPasteShape(const char *text
, Sci::Position len
, PasteShape shape
) {
2186 std::string convertedText
;
2187 if (convertPastes
) {
2188 // Convert line endings of the paste into our local line-endings mode
2189 convertedText
= Document::TransformLineEnds(text
, len
, pdoc
->eolMode
);
2190 len
= convertedText
.length();
2191 text
= convertedText
.c_str();
2193 if (shape
== PasteShape::rectangular
) {
2194 PasteRectangular(sel
.Start(), text
, len
);
2196 if (shape
== PasteShape::line
) {
2197 const Sci::Position insertPos
=
2198 pdoc
->LineStart(pdoc
->LineFromPosition(sel
.MainCaret()));
2199 Sci::Position lengthInserted
= pdoc
->InsertString(insertPos
, text
, len
);
2200 // add the newline if necessary
2201 if ((len
> 0) && (text
[len
- 1] != '\n' && text
[len
- 1] != '\r')) {
2202 const std::string_view endline
= pdoc
->EOLString();
2203 lengthInserted
+= pdoc
->InsertString(insertPos
+ lengthInserted
, endline
);
2205 if (sel
.MainCaret() == insertPos
) {
2206 SetEmptySelection(sel
.MainCaret() + lengthInserted
);
2209 InsertPaste(text
, len
);
2214 void Editor::ClearSelection(bool retainMultipleSelections
) {
2215 if (!sel
.IsRectangular() && !retainMultipleSelections
)
2218 for (size_t r
=0; r
<sel
.Count(); r
++) {
2219 if (!sel
.Range(r
).Empty()) {
2220 if (!RangeContainsProtected(sel
.Range(r
).Start().Position(),
2221 sel
.Range(r
).End().Position())) {
2222 pdoc
->DeleteChars(sel
.Range(r
).Start().Position(),
2223 sel
.Range(r
).Length());
2224 sel
.Range(r
) = SelectionRange(sel
.Range(r
).Start());
2228 ThinRectangularRange();
2229 sel
.RemoveDuplicates();
2231 SetHoverIndicatorPosition(sel
.MainCaret());
2234 void Editor::ClearAll() {
2237 if (0 != pdoc
->Length()) {
2238 pdoc
->DeleteChars(0, pdoc
->Length());
2240 if (!pdoc
->IsReadOnly()) {
2242 pdoc
->AnnotationClearAll();
2243 pdoc
->EOLAnnotationClearAll();
2244 pdoc
->MarginClearAll();
2248 view
.ClearAllTabstops();
2252 SetVerticalScrollPos();
2253 InvalidateStyleRedraw();
2256 void Editor::ClearDocumentStyle() {
2257 pdoc
->decorations
->DeleteLexerDecorations();
2258 pdoc
->StartStyling(0);
2259 pdoc
->SetStyleFor(pdoc
->Length(), 0);
2261 SetAnnotationHeights(0, pdoc
->LinesTotal());
2262 pdoc
->ClearLevels();
2265 void Editor::CopyAllowLine() {
2266 SelectionText selectedText
;
2267 CopySelectionRange(&selectedText
, true);
2268 CopyToClipboard(selectedText
);
2271 void Editor::Cut() {
2272 pdoc
->CheckReadOnly();
2273 if (!pdoc
->IsReadOnly() && !SelectionContainsProtected()) {
2279 void Editor::PasteRectangular(SelectionPosition pos
, const char *ptr
, Sci::Position len
) {
2280 if (pdoc
->IsReadOnly() || SelectionContainsProtected()) {
2284 sel
.RangeMain() = SelectionRange(pos
);
2285 Sci::Line line
= pdoc
->SciLineFromPosition(sel
.MainCaret());
2287 sel
.RangeMain().caret
= RealizeVirtualSpace(sel
.RangeMain().caret
);
2288 const int xInsert
= XFromPosition(sel
.RangeMain().caret
);
2289 bool prevCr
= false;
2290 while ((len
> 0) && IsEOLCharacter(ptr
[len
-1]))
2292 for (Sci::Position i
= 0; i
< len
; i
++) {
2293 if (IsEOLCharacter(ptr
[i
])) {
2294 if ((ptr
[i
] == '\r') || (!prevCr
))
2296 if (line
>= pdoc
->LinesTotal()) {
2297 const std::string_view eol
= pdoc
->EOLString();
2298 pdoc
->InsertString(pdoc
->LengthNoExcept(), eol
);
2300 // Pad the end of lines with spaces if required
2301 sel
.RangeMain().caret
.SetPosition(PositionFromLineX(line
, xInsert
));
2302 if ((XFromPosition(sel
.RangeMain().caret
) < xInsert
) && (i
+ 1 < len
)) {
2303 while (XFromPosition(sel
.RangeMain().caret
) < xInsert
) {
2305 const Sci::Position lengthInserted
= pdoc
->InsertString(sel
.MainCaret(), " ", 1);
2306 sel
.RangeMain().caret
.Add(lengthInserted
);
2309 prevCr
= ptr
[i
] == '\r';
2311 const Sci::Position lengthInserted
= pdoc
->InsertString(sel
.MainCaret(), ptr
+ i
, 1);
2312 sel
.RangeMain().caret
.Add(lengthInserted
);
2316 SetEmptySelection(pos
);
2319 bool Editor::CanPaste() {
2320 return !pdoc
->IsReadOnly() && !SelectionContainsProtected();
2323 void Editor::Clear() {
2324 // If multiple selections, don't delete EOLS
2326 bool singleVirtual
= false;
2327 if ((sel
.Count() == 1) &&
2328 !RangeContainsProtected(sel
.MainCaret(), sel
.MainCaret() + 1) &&
2329 sel
.RangeMain().Start().VirtualSpace()) {
2330 singleVirtual
= true;
2332 UndoGroup
ug(pdoc
, (sel
.Count() > 1) || singleVirtual
);
2333 for (size_t r
=0; r
<sel
.Count(); r
++) {
2334 if (!RangeContainsProtected(sel
.Range(r
).caret
.Position(), sel
.Range(r
).caret
.Position() + 1)) {
2335 if (sel
.Range(r
).Start().VirtualSpace()) {
2336 if (sel
.Range(r
).anchor
< sel
.Range(r
).caret
)
2337 sel
.Range(r
) = SelectionRange(RealizeVirtualSpace(sel
.Range(r
).anchor
.Position(), sel
.Range(r
).anchor
.VirtualSpace()));
2339 sel
.Range(r
) = SelectionRange(RealizeVirtualSpace(sel
.Range(r
).caret
.Position(), sel
.Range(r
).caret
.VirtualSpace()));
2341 if ((sel
.Count() == 1) || !pdoc
->IsPositionInLineEnd(sel
.Range(r
).caret
.Position())) {
2342 pdoc
->DelChar(sel
.Range(r
).caret
.Position());
2343 sel
.Range(r
).ClearVirtualSpace();
2344 } // else multiple selection so don't eat line ends
2346 sel
.Range(r
).ClearVirtualSpace();
2352 sel
.RemoveDuplicates();
2353 ShowCaretAtCurrentPosition(); // Avoid blinking
2356 void Editor::SelectAll() {
2358 SetSelection(0, pdoc
->Length());
2362 void Editor::Undo() {
2363 if (pdoc
->CanUndo()) {
2365 const Sci::Position newPos
= pdoc
->Undo();
2367 SetEmptySelection(newPos
);
2368 EnsureCaretVisible();
2372 void Editor::Redo() {
2373 if (pdoc
->CanRedo()) {
2374 const Sci::Position newPos
= pdoc
->Redo();
2376 SetEmptySelection(newPos
);
2377 EnsureCaretVisible();
2381 void Editor::DelCharBack(bool allowLineStartDeletion
) {
2383 if (!sel
.IsRectangular())
2385 if (sel
.IsRectangular())
2386 allowLineStartDeletion
= false;
2387 UndoGroup
ug(pdoc
, (sel
.Count() > 1) || !sel
.Empty());
2389 for (size_t r
=0; r
<sel
.Count(); r
++) {
2390 if (!RangeContainsProtected(sel
.Range(r
).caret
.Position() - 1, sel
.Range(r
).caret
.Position())) {
2391 if (sel
.Range(r
).caret
.VirtualSpace()) {
2392 sel
.Range(r
).caret
.SetVirtualSpace(sel
.Range(r
).caret
.VirtualSpace() - 1);
2393 sel
.Range(r
).anchor
.SetVirtualSpace(sel
.Range(r
).caret
.VirtualSpace());
2395 const Sci::Line lineCurrentPos
=
2396 pdoc
->SciLineFromPosition(sel
.Range(r
).caret
.Position());
2397 if (allowLineStartDeletion
|| (pdoc
->LineStart(lineCurrentPos
) != sel
.Range(r
).caret
.Position())) {
2398 if (pdoc
->GetColumn(sel
.Range(r
).caret
.Position()) <= pdoc
->GetLineIndentation(lineCurrentPos
) &&
2399 pdoc
->GetColumn(sel
.Range(r
).caret
.Position()) > 0 && pdoc
->backspaceUnindents
) {
2400 UndoGroup
ugInner(pdoc
, !ug
.Needed());
2401 const int indentation
= pdoc
->GetLineIndentation(lineCurrentPos
);
2402 const int indentationStep
= pdoc
->IndentSize();
2403 int indentationChange
= indentation
% indentationStep
;
2404 if (indentationChange
== 0)
2405 indentationChange
= indentationStep
;
2406 const Sci::Position posSelect
= pdoc
->SetLineIndentation(lineCurrentPos
, indentation
- indentationChange
);
2407 // SetEmptySelection
2408 sel
.Range(r
) = SelectionRange(posSelect
);
2410 pdoc
->DelCharBack(sel
.Range(r
).caret
.Position());
2415 sel
.Range(r
).ClearVirtualSpace();
2418 ThinRectangularRange();
2422 sel
.RemoveDuplicates();
2423 ContainerNeedsUpdate(Update::Selection
);
2424 // Avoid blinking during rapid typing:
2425 ShowCaretAtCurrentPosition();
2428 void Editor::NotifyFocus(bool focus
) {
2429 NotificationData scn
= {};
2430 scn
.nmhdr
.code
= focus
? Notification::FocusIn
: Notification::FocusOut
;
2434 void Editor::SetCtrlID(int identifier
) {
2435 ctrlID
= identifier
;
2438 void Editor::NotifyStyleToNeeded(Sci::Position endStyleNeeded
) {
2439 NotificationData scn
= {};
2440 scn
.nmhdr
.code
= Notification::StyleNeeded
;
2441 scn
.position
= endStyleNeeded
;
2445 void Editor::NotifyStyleNeeded(Document
*, void *, Sci::Position endStyleNeeded
) {
2446 NotifyStyleToNeeded(endStyleNeeded
);
2449 void Editor::NotifyErrorOccurred(Document
*, void *, Status status
) {
2450 errorStatus
= status
;
2453 void Editor::NotifyChar(int ch
, CharacterSource charSource
) {
2454 NotificationData scn
= {};
2455 scn
.nmhdr
.code
= Notification::CharAdded
;
2457 scn
.characterSource
= charSource
;
2461 void Editor::NotifySavePoint(bool isSavePoint
) {
2462 NotificationData scn
= {};
2464 scn
.nmhdr
.code
= Notification::SavePointReached
;
2465 if (changeHistoryOption
!= ChangeHistoryOption::Disabled
) {
2469 scn
.nmhdr
.code
= Notification::SavePointLeft
;
2474 void Editor::NotifyModifyAttempt() {
2475 NotificationData scn
= {};
2476 scn
.nmhdr
.code
= Notification::ModifyAttemptRO
;
2480 void Editor::NotifyDoubleClick(Point pt
, KeyMod modifiers
) {
2481 NotificationData scn
= {};
2482 scn
.nmhdr
.code
= Notification::DoubleClick
;
2483 scn
.line
= LineFromLocation(pt
);
2484 scn
.position
= PositionFromLocation(pt
, true);
2485 scn
.modifiers
= modifiers
;
2489 void Editor::NotifyHotSpotDoubleClicked(Sci::Position position
, KeyMod modifiers
) {
2490 NotificationData scn
= {};
2491 scn
.nmhdr
.code
= Notification::HotSpotDoubleClick
;
2492 scn
.position
= position
;
2493 scn
.modifiers
= modifiers
;
2497 void Editor::NotifyHotSpotClicked(Sci::Position position
, KeyMod modifiers
) {
2498 NotificationData scn
= {};
2499 scn
.nmhdr
.code
= Notification::HotSpotClick
;
2500 scn
.position
= position
;
2501 scn
.modifiers
= modifiers
;
2505 void Editor::NotifyHotSpotReleaseClick(Sci::Position position
, KeyMod modifiers
) {
2506 NotificationData scn
= {};
2507 scn
.nmhdr
.code
= Notification::HotSpotReleaseClick
;
2508 scn
.position
= position
;
2509 scn
.modifiers
= modifiers
;
2513 bool Editor::NotifyUpdateUI() {
2514 if (needUpdateUI
!= Update::None
) {
2515 NotificationData scn
= {};
2516 scn
.nmhdr
.code
= Notification::UpdateUI
;
2517 scn
.updated
= needUpdateUI
;
2519 needUpdateUI
= Update::None
;
2525 void Editor::NotifyPainted() {
2526 NotificationData scn
= {};
2527 scn
.nmhdr
.code
= Notification::Painted
;
2531 void Editor::NotifyIndicatorClick(bool click
, Sci::Position position
, KeyMod modifiers
) {
2532 const int mask
= pdoc
->decorations
->AllOnFor(position
);
2533 if ((click
&& mask
) || pdoc
->decorations
->ClickNotified()) {
2534 NotificationData scn
= {};
2535 pdoc
->decorations
->SetClickNotified(click
);
2536 scn
.nmhdr
.code
= click
? Notification::IndicatorClick
: Notification::IndicatorRelease
;
2537 scn
.modifiers
= modifiers
;
2538 scn
.position
= position
;
2543 bool Editor::NotifyMarginClick(Point pt
, KeyMod modifiers
) {
2544 const int marginClicked
= vs
.MarginFromLocation(pt
);
2545 if ((marginClicked
>= 0) && vs
.ms
[marginClicked
].sensitive
) {
2546 const Sci::Position position
= pdoc
->LineStart(LineFromLocation(pt
));
2547 if ((vs
.ms
[marginClicked
].mask
& MaskFolders
) && (FlagSet(foldAutomatic
, AutomaticFold::Click
))) {
2548 const bool ctrl
= FlagSet(modifiers
, KeyMod::Ctrl
);
2549 const bool shift
= FlagSet(modifiers
, KeyMod::Shift
);
2550 const Sci::Line lineClick
= pdoc
->SciLineFromPosition(position
);
2551 if (shift
&& ctrl
) {
2552 FoldAll(FoldAction::Toggle
);
2554 const FoldLevel levelClick
= pdoc
->GetFoldLevel(lineClick
);
2555 if (LevelIsHeader(levelClick
)) {
2557 // Ensure all children visible
2558 FoldExpand(lineClick
, FoldAction::Expand
, levelClick
);
2560 FoldExpand(lineClick
, FoldAction::Toggle
, levelClick
);
2563 FoldLine(lineClick
, FoldAction::Toggle
);
2569 NotificationData scn
= {};
2570 scn
.nmhdr
.code
= Notification::MarginClick
;
2571 scn
.modifiers
= modifiers
;
2572 scn
.position
= position
;
2573 scn
.margin
= marginClicked
;
2581 bool Editor::NotifyMarginRightClick(Point pt
, KeyMod modifiers
) {
2582 const int marginRightClicked
= vs
.MarginFromLocation(pt
);
2583 if ((marginRightClicked
>= 0) && vs
.ms
[marginRightClicked
].sensitive
) {
2584 const Sci::Position position
= pdoc
->LineStart(LineFromLocation(pt
));
2585 NotificationData scn
= {};
2586 scn
.nmhdr
.code
= Notification::MarginRightClick
;
2587 scn
.modifiers
= modifiers
;
2588 scn
.position
= position
;
2589 scn
.margin
= marginRightClicked
;
2597 void Editor::NotifyNeedShown(Sci::Position pos
, Sci::Position len
) {
2598 NotificationData scn
= {};
2599 scn
.nmhdr
.code
= Notification::NeedShown
;
2605 void Editor::NotifyDwelling(Point pt
, bool state
) {
2606 NotificationData scn
= {};
2607 scn
.nmhdr
.code
= state
? Notification::DwellStart
: Notification::DwellEnd
;
2608 scn
.position
= PositionFromLocation(pt
, true);
2609 scn
.x
= static_cast<int>(pt
.x
+ vs
.ExternalMarginWidth());
2610 scn
.y
= static_cast<int>(pt
.y
);
2614 void Editor::NotifyZoom() {
2615 NotificationData scn
= {};
2616 scn
.nmhdr
.code
= Notification::Zoom
;
2620 // Notifications from document
2621 void Editor::NotifyModifyAttempt(Document
*, void *) {
2622 //Platform::DebugPrintf("** Modify Attempt\n");
2623 NotifyModifyAttempt();
2626 void Editor::NotifySavePoint(Document
*, void *, bool atSavePoint
) {
2627 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
2628 NotifySavePoint(atSavePoint
);
2631 void Editor::CheckModificationForWrap(DocModification mh
) {
2632 if (FlagSet(mh
.modificationType
, ModificationFlags::InsertText
| ModificationFlags::DeleteText
)) {
2633 view
.llc
.Invalidate(LineLayout::ValidLevel::checkTextAndStyle
);
2634 const Sci::Line lineDoc
= pdoc
->SciLineFromPosition(mh
.position
);
2635 const Sci::Line lines
= std::max(static_cast<Sci::Line
>(0), mh
.linesAdded
);
2637 NeedWrapping(lineDoc
, lineDoc
+ lines
+ 1);
2640 // Fix up annotation heights
2641 SetAnnotationHeights(lineDoc
, lineDoc
+ lines
+ 2);
2647 // Move a position so it is still after the same character as before the insertion.
2648 constexpr Sci::Position
MovePositionForInsertion(Sci::Position position
, Sci::Position startInsertion
, Sci::Position length
) noexcept
{
2649 if (position
> startInsertion
) {
2650 return position
+ length
;
2655 // Move a position so it is still after the same character as before the deletion if that
2656 // character is still present else after the previous surviving character.
2657 constexpr Sci::Position
MovePositionForDeletion(Sci::Position position
, Sci::Position startDeletion
, Sci::Position length
) noexcept
{
2658 if (position
> startDeletion
) {
2659 const Sci::Position endDeletion
= startDeletion
+ length
;
2660 if (position
> endDeletion
) {
2661 return position
- length
;
2663 return startDeletion
;
2672 void Editor::NotifyModified(Document
*, DocModification mh
, void *) {
2673 ContainerNeedsUpdate(Update::Content
);
2674 if (paintState
== PaintState::painting
) {
2675 CheckForChangeOutsidePaint(Range(mh
.position
, mh
.position
+ mh
.length
));
2677 if (FlagSet(mh
.modificationType
, ModificationFlags::ChangeLineState
)) {
2678 if (paintState
== PaintState::painting
) {
2679 CheckForChangeOutsidePaint(
2680 Range(pdoc
->LineStart(mh
.line
),
2681 pdoc
->LineStart(mh
.line
+ 1)));
2683 // Could check that change is before last visible line.
2687 if (FlagSet(mh
.modificationType
, ModificationFlags::ChangeTabStops
)) {
2690 if (FlagSet(mh
.modificationType
, ModificationFlags::LexerState
)) {
2691 if (paintState
== PaintState::painting
) {
2692 CheckForChangeOutsidePaint(
2693 Range(mh
.position
, mh
.position
+ mh
.length
));
2698 if (FlagSet(mh
.modificationType
, ModificationFlags::ChangeStyle
| ModificationFlags::ChangeIndicator
)) {
2699 if (FlagSet(mh
.modificationType
, ModificationFlags::ChangeStyle
)) {
2700 pdoc
->IncrementStyleClock();
2702 if (paintState
== PaintState::notPainting
) {
2703 const Sci::Line lineDocTop
= pcs
->DocFromDisplay(topLine
);
2704 if (mh
.position
< pdoc
->LineStart(lineDocTop
)) {
2705 // Styling performed before this view
2708 InvalidateRange(mh
.position
, mh
.position
+ mh
.length
);
2711 if (FlagSet(mh
.modificationType
, ModificationFlags::ChangeStyle
)) {
2712 view
.llc
.Invalidate(LineLayout::ValidLevel::checkTextAndStyle
);
2715 // Move selection and brace highlights
2716 if (FlagSet(mh
.modificationType
, ModificationFlags::InsertText
)) {
2717 sel
.MovePositions(true, mh
.position
, mh
.length
);
2718 braces
[0] = MovePositionForInsertion(braces
[0], mh
.position
, mh
.length
);
2719 braces
[1] = MovePositionForInsertion(braces
[1], mh
.position
, mh
.length
);
2720 } else if (FlagSet(mh
.modificationType
, ModificationFlags::DeleteText
)) {
2721 sel
.MovePositions(false, mh
.position
, mh
.length
);
2722 braces
[0] = MovePositionForDeletion(braces
[0], mh
.position
, mh
.length
);
2723 braces
[1] = MovePositionForDeletion(braces
[1], mh
.position
, mh
.length
);
2725 if (FlagSet(mh
.modificationType
, ModificationFlags::BeforeInsert
| ModificationFlags::BeforeDelete
) && pcs
->HiddenLines()) {
2726 // Some lines are hidden so may need shown.
2727 const Sci::Line lineOfPos
= pdoc
->SciLineFromPosition(mh
.position
);
2728 Sci::Position endNeedShown
= mh
.position
;
2729 if (FlagSet(mh
.modificationType
, ModificationFlags::BeforeInsert
)) {
2730 if (pdoc
->ContainsLineEnd(mh
.text
, mh
.length
) && (mh
.position
!= pdoc
->LineStart(lineOfPos
)))
2731 endNeedShown
= pdoc
->LineStart(lineOfPos
+1);
2732 } else if (FlagSet(mh
.modificationType
, ModificationFlags::BeforeDelete
)) {
2733 // If the deletion includes any EOL then we extend the need shown area.
2734 endNeedShown
= mh
.position
+ mh
.length
;
2735 Sci::Line lineLast
= pdoc
->SciLineFromPosition(mh
.position
+mh
.length
);
2736 for (Sci::Line line
= lineOfPos
+ 1; line
<= lineLast
; line
++) {
2737 const Sci::Line lineMaxSubord
= pdoc
->GetLastChild(line
, {}, -1);
2738 if (lineLast
< lineMaxSubord
) {
2739 lineLast
= lineMaxSubord
;
2740 endNeedShown
= pdoc
->LineEnd(lineLast
);
2744 NeedShown(mh
.position
, endNeedShown
- mh
.position
);
2746 if (mh
.linesAdded
!= 0) {
2747 // Update contraction state for inserted and removed lines
2748 // lineOfPos should be calculated in context of state before modification, shouldn't it
2749 Sci::Line lineOfPos
= pdoc
->SciLineFromPosition(mh
.position
);
2750 if (mh
.position
> pdoc
->LineStart(lineOfPos
))
2751 lineOfPos
++; // Affecting subsequent lines
2752 if (mh
.linesAdded
> 0) {
2753 pcs
->InsertLines(lineOfPos
, mh
.linesAdded
);
2755 pcs
->DeleteLines(lineOfPos
, -mh
.linesAdded
);
2757 view
.LinesAddedOrRemoved(lineOfPos
, mh
.linesAdded
);
2759 if (FlagSet(mh
.modificationType
, ModificationFlags::ChangeAnnotation
)) {
2760 const Sci::Line lineDoc
= pdoc
->SciLineFromPosition(mh
.position
);
2761 if (vs
.annotationVisible
!= AnnotationVisible::Hidden
) {
2762 if (pcs
->SetHeight(lineDoc
, pcs
->GetHeight(lineDoc
) + static_cast<int>(mh
.annotationLinesAdded
))) {
2768 if (FlagSet(mh
.modificationType
, ModificationFlags::ChangeEOLAnnotation
)) {
2769 if (vs
.eolAnnotationVisible
!= EOLAnnotationVisible::Hidden
) {
2773 CheckModificationForWrap(mh
);
2774 if (mh
.linesAdded
!= 0) {
2775 // Avoid scrolling of display if change before current display
2776 if (mh
.position
< posTopLine
&& !CanDeferToLastStep(mh
)) {
2777 const Sci::Line newTop
= std::clamp
<Sci::Line
>(topLine
+ mh
.linesAdded
, 0, MaxScrollPos());
2778 if (newTop
!= topLine
) {
2780 SetVerticalScrollPos();
2784 if (paintState
== PaintState::notPainting
&& !CanDeferToLastStep(mh
)) {
2785 if (SynchronousStylingToVisible()) {
2786 QueueIdleWork(WorkItems::style
, pdoc
->Length());
2791 if (paintState
== PaintState::notPainting
&& mh
.length
&& !CanEliminate(mh
)) {
2792 if (SynchronousStylingToVisible()) {
2793 QueueIdleWork(WorkItems::style
, mh
.position
+ mh
.length
);
2795 InvalidateRange(mh
.position
, mh
.position
+ mh
.length
);
2796 if (FlagSet(changeHistoryOption
, ChangeHistoryOption::Markers
)) {
2797 RedrawSelMargin(pdoc
->SciLineFromPosition(mh
.position
));
2803 if (mh
.linesAdded
!= 0 && !CanDeferToLastStep(mh
)) {
2807 if ((FlagSet(mh
.modificationType
, ModificationFlags::ChangeMarker
)) || (FlagSet(mh
.modificationType
, ModificationFlags::ChangeMargin
))) {
2808 if ((!willRedrawAll
) && ((paintState
== PaintState::notPainting
) || !PaintContainsMargin())) {
2809 if (FlagSet(mh
.modificationType
, ModificationFlags::ChangeFold
)) {
2810 // Fold changes can affect the drawing of following lines so redraw whole margin
2811 RedrawSelMargin(marginView
.highlightDelimiter
.isEnabled
? -1 : mh
.line
- 1, true);
2813 RedrawSelMargin(mh
.line
);
2817 if ((FlagSet(mh
.modificationType
, ModificationFlags::ChangeFold
)) && (FlagSet(foldAutomatic
, AutomaticFold::Change
))) {
2818 FoldChanged(mh
.line
, mh
.foldLevelNow
, mh
.foldLevelPrev
);
2821 // NOW pay the piper WRT "deferred" visual updates
2822 if (IsLastStep(mh
)) {
2827 // If client wants to see this modification
2828 if (FlagSet(mh
.modificationType
, modEventMask
)) {
2829 if (commandEvents
) {
2830 if ((mh
.modificationType
& (ModificationFlags::ChangeStyle
| ModificationFlags::ChangeIndicator
)) == ModificationFlags::None
) {
2831 // Real modification made to text of document.
2832 NotifyChange(); // Send EN_CHANGE
2836 NotificationData scn
= {};
2837 scn
.nmhdr
.code
= Notification::Modified
;
2838 scn
.position
= mh
.position
;
2839 scn
.modificationType
= mh
.modificationType
;
2841 scn
.length
= mh
.length
;
2842 scn
.linesAdded
= mh
.linesAdded
;
2844 scn
.foldLevelNow
= mh
.foldLevelNow
;
2845 scn
.foldLevelPrev
= mh
.foldLevelPrev
;
2846 scn
.token
= static_cast<int>(mh
.token
);
2847 scn
.annotationLinesAdded
= mh
.annotationLinesAdded
;
2852 void Editor::NotifyDeleted(Document
*, void *) noexcept
{
2856 void Editor::NotifyMacroRecord(Message iMessage
, uptr_t wParam
, sptr_t lParam
) {
2858 // Enumerates all macroable messages
2862 case Message::Paste
:
2863 case Message::Clear
:
2864 case Message::ReplaceSel
:
2865 case Message::AddText
:
2866 case Message::InsertText
:
2867 case Message::AppendText
:
2868 case Message::ClearAll
:
2869 case Message::SelectAll
:
2870 case Message::GotoLine
:
2871 case Message::GotoPos
:
2872 case Message::SearchAnchor
:
2873 case Message::SearchNext
:
2874 case Message::SearchPrev
:
2875 case Message::LineDown
:
2876 case Message::LineDownExtend
:
2877 case Message::ParaDown
:
2878 case Message::ParaDownExtend
:
2879 case Message::LineUp
:
2880 case Message::LineUpExtend
:
2881 case Message::ParaUp
:
2882 case Message::ParaUpExtend
:
2883 case Message::CharLeft
:
2884 case Message::CharLeftExtend
:
2885 case Message::CharRight
:
2886 case Message::CharRightExtend
:
2887 case Message::WordLeft
:
2888 case Message::WordLeftExtend
:
2889 case Message::WordRight
:
2890 case Message::WordRightExtend
:
2891 case Message::WordPartLeft
:
2892 case Message::WordPartLeftExtend
:
2893 case Message::WordPartRight
:
2894 case Message::WordPartRightExtend
:
2895 case Message::WordLeftEnd
:
2896 case Message::WordLeftEndExtend
:
2897 case Message::WordRightEnd
:
2898 case Message::WordRightEndExtend
:
2900 case Message::HomeExtend
:
2901 case Message::LineEnd
:
2902 case Message::LineEndExtend
:
2903 case Message::HomeWrap
:
2904 case Message::HomeWrapExtend
:
2905 case Message::LineEndWrap
:
2906 case Message::LineEndWrapExtend
:
2907 case Message::DocumentStart
:
2908 case Message::DocumentStartExtend
:
2909 case Message::DocumentEnd
:
2910 case Message::DocumentEndExtend
:
2911 case Message::StutteredPageUp
:
2912 case Message::StutteredPageUpExtend
:
2913 case Message::StutteredPageDown
:
2914 case Message::StutteredPageDownExtend
:
2915 case Message::PageUp
:
2916 case Message::PageUpExtend
:
2917 case Message::PageDown
:
2918 case Message::PageDownExtend
:
2919 case Message::EditToggleOvertype
:
2920 case Message::Cancel
:
2921 case Message::DeleteBack
:
2923 case Message::BackTab
:
2924 case Message::FormFeed
:
2925 case Message::VCHome
:
2926 case Message::VCHomeExtend
:
2927 case Message::VCHomeWrap
:
2928 case Message::VCHomeWrapExtend
:
2929 case Message::VCHomeDisplay
:
2930 case Message::VCHomeDisplayExtend
:
2931 case Message::DelWordLeft
:
2932 case Message::DelWordRight
:
2933 case Message::DelWordRightEnd
:
2934 case Message::DelLineLeft
:
2935 case Message::DelLineRight
:
2936 case Message::LineCopy
:
2937 case Message::LineCut
:
2938 case Message::LineDelete
:
2939 case Message::LineTranspose
:
2940 case Message::LineReverse
:
2941 case Message::LineDuplicate
:
2942 case Message::LowerCase
:
2943 case Message::UpperCase
:
2944 case Message::LineScrollDown
:
2945 case Message::LineScrollUp
:
2946 case Message::DeleteBackNotLine
:
2947 case Message::HomeDisplay
:
2948 case Message::HomeDisplayExtend
:
2949 case Message::LineEndDisplay
:
2950 case Message::LineEndDisplayExtend
:
2951 case Message::SetSelectionMode
:
2952 case Message::LineDownRectExtend
:
2953 case Message::LineUpRectExtend
:
2954 case Message::CharLeftRectExtend
:
2955 case Message::CharRightRectExtend
:
2956 case Message::HomeRectExtend
:
2957 case Message::VCHomeRectExtend
:
2958 case Message::LineEndRectExtend
:
2959 case Message::PageUpRectExtend
:
2960 case Message::PageDownRectExtend
:
2961 case Message::SelectionDuplicate
:
2962 case Message::CopyAllowLine
:
2963 case Message::VerticalCentreCaret
:
2964 case Message::MoveSelectedLinesUp
:
2965 case Message::MoveSelectedLinesDown
:
2966 case Message::ScrollToStart
:
2967 case Message::ScrollToEnd
:
2970 // Filter out all others like display changes. Also, newlines are redundant
2971 // with char insert messages.
2972 case Message::NewLine
:
2974 // printf("Filtered out %ld of macro recording\n", iMessage);
2978 // Send notification
2979 NotificationData scn
= {};
2980 scn
.nmhdr
.code
= Notification::MacroRecord
;
2981 scn
.message
= iMessage
;
2982 scn
.wParam
= wParam
;
2983 scn
.lParam
= lParam
;
2987 // Something has changed that the container should know about
2988 void Editor::ContainerNeedsUpdate(Update flags
) noexcept
{
2989 needUpdateUI
= needUpdateUI
| flags
;
2993 * Force scroll and keep position relative to top of window.
2995 * If stuttered = true and not already at first/last row, move to first/last row of window.
2996 * If stuttered = true and already at first/last row, scroll as normal.
2998 void Editor::PageMove(int direction
, Selection::SelTypes selt
, bool stuttered
) {
2999 Sci::Line topLineNew
;
3000 SelectionPosition newPos
;
3002 const Sci::Line currentLine
= pdoc
->SciLineFromPosition(sel
.MainCaret());
3003 const Sci::Line topStutterLine
= topLine
+ caretPolicies
.y
.slop
;
3004 const Sci::Line bottomStutterLine
=
3005 pdoc
->SciLineFromPosition(PositionFromLocation(
3006 Point::FromInts(lastXChosen
- xOffset
, direction
* vs
.lineHeight
* static_cast<int>(LinesToScroll()))))
3007 - caretPolicies
.y
.slop
- 1;
3009 if (stuttered
&& (direction
< 0 && currentLine
> topStutterLine
)) {
3010 topLineNew
= topLine
;
3011 newPos
= SPositionFromLocation(Point::FromInts(lastXChosen
- xOffset
, vs
.lineHeight
* caretPolicies
.y
.slop
),
3012 false, false, UserVirtualSpace());
3014 } else if (stuttered
&& (direction
> 0 && currentLine
< bottomStutterLine
)) {
3015 topLineNew
= topLine
;
3016 newPos
= SPositionFromLocation(Point::FromInts(lastXChosen
- xOffset
, vs
.lineHeight
* static_cast<int>(LinesToScroll() - caretPolicies
.y
.slop
)),
3017 false, false, UserVirtualSpace());
3020 const Point pt
= LocationFromPosition(sel
.MainCaret());
3022 topLineNew
= std::clamp
<Sci::Line
>(
3023 topLine
+ direction
* LinesToScroll(), 0, MaxScrollPos());
3024 newPos
= SPositionFromLocation(
3025 Point::FromInts(lastXChosen
- xOffset
, static_cast<int>(pt
.y
) +
3026 direction
* (vs
.lineHeight
* static_cast<int>(LinesToScroll()))),
3027 false, false, UserVirtualSpace());
3030 if (topLineNew
!= topLine
) {
3031 SetTopLine(topLineNew
);
3032 MovePositionTo(newPos
, selt
);
3033 SetVerticalScrollPos();
3036 MovePositionTo(newPos
, selt
);
3040 void Editor::ChangeCaseOfSelection(CaseMapping caseMapping
) {
3042 for (size_t r
=0; r
<sel
.Count(); r
++) {
3043 SelectionRange current
= sel
.Range(r
);
3044 SelectionRange currentNoVS
= current
;
3045 currentNoVS
.ClearVirtualSpace();
3046 const size_t rangeBytes
= currentNoVS
.Length();
3047 if (rangeBytes
> 0) {
3048 std::string sText
= RangeText(currentNoVS
.Start().Position(), currentNoVS
.End().Position());
3050 std::string sMapped
= CaseMapString(sText
, caseMapping
);
3052 if (sMapped
!= sText
) {
3053 size_t firstDifference
= 0;
3054 while (sMapped
[firstDifference
] == sText
[firstDifference
])
3056 size_t lastDifferenceText
= sText
.size() - 1;
3057 size_t lastDifferenceMapped
= sMapped
.size() - 1;
3058 while (sMapped
[lastDifferenceMapped
] == sText
[lastDifferenceText
]) {
3059 lastDifferenceText
--;
3060 lastDifferenceMapped
--;
3062 const size_t endDifferenceText
= sText
.size() - 1 - lastDifferenceText
;
3064 currentNoVS
.Start().Position() + firstDifference
,
3065 rangeBytes
- firstDifference
- endDifferenceText
);
3066 const Sci::Position lengthChange
= lastDifferenceMapped
- firstDifference
+ 1;
3067 const Sci::Position lengthInserted
= pdoc
->InsertString(
3068 currentNoVS
.Start().Position() + firstDifference
,
3069 sMapped
.c_str() + firstDifference
,
3071 // Automatic movement changes selection so reset to exactly the same as it was.
3072 const Sci::Position diffSizes
= sMapped
.size() - sText
.size() + lengthInserted
- lengthChange
;
3073 if (diffSizes
!= 0) {
3074 if (current
.anchor
> current
.caret
)
3075 current
.anchor
.Add(diffSizes
);
3077 current
.caret
.Add(diffSizes
);
3079 sel
.Range(r
) = current
;
3085 void Editor::LineTranspose() {
3086 const Sci::Line line
= pdoc
->SciLineFromPosition(sel
.MainCaret());
3090 const Sci::Position startPrevious
= pdoc
->LineStart(line
- 1);
3091 const std::string linePrevious
= RangeText(startPrevious
, pdoc
->LineEnd(line
- 1));
3093 Sci::Position startCurrent
= pdoc
->LineStart(line
);
3094 const std::string lineCurrent
= RangeText(startCurrent
, pdoc
->LineEnd(line
));
3096 pdoc
->DeleteChars(startCurrent
, lineCurrent
.length());
3097 pdoc
->DeleteChars(startPrevious
, linePrevious
.length());
3098 startCurrent
-= linePrevious
.length();
3100 startCurrent
+= pdoc
->InsertString(startPrevious
, lineCurrent
);
3101 pdoc
->InsertString(startCurrent
, linePrevious
);
3102 // Move caret to start of current line
3103 MovePositionTo(SelectionPosition(startCurrent
));
3107 void Editor::LineReverse() {
3108 const Sci::Line lineStart
=
3109 pdoc
->SciLineFromPosition(sel
.RangeMain().Start().Position());
3110 const Sci::Line lineEnd
=
3111 pdoc
->SciLineFromPosition(sel
.RangeMain().End().Position()-1);
3112 const Sci::Line lineDiff
= lineEnd
- lineStart
;
3116 for (Sci::Line i
=(lineDiff
+1)/2-1; i
>=0; --i
) {
3117 const Sci::Line lineNum2
= lineEnd
- i
;
3118 const Sci::Line lineNum1
= lineStart
+ i
;
3119 Sci::Position lineStart2
= pdoc
->LineStart(lineNum2
);
3120 const Sci::Position lineStart1
= pdoc
->LineStart(lineNum1
);
3121 const std::string line2
= RangeText(lineStart2
, pdoc
->LineEnd(lineNum2
));
3122 const std::string line1
= RangeText(lineStart1
, pdoc
->LineEnd(lineNum1
));
3123 const Sci::Position lineLen2
= line2
.length();
3124 const Sci::Position lineLen1
= line1
.length();
3125 pdoc
->DeleteChars(lineStart2
, lineLen2
);
3126 pdoc
->DeleteChars(lineStart1
, lineLen1
);
3127 lineStart2
-= lineLen1
;
3128 pdoc
->InsertString(lineStart2
, line1
);
3129 pdoc
->InsertString(lineStart1
, line2
);
3131 // Wholly select all affected lines
3132 sel
.RangeMain() = SelectionRange(pdoc
->LineStart(lineStart
),
3133 pdoc
->LineStart(lineEnd
+1));
3136 void Editor::Duplicate(bool forLine
) {
3141 std::string_view eol
;
3143 eol
= pdoc
->EOLString();
3145 for (size_t r
=0; r
<sel
.Count(); r
++) {
3146 SelectionPosition start
= sel
.Range(r
).Start();
3147 SelectionPosition end
= sel
.Range(r
).End();
3149 const Sci::Line line
= pdoc
->SciLineFromPosition(sel
.Range(r
).caret
.Position());
3150 start
= SelectionPosition(pdoc
->LineStart(line
));
3151 end
= SelectionPosition(pdoc
->LineEnd(line
));
3153 std::string text
= RangeText(start
.Position(), end
.Position());
3154 Sci::Position lengthInserted
= 0;
3156 lengthInserted
= pdoc
->InsertString(end
.Position(), eol
);
3157 pdoc
->InsertString(end
.Position() + lengthInserted
, text
);
3159 if (sel
.Count() && sel
.IsRectangular()) {
3160 SelectionPosition last
= sel
.Last();
3162 const Sci::Line line
= pdoc
->SciLineFromPosition(last
.Position());
3163 last
= SelectionPosition(last
.Position() +
3164 pdoc
->LineStart(line
+1) - pdoc
->LineStart(line
));
3166 if (sel
.Rectangular().anchor
> sel
.Rectangular().caret
)
3167 sel
.Rectangular().anchor
= last
;
3169 sel
.Rectangular().caret
= last
;
3170 SetRectangularRange();
3174 void Editor::CancelModes() {
3175 sel
.SetMoveExtends(false);
3178 void Editor::NewLine() {
3179 InvalidateWholeSelection();
3180 if (sel
.IsRectangular() || !additionalSelectionTyping
) {
3181 // Remove non-main ranges
3182 sel
.DropAdditionalRanges();
3185 UndoGroup
ug(pdoc
, !sel
.Empty() || (sel
.Count() > 1));
3192 // Insert each line end
3193 size_t countInsertions
= 0;
3194 const std::string_view eol
= pdoc
->EOLString();
3195 for (size_t r
= 0; r
< sel
.Count(); r
++) {
3196 sel
.Range(r
).ClearVirtualSpace();
3197 const Sci::Position positionInsert
= sel
.Range(r
).caret
.Position();
3198 const Sci::Position insertLength
= pdoc
->InsertString(positionInsert
, eol
);
3199 if (insertLength
> 0) {
3200 sel
.Range(r
) = SelectionRange(positionInsert
+ insertLength
);
3205 // Perform notifications after all the changes as the application may change the
3206 // selections in response to the characters.
3207 for (size_t i
= 0; i
< countInsertions
; i
++) {
3208 for (const char ch
: eol
) {
3209 NotifyChar(ch
, CharacterSource::DirectInput
);
3210 if (recordingMacro
) {
3211 const char txt
[2] = { ch
, '\0' };
3212 NotifyMacroRecord(Message::ReplaceSel
, 0, reinterpret_cast<sptr_t
>(txt
));
3219 EnsureCaretVisible();
3220 // Avoid blinking during rapid typing:
3221 ShowCaretAtCurrentPosition();
3224 SelectionPosition
Editor::PositionUpOrDown(SelectionPosition spStart
, int direction
, int lastX
) {
3225 const Point pt
= LocationFromPosition(spStart
);
3228 if (vs
.annotationVisible
!= AnnotationVisible::Hidden
) {
3229 const Sci::Line lineDoc
= pdoc
->SciLineFromPosition(spStart
.Position());
3230 const Point ptStartLine
= LocationFromPosition(pdoc
->LineStart(lineDoc
));
3231 const int subLine
= static_cast<int>(pt
.y
- ptStartLine
.y
) / vs
.lineHeight
;
3233 if (direction
< 0 && subLine
== 0) {
3234 const Sci::Line lineDisplay
= pcs
->DisplayFromDoc(lineDoc
);
3235 if (lineDisplay
> 0) {
3236 skipLines
= pdoc
->AnnotationLines(pcs
->DocFromDisplay(lineDisplay
- 1));
3238 } else if (direction
> 0 && subLine
>= (pcs
->GetHeight(lineDoc
) - 1 - pdoc
->AnnotationLines(lineDoc
))) {
3239 skipLines
= pdoc
->AnnotationLines(lineDoc
);
3243 const Sci::Line newY
= static_cast<Sci::Line
>(pt
.y
) + (1 + skipLines
) * direction
* vs
.lineHeight
;
3245 lastX
= static_cast<int>(pt
.x
) + xOffset
;
3247 SelectionPosition posNew
= SPositionFromLocation(
3248 Point::FromInts(lastX
- xOffset
, static_cast<int>(newY
)), false, false, UserVirtualSpace());
3250 if (direction
< 0) {
3251 // Line wrapping may lead to a location on the same line, so
3252 // seek back if that is the case.
3253 Point ptNew
= LocationFromPosition(posNew
.Position());
3254 while ((posNew
.Position() > 0) && (pt
.y
== ptNew
.y
)) {
3256 posNew
.SetVirtualSpace(0);
3257 ptNew
= LocationFromPosition(posNew
.Position());
3259 } else if (direction
> 0 && posNew
.Position() != pdoc
->Length()) {
3260 // There is an equivalent case when moving down which skips
3262 Point ptNew
= LocationFromPosition(posNew
.Position());
3263 while ((posNew
.Position() > spStart
.Position()) && (ptNew
.y
> newY
)) {
3265 posNew
.SetVirtualSpace(0);
3266 ptNew
= LocationFromPosition(posNew
.Position());
3272 void Editor::CursorUpOrDown(int direction
, Selection::SelTypes selt
) {
3273 if ((selt
== Selection::SelTypes::none
) && sel
.MoveExtends()) {
3274 selt
= !sel
.IsRectangular() ? Selection::SelTypes::stream
: Selection::SelTypes::rectangle
;
3276 SelectionPosition caretToUse
= sel
.RangeMain().caret
;
3277 if (sel
.IsRectangular()) {
3278 if (selt
== Selection::SelTypes::none
) {
3279 caretToUse
= (direction
> 0) ? sel
.Limits().end
: sel
.Limits().start
;
3281 caretToUse
= sel
.Rectangular().caret
;
3284 if (selt
== Selection::SelTypes::rectangle
) {
3285 const SelectionRange rangeBase
= sel
.IsRectangular() ? sel
.Rectangular() : sel
.RangeMain();
3286 if (!sel
.IsRectangular()) {
3287 InvalidateWholeSelection();
3288 sel
.DropAdditionalRanges();
3290 const SelectionPosition posNew
= MovePositionSoVisible(
3291 PositionUpOrDown(caretToUse
, direction
, lastXChosen
), direction
);
3292 sel
.selType
= Selection::SelTypes::rectangle
;
3293 sel
.Rectangular() = SelectionRange(posNew
, rangeBase
.anchor
);
3294 SetRectangularRange();
3295 MovedCaret(posNew
, caretToUse
, true, caretPolicies
);
3296 } else if (sel
.selType
== Selection::SelTypes::lines
&& sel
.MoveExtends()) {
3297 // Calculate new caret position and call SetSelection(), which will ensure whole lines are selected.
3298 const SelectionPosition posNew
= MovePositionSoVisible(
3299 PositionUpOrDown(caretToUse
, direction
, -1), direction
);
3300 SetSelection(posNew
, sel
.RangeMain().anchor
);
3302 InvalidateWholeSelection();
3303 if (!additionalSelectionTyping
|| (sel
.IsRectangular())) {
3304 sel
.DropAdditionalRanges();
3306 sel
.selType
= Selection::SelTypes::stream
;
3307 for (size_t r
= 0; r
< sel
.Count(); r
++) {
3308 const int lastX
= (r
== sel
.Main()) ? lastXChosen
: -1;
3309 const SelectionPosition spCaretNow
= sel
.Range(r
).caret
;
3310 const SelectionPosition posNew
= MovePositionSoVisible(
3311 PositionUpOrDown(spCaretNow
, direction
, lastX
), direction
);
3312 sel
.Range(r
) = selt
== Selection::SelTypes::stream
?
3313 SelectionRange(posNew
, sel
.Range(r
).anchor
) : SelectionRange(posNew
);
3315 sel
.RemoveDuplicates();
3316 MovedCaret(sel
.RangeMain().caret
, caretToUse
, true, caretPolicies
);
3320 void Editor::ParaUpOrDown(int direction
, Selection::SelTypes selt
) {
3322 const Sci::Position savedPos
= sel
.MainCaret();
3324 MovePositionTo(SelectionPosition(direction
> 0 ? pdoc
->ParaDown(sel
.MainCaret()) : pdoc
->ParaUp(sel
.MainCaret())), selt
);
3325 lineDoc
= pdoc
->SciLineFromPosition(sel
.MainCaret());
3326 if (direction
> 0) {
3327 if (sel
.MainCaret() >= pdoc
->Length() && !pcs
->GetVisible(lineDoc
)) {
3328 if (selt
== Selection::SelTypes::none
) {
3329 MovePositionTo(SelectionPosition(pdoc
->LineEndPosition(savedPos
)));
3334 } while (!pcs
->GetVisible(lineDoc
));
3337 Range
Editor::RangeDisplayLine(Sci::Line lineVisible
) {
3339 AutoSurface
surface(this);
3340 return view
.RangeDisplayLine(surface
, *this, lineVisible
, vs
);
3343 Sci::Position
Editor::StartEndDisplayLine(Sci::Position pos
, bool start
) {
3345 AutoSurface
surface(this);
3346 const Sci::Position posRet
= view
.StartEndDisplayLine(surface
, *this, pos
, start
, vs
);
3347 if (posRet
== Sci::invalidPosition
) {
3356 constexpr short HighShortFromWParam(uptr_t x
) {
3357 return static_cast<short>(x
>> 16);
3360 constexpr short LowShortFromWParam(uptr_t x
) {
3361 return static_cast<short>(x
& 0xffff);
3364 constexpr Message
WithExtends(Message iMessage
) noexcept
{
3366 case Message::CharLeft
: return Message::CharLeftExtend
;
3367 case Message::CharRight
: return Message::CharRightExtend
;
3369 case Message::WordLeft
: return Message::WordLeftExtend
;
3370 case Message::WordRight
: return Message::WordRightExtend
;
3371 case Message::WordLeftEnd
: return Message::WordLeftEndExtend
;
3372 case Message::WordRightEnd
: return Message::WordRightEndExtend
;
3373 case Message::WordPartLeft
: return Message::WordPartLeftExtend
;
3374 case Message::WordPartRight
: return Message::WordPartRightExtend
;
3376 case Message::Home
: return Message::HomeExtend
;
3377 case Message::HomeDisplay
: return Message::HomeDisplayExtend
;
3378 case Message::HomeWrap
: return Message::HomeWrapExtend
;
3379 case Message::VCHome
: return Message::VCHomeExtend
;
3380 case Message::VCHomeDisplay
: return Message::VCHomeDisplayExtend
;
3381 case Message::VCHomeWrap
: return Message::VCHomeWrapExtend
;
3383 case Message::LineEnd
: return Message::LineEndExtend
;
3384 case Message::LineEndDisplay
: return Message::LineEndDisplayExtend
;
3385 case Message::LineEndWrap
: return Message::LineEndWrapExtend
;
3387 default: return iMessage
;
3391 constexpr int NaturalDirection(Message iMessage
) noexcept
{
3393 case Message::CharLeft
:
3394 case Message::CharLeftExtend
:
3395 case Message::CharLeftRectExtend
:
3396 case Message::WordLeft
:
3397 case Message::WordLeftExtend
:
3398 case Message::WordLeftEnd
:
3399 case Message::WordLeftEndExtend
:
3400 case Message::WordPartLeft
:
3401 case Message::WordPartLeftExtend
:
3403 case Message::HomeExtend
:
3404 case Message::HomeDisplay
:
3405 case Message::HomeDisplayExtend
:
3406 case Message::HomeWrap
:
3407 case Message::HomeWrapExtend
:
3408 // VC_HOME* mostly goes back
3409 case Message::VCHome
:
3410 case Message::VCHomeExtend
:
3411 case Message::VCHomeDisplay
:
3412 case Message::VCHomeDisplayExtend
:
3413 case Message::VCHomeWrap
:
3414 case Message::VCHomeWrapExtend
:
3422 constexpr bool IsRectExtend(Message iMessage
, bool isRectMoveExtends
) noexcept
{
3424 case Message::CharLeftRectExtend
:
3425 case Message::CharRightRectExtend
:
3426 case Message::HomeRectExtend
:
3427 case Message::VCHomeRectExtend
:
3428 case Message::LineEndRectExtend
:
3431 if (isRectMoveExtends
) {
3432 // Handle Message::SetSelectionMode(SelectionMode::Rectangle) and subsequent movements.
3434 case Message::CharLeftExtend
:
3435 case Message::CharRightExtend
:
3436 case Message::HomeExtend
:
3437 case Message::VCHomeExtend
:
3438 case Message::LineEndExtend
:
3450 Sci::Position
Editor::VCHomeDisplayPosition(Sci::Position position
) {
3451 const Sci::Position homePos
= pdoc
->VCHomePosition(position
);
3452 const Sci::Position viewLineStart
= StartEndDisplayLine(position
, true);
3453 if (viewLineStart
> homePos
)
3454 return viewLineStart
;
3459 Sci::Position
Editor::VCHomeWrapPosition(Sci::Position position
) {
3460 const Sci::Position homePos
= pdoc
->VCHomePosition(position
);
3461 const Sci::Position viewLineStart
= StartEndDisplayLine(position
, true);
3462 if ((viewLineStart
< position
) && (viewLineStart
> homePos
))
3463 return viewLineStart
;
3468 Sci::Position
Editor::LineEndWrapPosition(Sci::Position position
) {
3469 const Sci::Position endPos
= StartEndDisplayLine(position
, false);
3470 const Sci::Position realEndPos
= pdoc
->LineEndPosition(position
);
3471 if (endPos
> realEndPos
// if moved past visible EOLs
3472 || position
>= endPos
) // if at end of display line already
3478 int Editor::HorizontalMove(Message iMessage
) {
3479 if (sel
.selType
== Selection::SelTypes::lines
) {
3480 return 0; // horizontal moves with line selection have no effect
3482 if (sel
.MoveExtends()) {
3483 iMessage
= WithExtends(iMessage
);
3486 if (!multipleSelection
&& !sel
.IsRectangular()) {
3487 // Simplify selection down to 1
3488 sel
.SetSelection(sel
.RangeMain());
3491 // Invalidate each of the current selections
3492 InvalidateWholeSelection();
3494 if (IsRectExtend(iMessage
, sel
.IsRectangular() && sel
.MoveExtends())) {
3495 const SelectionRange rangeBase
= sel
.IsRectangular() ? sel
.Rectangular() : sel
.RangeMain();
3496 if (!sel
.IsRectangular()) {
3497 sel
.DropAdditionalRanges();
3499 // Will change to rectangular if not currently rectangular
3500 SelectionPosition spCaret
= rangeBase
.caret
;
3502 case Message::CharLeftRectExtend
:
3503 case Message::CharLeftExtend
: // only when sel.IsRectangular() && sel.MoveExtends()
3504 if (pdoc
->IsLineEndPosition(spCaret
.Position()) && spCaret
.VirtualSpace()) {
3505 spCaret
.SetVirtualSpace(spCaret
.VirtualSpace() - 1);
3506 } else if (!FlagSet(virtualSpaceOptions
, VirtualSpace::NoWrapLineStart
) || pdoc
->GetColumn(spCaret
.Position()) > 0) {
3507 spCaret
= SelectionPosition(spCaret
.Position() - 1);
3510 case Message::CharRightRectExtend
:
3511 case Message::CharRightExtend
: // only when sel.IsRectangular() && sel.MoveExtends()
3512 if (FlagSet(virtualSpaceOptions
, VirtualSpace::RectangularSelection
) && pdoc
->IsLineEndPosition(sel
.MainCaret())) {
3513 spCaret
.SetVirtualSpace(spCaret
.VirtualSpace() + 1);
3515 spCaret
= SelectionPosition(spCaret
.Position() + 1);
3518 case Message::HomeRectExtend
:
3519 case Message::HomeExtend
: // only when sel.IsRectangular() && sel.MoveExtends()
3520 spCaret
= SelectionPosition(
3521 pdoc
->LineStart(pdoc
->LineFromPosition(spCaret
.Position())));
3523 case Message::VCHomeRectExtend
:
3524 case Message::VCHomeExtend
: // only when sel.IsRectangular() && sel.MoveExtends()
3525 spCaret
= SelectionPosition(pdoc
->VCHomePosition(spCaret
.Position()));
3527 case Message::LineEndRectExtend
:
3528 case Message::LineEndExtend
: // only when sel.IsRectangular() && sel.MoveExtends()
3529 spCaret
= SelectionPosition(pdoc
->LineEndPosition(spCaret
.Position()));
3534 const int directionMove
= (spCaret
< rangeBase
.caret
) ? -1 : 1;
3535 spCaret
= MovePositionSoVisible(spCaret
, directionMove
);
3536 sel
.selType
= Selection::SelTypes::rectangle
;
3537 sel
.Rectangular() = SelectionRange(spCaret
, rangeBase
.anchor
);
3538 SetRectangularRange();
3539 } else if (sel
.IsRectangular()) {
3540 // Not a rectangular extension so switch to stream.
3541 SelectionPosition selAtLimit
= (NaturalDirection(iMessage
) > 0) ? sel
.Limits().end
: sel
.Limits().start
;
3544 selAtLimit
= SelectionPosition(
3545 pdoc
->LineStart(pdoc
->LineFromPosition(selAtLimit
.Position())));
3547 case Message::VCHome
:
3548 selAtLimit
= SelectionPosition(pdoc
->VCHomePosition(selAtLimit
.Position()));
3550 case Message::LineEnd
:
3551 selAtLimit
= SelectionPosition(pdoc
->LineEndPosition(selAtLimit
.Position()));
3556 sel
.selType
= Selection::SelTypes::stream
;
3557 sel
.SetSelection(SelectionRange(selAtLimit
));
3559 if (!additionalSelectionTyping
) {
3560 InvalidateWholeSelection();
3561 sel
.DropAdditionalRanges();
3563 for (size_t r
= 0; r
< sel
.Count(); r
++) {
3564 const SelectionPosition spCaretNow
= sel
.Range(r
).caret
;
3565 SelectionPosition spCaret
= spCaretNow
;
3567 case Message::CharLeft
:
3568 case Message::CharLeftExtend
:
3569 if (spCaret
.VirtualSpace()) {
3570 spCaret
.SetVirtualSpace(spCaret
.VirtualSpace() - 1);
3571 } else if (!FlagSet(virtualSpaceOptions
, VirtualSpace::NoWrapLineStart
) || pdoc
->GetColumn(spCaret
.Position()) > 0) {
3572 spCaret
= SelectionPosition(spCaret
.Position() - 1);
3575 case Message::CharRight
:
3576 case Message::CharRightExtend
:
3577 if (FlagSet(virtualSpaceOptions
, VirtualSpace::UserAccessible
) && pdoc
->IsLineEndPosition(spCaret
.Position())) {
3578 spCaret
.SetVirtualSpace(spCaret
.VirtualSpace() + 1);
3580 spCaret
= SelectionPosition(spCaret
.Position() + 1);
3583 case Message::WordLeft
:
3584 case Message::WordLeftExtend
:
3585 spCaret
= SelectionPosition(pdoc
->NextWordStart(spCaret
.Position(), -1));
3587 case Message::WordRight
:
3588 case Message::WordRightExtend
:
3589 spCaret
= SelectionPosition(pdoc
->NextWordStart(spCaret
.Position(), 1));
3591 case Message::WordLeftEnd
:
3592 case Message::WordLeftEndExtend
:
3593 spCaret
= SelectionPosition(pdoc
->NextWordEnd(spCaret
.Position(), -1));
3595 case Message::WordRightEnd
:
3596 case Message::WordRightEndExtend
:
3597 spCaret
= SelectionPosition(pdoc
->NextWordEnd(spCaret
.Position(), 1));
3599 case Message::WordPartLeft
:
3600 case Message::WordPartLeftExtend
:
3601 spCaret
= SelectionPosition(pdoc
->WordPartLeft(spCaret
.Position()));
3603 case Message::WordPartRight
:
3604 case Message::WordPartRightExtend
:
3605 spCaret
= SelectionPosition(pdoc
->WordPartRight(spCaret
.Position()));
3608 case Message::HomeExtend
:
3609 spCaret
= SelectionPosition(
3610 pdoc
->LineStart(pdoc
->LineFromPosition(spCaret
.Position())));
3612 case Message::HomeDisplay
:
3613 case Message::HomeDisplayExtend
:
3614 spCaret
= SelectionPosition(StartEndDisplayLine(spCaret
.Position(), true));
3616 case Message::HomeWrap
:
3617 case Message::HomeWrapExtend
:
3618 spCaret
= MovePositionSoVisible(StartEndDisplayLine(spCaret
.Position(), true), -1);
3619 if (spCaretNow
<= spCaret
)
3620 spCaret
= SelectionPosition(
3621 pdoc
->LineStart(pdoc
->LineFromPosition(spCaret
.Position())));
3623 case Message::VCHome
:
3624 case Message::VCHomeExtend
:
3625 // VCHome alternates between beginning of line and beginning of text so may move back or forwards
3626 spCaret
= SelectionPosition(pdoc
->VCHomePosition(spCaret
.Position()));
3628 case Message::VCHomeDisplay
:
3629 case Message::VCHomeDisplayExtend
:
3630 spCaret
= SelectionPosition(VCHomeDisplayPosition(spCaret
.Position()));
3632 case Message::VCHomeWrap
:
3633 case Message::VCHomeWrapExtend
:
3634 spCaret
= SelectionPosition(VCHomeWrapPosition(spCaret
.Position()));
3636 case Message::LineEnd
:
3637 case Message::LineEndExtend
:
3638 spCaret
= SelectionPosition(pdoc
->LineEndPosition(spCaret
.Position()));
3640 case Message::LineEndDisplay
:
3641 case Message::LineEndDisplayExtend
:
3642 spCaret
= SelectionPosition(StartEndDisplayLine(spCaret
.Position(), false));
3644 case Message::LineEndWrap
:
3645 case Message::LineEndWrapExtend
:
3646 spCaret
= SelectionPosition(LineEndWrapPosition(spCaret
.Position()));
3650 PLATFORM_ASSERT(false);
3653 const int directionMove
= (spCaret
< spCaretNow
) ? -1 : 1;
3654 spCaret
= MovePositionSoVisible(spCaret
, directionMove
);
3656 // Handle move versus extend, and special behaviour for non-empty left/right
3658 case Message::CharLeft
:
3659 case Message::CharRight
:
3660 if (sel
.Range(r
).Empty()) {
3661 sel
.Range(r
) = SelectionRange(spCaret
);
3663 sel
.Range(r
) = SelectionRange(
3664 (iMessage
== Message::CharLeft
) ? sel
.Range(r
).Start() : sel
.Range(r
).End());
3668 case Message::WordLeft
:
3669 case Message::WordRight
:
3670 case Message::WordLeftEnd
:
3671 case Message::WordRightEnd
:
3672 case Message::WordPartLeft
:
3673 case Message::WordPartRight
:
3675 case Message::HomeDisplay
:
3676 case Message::HomeWrap
:
3677 case Message::VCHome
:
3678 case Message::VCHomeDisplay
:
3679 case Message::VCHomeWrap
:
3680 case Message::LineEnd
:
3681 case Message::LineEndDisplay
:
3682 case Message::LineEndWrap
:
3683 sel
.Range(r
) = SelectionRange(spCaret
);
3686 case Message::CharLeftExtend
:
3687 case Message::CharRightExtend
:
3688 case Message::WordLeftExtend
:
3689 case Message::WordRightExtend
:
3690 case Message::WordLeftEndExtend
:
3691 case Message::WordRightEndExtend
:
3692 case Message::WordPartLeftExtend
:
3693 case Message::WordPartRightExtend
:
3694 case Message::HomeExtend
:
3695 case Message::HomeDisplayExtend
:
3696 case Message::HomeWrapExtend
:
3697 case Message::VCHomeExtend
:
3698 case Message::VCHomeDisplayExtend
:
3699 case Message::VCHomeWrapExtend
:
3700 case Message::LineEndExtend
:
3701 case Message::LineEndDisplayExtend
:
3702 case Message::LineEndWrapExtend
: {
3703 SelectionRange rangeNew
= SelectionRange(spCaret
, sel
.Range(r
).anchor
);
3704 sel
.TrimOtherSelections(r
, SelectionRange(rangeNew
));
3705 sel
.Range(r
) = rangeNew
;
3710 PLATFORM_ASSERT(false);
3715 sel
.RemoveDuplicates();
3717 MovedCaret(sel
.RangeMain().caret
, SelectionPosition(Sci::invalidPosition
), true, caretPolicies
);
3719 // Invalidate the new state of the selection
3720 InvalidateWholeSelection();
3723 // Need the line moving and so forth from MovePositionTo
3727 int Editor::DelWordOrLine(Message iMessage
) {
3728 // Virtual space may be realised for Message::DelWordRight or Message::DelWordRightEnd
3729 // which means 2 actions so wrap in an undo group.
3731 // Rightwards and leftwards deletions differ in treatment of virtual space.
3732 // Clear virtual space for leftwards, realise for rightwards.
3733 const bool leftwards
= (iMessage
== Message::DelWordLeft
) || (iMessage
== Message::DelLineLeft
);
3735 if (!additionalSelectionTyping
) {
3736 InvalidateWholeSelection();
3737 sel
.DropAdditionalRanges();
3740 UndoGroup
ug0(pdoc
, (sel
.Count() > 1) || !leftwards
);
3742 for (size_t r
= 0; r
< sel
.Count(); r
++) {
3744 // Delete to the left so first clear the virtual space.
3745 sel
.Range(r
).ClearVirtualSpace();
3747 // Delete to the right so first realise the virtual space.
3748 sel
.Range(r
) = SelectionRange(
3749 RealizeVirtualSpace(sel
.Range(r
).caret
));
3754 case Message::DelWordLeft
:
3755 rangeDelete
= Range(
3756 pdoc
->NextWordStart(sel
.Range(r
).caret
.Position(), -1),
3757 sel
.Range(r
).caret
.Position());
3759 case Message::DelWordRight
:
3760 rangeDelete
= Range(
3761 sel
.Range(r
).caret
.Position(),
3762 pdoc
->NextWordStart(sel
.Range(r
).caret
.Position(), 1));
3764 case Message::DelWordRightEnd
:
3765 rangeDelete
= Range(
3766 sel
.Range(r
).caret
.Position(),
3767 pdoc
->NextWordEnd(sel
.Range(r
).caret
.Position(), 1));
3769 case Message::DelLineLeft
:
3770 rangeDelete
= Range(
3771 pdoc
->LineStart(pdoc
->LineFromPosition(sel
.Range(r
).caret
.Position())),
3772 sel
.Range(r
).caret
.Position());
3774 case Message::DelLineRight
:
3775 rangeDelete
= Range(
3776 sel
.Range(r
).caret
.Position(),
3777 pdoc
->LineEnd(pdoc
->LineFromPosition(sel
.Range(r
).caret
.Position())));
3782 if (!RangeContainsProtected(rangeDelete
.start
, rangeDelete
.end
)) {
3783 pdoc
->DeleteChars(rangeDelete
.start
, rangeDelete
.end
- rangeDelete
.start
);
3787 // May need something stronger here: can selections overlap at this point?
3788 sel
.RemoveDuplicates();
3790 MovedCaret(sel
.RangeMain().caret
, SelectionPosition(Sci::invalidPosition
), true, caretPolicies
);
3792 // Invalidate the new state of the selection
3793 InvalidateWholeSelection();
3799 int Editor::KeyCommand(Message iMessage
) {
3801 case Message::LineDown
:
3802 CursorUpOrDown(1, Selection::SelTypes::none
);
3804 case Message::LineDownExtend
:
3805 CursorUpOrDown(1, Selection::SelTypes::stream
);
3807 case Message::LineDownRectExtend
:
3808 CursorUpOrDown(1, Selection::SelTypes::rectangle
);
3810 case Message::ParaDown
:
3811 ParaUpOrDown(1, Selection::SelTypes::none
);
3813 case Message::ParaDownExtend
:
3814 ParaUpOrDown(1, Selection::SelTypes::stream
);
3816 case Message::LineScrollDown
:
3817 ScrollTo(topLine
+ 1);
3818 MoveCaretInsideView(false);
3820 case Message::LineUp
:
3821 CursorUpOrDown(-1, Selection::SelTypes::none
);
3823 case Message::LineUpExtend
:
3824 CursorUpOrDown(-1, Selection::SelTypes::stream
);
3826 case Message::LineUpRectExtend
:
3827 CursorUpOrDown(-1, Selection::SelTypes::rectangle
);
3829 case Message::ParaUp
:
3830 ParaUpOrDown(-1, Selection::SelTypes::none
);
3832 case Message::ParaUpExtend
:
3833 ParaUpOrDown(-1, Selection::SelTypes::stream
);
3835 case Message::LineScrollUp
:
3836 ScrollTo(topLine
- 1);
3837 MoveCaretInsideView(false);
3840 case Message::CharLeft
:
3841 case Message::CharLeftExtend
:
3842 case Message::CharLeftRectExtend
:
3843 case Message::CharRight
:
3844 case Message::CharRightExtend
:
3845 case Message::CharRightRectExtend
:
3846 case Message::WordLeft
:
3847 case Message::WordLeftExtend
:
3848 case Message::WordRight
:
3849 case Message::WordRightExtend
:
3850 case Message::WordLeftEnd
:
3851 case Message::WordLeftEndExtend
:
3852 case Message::WordRightEnd
:
3853 case Message::WordRightEndExtend
:
3854 case Message::WordPartLeft
:
3855 case Message::WordPartLeftExtend
:
3856 case Message::WordPartRight
:
3857 case Message::WordPartRightExtend
:
3859 case Message::HomeExtend
:
3860 case Message::HomeRectExtend
:
3861 case Message::HomeDisplay
:
3862 case Message::HomeDisplayExtend
:
3863 case Message::HomeWrap
:
3864 case Message::HomeWrapExtend
:
3865 case Message::VCHome
:
3866 case Message::VCHomeExtend
:
3867 case Message::VCHomeRectExtend
:
3868 case Message::VCHomeDisplay
:
3869 case Message::VCHomeDisplayExtend
:
3870 case Message::VCHomeWrap
:
3871 case Message::VCHomeWrapExtend
:
3872 case Message::LineEnd
:
3873 case Message::LineEndExtend
:
3874 case Message::LineEndRectExtend
:
3875 case Message::LineEndDisplay
:
3876 case Message::LineEndDisplayExtend
:
3877 case Message::LineEndWrap
:
3878 case Message::LineEndWrapExtend
:
3879 return HorizontalMove(iMessage
);
3881 case Message::DocumentStart
:
3885 case Message::DocumentStartExtend
:
3886 MovePositionTo(0, Selection::SelTypes::stream
);
3889 case Message::DocumentEnd
:
3890 MovePositionTo(pdoc
->Length());
3893 case Message::DocumentEndExtend
:
3894 MovePositionTo(pdoc
->Length(), Selection::SelTypes::stream
);
3897 case Message::StutteredPageUp
:
3898 PageMove(-1, Selection::SelTypes::none
, true);
3900 case Message::StutteredPageUpExtend
:
3901 PageMove(-1, Selection::SelTypes::stream
, true);
3903 case Message::StutteredPageDown
:
3904 PageMove(1, Selection::SelTypes::none
, true);
3906 case Message::StutteredPageDownExtend
:
3907 PageMove(1, Selection::SelTypes::stream
, true);
3909 case Message::PageUp
:
3912 case Message::PageUpExtend
:
3913 PageMove(-1, Selection::SelTypes::stream
);
3915 case Message::PageUpRectExtend
:
3916 PageMove(-1, Selection::SelTypes::rectangle
);
3918 case Message::PageDown
:
3921 case Message::PageDownExtend
:
3922 PageMove(1, Selection::SelTypes::stream
);
3924 case Message::PageDownRectExtend
:
3925 PageMove(1, Selection::SelTypes::rectangle
);
3927 case Message::EditToggleOvertype
:
3928 inOverstrike
= !inOverstrike
;
3929 ContainerNeedsUpdate(Update::Selection
);
3930 ShowCaretAtCurrentPosition();
3933 case Message::Cancel
: // Cancel any modes - handled in subclass
3934 // Also unselect text
3936 if ((sel
.Count() > 1) && !sel
.IsRectangular()) {
3937 // Drop additional selections
3938 InvalidateWholeSelection();
3939 sel
.DropAdditionalRanges();
3942 case Message::DeleteBack
:
3944 if ((caretSticky
== CaretSticky::Off
) || (caretSticky
== CaretSticky::WhiteSpace
)) {
3947 EnsureCaretVisible();
3949 case Message::DeleteBackNotLine
:
3951 if ((caretSticky
== CaretSticky::Off
) || (caretSticky
== CaretSticky::WhiteSpace
)) {
3954 EnsureCaretVisible();
3958 if (caretSticky
== CaretSticky::Off
) {
3961 EnsureCaretVisible();
3962 ShowCaretAtCurrentPosition(); // Avoid blinking
3964 case Message::BackTab
:
3966 if ((caretSticky
== CaretSticky::Off
) || (caretSticky
== CaretSticky::WhiteSpace
)) {
3969 EnsureCaretVisible();
3970 ShowCaretAtCurrentPosition(); // Avoid blinking
3972 case Message::NewLine
:
3975 case Message::FormFeed
:
3978 case Message::ZoomIn
:
3979 if (vs
.zoomLevel
< 20) {
3981 InvalidateStyleRedraw();
3985 case Message::ZoomOut
:
3986 if (vs
.zoomLevel
> -10) {
3988 InvalidateStyleRedraw();
3993 case Message::DelWordLeft
:
3994 case Message::DelWordRight
:
3995 case Message::DelWordRightEnd
:
3996 case Message::DelLineLeft
:
3997 case Message::DelLineRight
:
3998 return DelWordOrLine(iMessage
);
4000 case Message::LineCopy
: {
4001 const Sci::Line lineStart
= pdoc
->SciLineFromPosition(SelectionStart().Position());
4002 const Sci::Line lineEnd
= pdoc
->SciLineFromPosition(SelectionEnd().Position());
4003 CopyRangeToClipboard(pdoc
->LineStart(lineStart
),
4004 pdoc
->LineStart(lineEnd
+ 1));
4007 case Message::LineCut
: {
4008 const Sci::Line lineStart
= pdoc
->SciLineFromPosition(SelectionStart().Position());
4009 const Sci::Line lineEnd
= pdoc
->SciLineFromPosition(SelectionEnd().Position());
4010 const Sci::Position start
= pdoc
->LineStart(lineStart
);
4011 const Sci::Position end
= pdoc
->LineStart(lineEnd
+ 1);
4012 SetSelection(start
, end
);
4017 case Message::LineDelete
: {
4018 const Sci::Line line
= pdoc
->SciLineFromPosition(sel
.MainCaret());
4019 const Sci::Position start
= pdoc
->LineStart(line
);
4020 const Sci::Position end
= pdoc
->LineStart(line
+ 1);
4021 pdoc
->DeleteChars(start
, end
- start
);
4024 case Message::LineTranspose
:
4027 case Message::LineReverse
:
4030 case Message::LineDuplicate
:
4033 case Message::SelectionDuplicate
:
4036 case Message::LowerCase
:
4037 ChangeCaseOfSelection(CaseMapping::lower
);
4039 case Message::UpperCase
:
4040 ChangeCaseOfSelection(CaseMapping::upper
);
4042 case Message::ScrollToStart
:
4045 case Message::ScrollToEnd
:
4046 ScrollTo(MaxScrollPos());
4054 int Editor::KeyDefault(Keys
, KeyMod
) {
4058 int Editor::KeyDownWithModifiers(Keys key
, KeyMod modifiers
, bool *consumed
) {
4060 const Message msg
= kmap
.Find(key
, modifiers
);
4061 if (msg
!= static_cast<Message
>(0)) {
4064 return static_cast<int>(WndProc(msg
, 0, 0));
4068 return KeyDefault(key
, modifiers
);
4072 void Editor::Indent(bool forwards
) {
4074 for (size_t r
=0; r
<sel
.Count(); r
++) {
4075 const Sci::Line lineOfAnchor
=
4076 pdoc
->SciLineFromPosition(sel
.Range(r
).anchor
.Position());
4077 Sci::Position caretPosition
= sel
.Range(r
).caret
.Position();
4078 const Sci::Line lineCurrentPos
= pdoc
->SciLineFromPosition(caretPosition
);
4079 if (lineOfAnchor
== lineCurrentPos
) {
4081 pdoc
->DeleteChars(sel
.Range(r
).Start().Position(), sel
.Range(r
).Length());
4082 caretPosition
= sel
.Range(r
).caret
.Position();
4083 if (pdoc
->GetColumn(caretPosition
) <= pdoc
->GetColumn(pdoc
->GetLineIndentPosition(lineCurrentPos
)) &&
4085 const int indentation
= pdoc
->GetLineIndentation(lineCurrentPos
);
4086 const int indentationStep
= pdoc
->IndentSize();
4087 const Sci::Position posSelect
= pdoc
->SetLineIndentation(
4088 lineCurrentPos
, indentation
+ indentationStep
- indentation
% indentationStep
);
4089 sel
.Range(r
) = SelectionRange(posSelect
);
4091 if (pdoc
->useTabs
) {
4092 const Sci::Position lengthInserted
= pdoc
->InsertString(caretPosition
, "\t", 1);
4093 sel
.Range(r
) = SelectionRange(caretPosition
+ lengthInserted
);
4095 int numSpaces
= (pdoc
->tabInChars
) -
4096 (pdoc
->GetColumn(caretPosition
) % (pdoc
->tabInChars
));
4098 numSpaces
= pdoc
->tabInChars
;
4099 const std::string
spaceText(numSpaces
, ' ');
4100 const Sci::Position lengthInserted
= pdoc
->InsertString(caretPosition
, spaceText
);
4101 sel
.Range(r
) = SelectionRange(caretPosition
+ lengthInserted
);
4105 if (pdoc
->GetColumn(caretPosition
) <= pdoc
->GetLineIndentation(lineCurrentPos
) &&
4107 const int indentation
= pdoc
->GetLineIndentation(lineCurrentPos
);
4108 const int indentationStep
= pdoc
->IndentSize();
4109 const Sci::Position posSelect
= pdoc
->SetLineIndentation(lineCurrentPos
, indentation
- indentationStep
);
4110 sel
.Range(r
) = SelectionRange(posSelect
);
4112 Sci::Position newColumn
= ((pdoc
->GetColumn(caretPosition
) - 1) / pdoc
->tabInChars
) *
4116 Sci::Position newPos
= caretPosition
;
4117 while (pdoc
->GetColumn(newPos
) > newColumn
)
4119 sel
.Range(r
) = SelectionRange(newPos
);
4122 } else { // Multiline
4123 const Sci::Position anchorPosOnLine
= sel
.Range(r
).anchor
.Position() -
4124 pdoc
->LineStart(lineOfAnchor
);
4125 const Sci::Position currentPosPosOnLine
= caretPosition
-
4126 pdoc
->LineStart(lineCurrentPos
);
4127 // Multiple lines selected so indent / dedent
4128 const Sci::Line lineTopSel
= std::min(lineOfAnchor
, lineCurrentPos
);
4129 Sci::Line lineBottomSel
= std::max(lineOfAnchor
, lineCurrentPos
);
4130 if (pdoc
->LineStart(lineBottomSel
) == sel
.Range(r
).anchor
.Position() || pdoc
->LineStart(lineBottomSel
) == caretPosition
)
4131 lineBottomSel
--; // If not selecting any characters on a line, do not indent
4132 pdoc
->Indent(forwards
, lineBottomSel
, lineTopSel
);
4133 if (lineOfAnchor
< lineCurrentPos
) {
4134 if (currentPosPosOnLine
== 0)
4135 sel
.Range(r
) = SelectionRange(pdoc
->LineStart(lineCurrentPos
),
4136 pdoc
->LineStart(lineOfAnchor
));
4138 sel
.Range(r
) = SelectionRange(pdoc
->LineStart(lineCurrentPos
+ 1),
4139 pdoc
->LineStart(lineOfAnchor
));
4141 if (anchorPosOnLine
== 0)
4142 sel
.Range(r
) = SelectionRange(pdoc
->LineStart(lineCurrentPos
),
4143 pdoc
->LineStart(lineOfAnchor
));
4145 sel
.Range(r
) = SelectionRange(pdoc
->LineStart(lineCurrentPos
),
4146 pdoc
->LineStart(lineOfAnchor
+ 1));
4150 ContainerNeedsUpdate(Update::Selection
);
4153 std::unique_ptr
<CaseFolder
> Editor::CaseFolderForEncoding() {
4154 // Simple default that only maps ASCII upper case to lower case.
4155 return std::make_unique
<CaseFolderTable
>();
4159 * Search of a text in the document, in the given range.
4160 * @return The position of the found text, -1 if not found.
4162 Sci::Position
Editor::FindText(
4163 uptr_t wParam
, ///< Search modes : @c FindOption::MatchCase, @c FindOption::WholeWord,
4164 ///< @c FindOption::WordStart, @c FindOption::RegExp or @c FindOption::Posix.
4165 sptr_t lParam
) { ///< @c Sci_TextToFind structure: The text to search for in the given range.
4167 TextToFind
*ft
= static_cast<TextToFind
*>(PtrFromSPtr(lParam
));
4168 Sci::Position lengthFound
= strlen(ft
->lpstrText
);
4169 if (!pdoc
->HasCaseFolder())
4170 pdoc
->SetCaseFolder(CaseFolderForEncoding());
4172 const Sci::Position pos
= pdoc
->FindText(
4173 static_cast<Sci::Position
>(ft
->chrg
.cpMin
),
4174 static_cast<Sci::Position
>(ft
->chrg
.cpMax
),
4176 static_cast<FindOption
>(wParam
),
4179 ft
->chrgText
.cpMin
= static_cast<Sci_PositionCR
>(pos
);
4180 ft
->chrgText
.cpMax
= static_cast<Sci_PositionCR
>(pos
+ lengthFound
);
4183 } catch (RegexError
&) {
4184 errorStatus
= Status::RegEx
;
4190 * Search of a text in the document, in the given range.
4191 * @return The position of the found text, -1 if not found.
4193 Sci::Position
Editor::FindTextFull(
4194 uptr_t wParam
, ///< Search modes : @c FindOption::MatchCase, @c FindOption::WholeWord,
4195 ///< @c FindOption::WordStart, @c FindOption::RegExp or @c FindOption::Posix.
4196 sptr_t lParam
) { ///< @c Sci_TextToFindFull structure: The text to search for in the given range.
4198 TextToFindFull
*ft
= static_cast<TextToFindFull
*>(PtrFromSPtr(lParam
));
4199 Sci::Position lengthFound
= strlen(ft
->lpstrText
);
4200 if (!pdoc
->HasCaseFolder())
4201 pdoc
->SetCaseFolder(CaseFolderForEncoding());
4203 const Sci::Position pos
= pdoc
->FindText(
4207 static_cast<FindOption
>(wParam
),
4210 ft
->chrgText
.cpMin
= pos
;
4211 ft
->chrgText
.cpMax
= pos
+ lengthFound
;
4214 } catch (RegexError
&) {
4215 errorStatus
= Status::RegEx
;
4221 * Relocatable search support : Searches relative to current selection
4222 * point and sets the selection to the found text range with
4226 * Anchor following searches at current selection start: This allows
4227 * multiple incremental interactive searches to be macro recorded
4228 * while still setting the selection to found text so the find/select
4229 * operation is self-contained.
4231 void Editor::SearchAnchor() noexcept
{
4232 searchAnchor
= SelectionStart().Position();
4236 * Find text from current search anchor: Must call @c SearchAnchor first.
4237 * Used for next text and previous text requests.
4238 * @return The position of the found text, -1 if not found.
4240 Sci::Position
Editor::SearchText(
4241 Message iMessage
, ///< Accepts both @c Message::SearchNext and @c Message::SearchPrev.
4242 uptr_t wParam
, ///< Search modes : @c FindOption::MatchCase, @c FindOption::WholeWord,
4243 ///< @c FindOption::WordStart, @c FindOption::RegExp or @c FindOption::Posix.
4244 sptr_t lParam
) { ///< The text to search for.
4246 const char *txt
= ConstCharPtrFromSPtr(lParam
);
4247 Sci::Position pos
= Sci::invalidPosition
;
4248 Sci::Position lengthFound
= strlen(txt
);
4249 if (!pdoc
->HasCaseFolder())
4250 pdoc
->SetCaseFolder(CaseFolderForEncoding());
4252 if (iMessage
== Message::SearchNext
) {
4253 pos
= pdoc
->FindText(searchAnchor
, pdoc
->Length(), txt
,
4254 static_cast<FindOption
>(wParam
),
4257 pos
= pdoc
->FindText(searchAnchor
, 0, txt
,
4258 static_cast<FindOption
>(wParam
),
4261 } catch (RegexError
&) {
4262 errorStatus
= Status::RegEx
;
4263 return Sci::invalidPosition
;
4265 if (pos
!= Sci::invalidPosition
) {
4266 SetSelection(pos
, pos
+ lengthFound
);
4272 std::string
Editor::CaseMapString(const std::string
&s
, CaseMapping caseMapping
) {
4274 for (char &ch
: ret
) {
4275 switch (caseMapping
) {
4276 case CaseMapping::upper
:
4277 ch
= MakeUpperCase(ch
);
4279 case CaseMapping::lower
:
4280 ch
= MakeLowerCase(ch
);
4282 default: // no action
4290 * Search for text in the target range of the document.
4291 * @return The position of the found text, -1 if not found.
4293 Sci::Position
Editor::SearchInTarget(const char *text
, Sci::Position length
) {
4294 Sci::Position lengthFound
= length
;
4296 if (!pdoc
->HasCaseFolder())
4297 pdoc
->SetCaseFolder(CaseFolderForEncoding());
4299 const Sci::Position pos
= pdoc
->FindText(targetRange
.start
.Position(), targetRange
.end
.Position(), text
,
4303 targetRange
.start
.SetPosition(pos
);
4304 targetRange
.end
.SetPosition(pos
+ lengthFound
);
4307 } catch (RegexError
&) {
4308 errorStatus
= Status::RegEx
;
4313 void Editor::GoToLine(Sci::Line lineNo
) {
4314 if (lineNo
> pdoc
->LinesTotal())
4315 lineNo
= pdoc
->LinesTotal();
4318 SetEmptySelection(pdoc
->LineStart(lineNo
));
4319 ShowCaretAtCurrentPosition();
4320 EnsureCaretVisible();
4323 static bool Close(Point pt1
, Point pt2
, Point threshold
) noexcept
{
4324 const Point ptDifference
= pt2
- pt1
;
4325 if (std::abs(ptDifference
.x
) > threshold
.x
)
4327 if (std::abs(ptDifference
.y
) > threshold
.y
)
4332 std::string
Editor::RangeText(Sci::Position start
, Sci::Position end
) const {
4334 const Sci::Position len
= end
- start
;
4335 std::string
ret(len
, '\0');
4336 pdoc
->GetCharRange(ret
.data(), start
, len
);
4339 return std::string();
4342 void Editor::CopySelectionRange(SelectionText
*ss
, bool allowLineCopy
) {
4344 if (allowLineCopy
) {
4345 const Sci::Line currentLine
= pdoc
->SciLineFromPosition(sel
.MainCaret());
4346 const Sci::Position start
= pdoc
->LineStart(currentLine
);
4347 const Sci::Position end
= pdoc
->LineEnd(currentLine
);
4349 std::string text
= RangeText(start
, end
);
4350 if (pdoc
->eolMode
!= EndOfLine::Lf
)
4351 text
.push_back('\r');
4352 if (pdoc
->eolMode
!= EndOfLine::Cr
)
4353 text
.push_back('\n');
4354 ss
->Copy(text
, pdoc
->dbcsCodePage
,
4355 vs
.styles
[StyleDefault
].characterSet
, false, true);
4359 std::vector
<SelectionRange
> rangesInOrder
= sel
.RangesCopy();
4360 if (sel
.selType
== Selection::SelTypes::rectangle
)
4361 std::sort(rangesInOrder
.begin(), rangesInOrder
.end());
4362 for (const SelectionRange
¤t
: rangesInOrder
) {
4363 text
.append(RangeText(current
.Start().Position(), current
.End().Position()));
4364 if (sel
.selType
== Selection::SelTypes::rectangle
) {
4365 if (pdoc
->eolMode
!= EndOfLine::Lf
)
4366 text
.push_back('\r');
4367 if (pdoc
->eolMode
!= EndOfLine::Cr
)
4368 text
.push_back('\n');
4371 ss
->Copy(text
, pdoc
->dbcsCodePage
,
4372 vs
.styles
[StyleDefault
].characterSet
, sel
.IsRectangular(), sel
.selType
== Selection::SelTypes::lines
);
4376 void Editor::CopyRangeToClipboard(Sci::Position start
, Sci::Position end
) {
4377 start
= pdoc
->ClampPositionIntoDocument(start
);
4378 end
= pdoc
->ClampPositionIntoDocument(end
);
4379 SelectionText selectedText
;
4380 std::string text
= RangeText(start
, end
);
4381 selectedText
.Copy(text
,
4382 pdoc
->dbcsCodePage
, vs
.styles
[StyleDefault
].characterSet
, false, false);
4383 CopyToClipboard(selectedText
);
4386 void Editor::CopyText(size_t length
, const char *text
) {
4387 SelectionText selectedText
;
4388 selectedText
.Copy(std::string(text
, length
),
4389 pdoc
->dbcsCodePage
, vs
.styles
[StyleDefault
].characterSet
, false, false);
4390 CopyToClipboard(selectedText
);
4393 void Editor::SetDragPosition(SelectionPosition newPos
) {
4394 if (newPos
.Position() >= 0) {
4395 newPos
= MovePositionOutsideChar(newPos
, 1);
4398 if (!(posDrag
== newPos
)) {
4399 const CaretPolicies dragCaretPolicies
= {
4400 CaretPolicySlop(CaretPolicy::Slop
| CaretPolicy::Strict
| CaretPolicy::Even
, 50),
4401 CaretPolicySlop(CaretPolicy::Slop
| CaretPolicy::Strict
| CaretPolicy::Even
, 2)
4403 MovedCaret(newPos
, posDrag
, true, dragCaretPolicies
);
4406 FineTickerCancel(TickReason::caret
);
4407 if ((caret
.active
) && (caret
.period
> 0) && (newPos
.Position() < 0))
4408 FineTickerStart(TickReason::caret
, caret
.period
, caret
.period
/10);
4415 void Editor::DisplayCursor(Window::Cursor c
) {
4416 if (cursorMode
== CursorShape::Normal
)
4419 wMain
.SetCursor(static_cast<Window::Cursor
>(cursorMode
));
4422 bool Editor::DragThreshold(Point ptStart
, Point ptNow
) {
4423 const Point ptDiff
= ptStart
- ptNow
;
4424 const XYPOSITION distanceSquared
= ptDiff
.x
* ptDiff
.x
+ ptDiff
.y
* ptDiff
.y
;
4425 return distanceSquared
> 16.0f
;
4428 void Editor::StartDrag() {
4429 // Always handled by subclasses
4430 //SetMouseCapture(true);
4431 //DisplayCursor(Windows::Cursor::Arrow);
4434 void Editor::DropAt(SelectionPosition position
, const char *value
, size_t lengthValue
, bool moving
, bool rectangular
) {
4435 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
4436 if (inDragDrop
== DragDrop::dragging
)
4437 dropWentOutside
= false;
4439 const bool positionWasInSelection
= PositionInSelection(position
.Position());
4441 const bool positionOnEdgeOfSelection
=
4442 (position
== SelectionStart()) || (position
== SelectionEnd());
4444 if ((inDragDrop
!= DragDrop::dragging
) || !(positionWasInSelection
) ||
4445 (positionOnEdgeOfSelection
&& !moving
)) {
4447 const SelectionPosition selStart
= SelectionStart();
4448 const SelectionPosition selEnd
= SelectionEnd();
4452 SelectionPosition positionAfterDeletion
= position
;
4453 if ((inDragDrop
== DragDrop::dragging
) && moving
) {
4454 // Remove dragged out text
4455 if (rectangular
|| sel
.selType
== Selection::SelTypes::lines
) {
4456 for (size_t r
=0; r
<sel
.Count(); r
++) {
4457 if (position
>= sel
.Range(r
).Start()) {
4458 if (position
> sel
.Range(r
).End()) {
4459 positionAfterDeletion
.Add(-sel
.Range(r
).Length());
4461 positionAfterDeletion
.Add(-SelectionRange(position
, sel
.Range(r
).Start()).Length());
4466 if (position
> selStart
) {
4467 positionAfterDeletion
.Add(-SelectionRange(selEnd
, selStart
).Length());
4472 position
= positionAfterDeletion
;
4474 std::string convertedText
= Document::TransformLineEnds(value
, lengthValue
, pdoc
->eolMode
);
4477 PasteRectangular(position
, convertedText
.c_str(), convertedText
.length());
4478 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
4479 SetEmptySelection(position
);
4481 position
= MovePositionOutsideChar(position
, sel
.MainCaret() - position
.Position());
4482 position
= RealizeVirtualSpace(position
);
4483 const Sci::Position lengthInserted
= pdoc
->InsertString(
4484 position
.Position(), convertedText
);
4485 if (lengthInserted
> 0) {
4486 SelectionPosition posAfterInsertion
= position
;
4487 posAfterInsertion
.Add(lengthInserted
);
4488 SetSelection(posAfterInsertion
, position
);
4491 } else if (inDragDrop
== DragDrop::dragging
) {
4492 SetEmptySelection(position
);
4496 void Editor::DropAt(SelectionPosition position
, const char *value
, bool moving
, bool rectangular
) {
4497 DropAt(position
, value
, strlen(value
), moving
, rectangular
);
4501 * @return true if given position is inside the selection,
4503 bool Editor::PositionInSelection(Sci::Position pos
) {
4504 pos
= MovePositionOutsideChar(pos
, sel
.MainCaret() - pos
);
4505 for (size_t r
=0; r
<sel
.Count(); r
++) {
4506 if (sel
.Range(r
).Contains(pos
))
4512 bool Editor::PointInSelection(Point pt
) {
4513 const SelectionPosition pos
= SPositionFromLocation(pt
, false, true);
4514 const Point ptPos
= LocationFromPosition(pos
);
4515 for (size_t r
=0; r
<sel
.Count(); r
++) {
4516 const SelectionRange
&range
= sel
.Range(r
);
4517 if (range
.Contains(pos
)) {
4519 if (pos
== range
.Start()) {
4520 // see if just before selection
4521 if (pt
.x
< ptPos
.x
) {
4525 if (pos
== range
.End()) {
4526 // see if just after selection
4527 if (pt
.x
> ptPos
.x
) {
4538 bool Editor::PointInSelMargin(Point pt
) const {
4539 // Really means: "Point in a margin"
4540 if (vs
.fixedColumnWidth
> 0) { // There is a margin
4541 PRectangle rcSelMargin
= GetClientRectangle();
4542 rcSelMargin
.right
= static_cast<XYPOSITION
>(vs
.textStart
- vs
.leftMarginWidth
);
4543 rcSelMargin
.left
= static_cast<XYPOSITION
>(vs
.textStart
- vs
.fixedColumnWidth
);
4544 const Point ptOrigin
= GetVisibleOriginInMain();
4545 rcSelMargin
.Move(0, -ptOrigin
.y
);
4546 return rcSelMargin
.ContainsWholePixel(pt
);
4552 Window::Cursor
Editor::GetMarginCursor(Point pt
) const noexcept
{
4554 for (const MarginStyle
&m
: vs
.ms
) {
4555 if ((pt
.x
>= x
) && (pt
.x
< x
+ m
.width
))
4556 return static_cast<Window::Cursor
>(m
.cursor
);
4559 return Window::Cursor::reverseArrow
;
4562 void Editor::TrimAndSetSelection(Sci::Position currentPos_
, Sci::Position anchor_
) {
4563 sel
.TrimSelection(SelectionRange(currentPos_
, anchor_
));
4564 SetSelection(currentPos_
, anchor_
);
4567 void Editor::LineSelection(Sci::Position lineCurrentPos_
, Sci::Position lineAnchorPos_
, bool wholeLine
) {
4568 Sci::Position selCurrentPos
;
4569 Sci::Position selAnchorPos
;
4571 const Sci::Line lineCurrent_
= pdoc
->SciLineFromPosition(lineCurrentPos_
);
4572 const Sci::Line lineAnchor_
= pdoc
->SciLineFromPosition(lineAnchorPos_
);
4573 if (lineAnchorPos_
< lineCurrentPos_
) {
4574 selCurrentPos
= pdoc
->LineStart(lineCurrent_
+ 1);
4575 selAnchorPos
= pdoc
->LineStart(lineAnchor_
);
4576 } else if (lineAnchorPos_
> lineCurrentPos_
) {
4577 selCurrentPos
= pdoc
->LineStart(lineCurrent_
);
4578 selAnchorPos
= pdoc
->LineStart(lineAnchor_
+ 1);
4579 } else { // Same line, select it
4580 selCurrentPos
= pdoc
->LineStart(lineAnchor_
+ 1);
4581 selAnchorPos
= pdoc
->LineStart(lineAnchor_
);
4584 if (lineAnchorPos_
< lineCurrentPos_
) {
4585 selCurrentPos
= StartEndDisplayLine(lineCurrentPos_
, false) + 1;
4586 selCurrentPos
= pdoc
->MovePositionOutsideChar(selCurrentPos
, 1);
4587 selAnchorPos
= StartEndDisplayLine(lineAnchorPos_
, true);
4588 } else if (lineAnchorPos_
> lineCurrentPos_
) {
4589 selCurrentPos
= StartEndDisplayLine(lineCurrentPos_
, true);
4590 selAnchorPos
= StartEndDisplayLine(lineAnchorPos_
, false) + 1;
4591 selAnchorPos
= pdoc
->MovePositionOutsideChar(selAnchorPos
, 1);
4592 } else { // Same line, select it
4593 selCurrentPos
= StartEndDisplayLine(lineAnchorPos_
, false) + 1;
4594 selCurrentPos
= pdoc
->MovePositionOutsideChar(selCurrentPos
, 1);
4595 selAnchorPos
= StartEndDisplayLine(lineAnchorPos_
, true);
4598 TrimAndSetSelection(selCurrentPos
, selAnchorPos
);
4601 void Editor::WordSelection(Sci::Position pos
) {
4602 if (pos
< wordSelectAnchorStartPos
) {
4603 // Extend backward to the word containing pos.
4604 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
4605 // This ensures that a series of empty lines isn't counted as a single "word".
4606 if (!pdoc
->IsLineEndPosition(pos
))
4607 pos
= pdoc
->ExtendWordSelect(pdoc
->MovePositionOutsideChar(pos
+ 1, 1), -1);
4608 TrimAndSetSelection(pos
, wordSelectAnchorEndPos
);
4609 } else if (pos
> wordSelectAnchorEndPos
) {
4610 // Extend forward to the word containing the character to the left of pos.
4611 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
4612 // This ensures that a series of empty lines isn't counted as a single "word".
4613 if (pos
> pdoc
->LineStart(pdoc
->LineFromPosition(pos
)))
4614 pos
= pdoc
->ExtendWordSelect(pdoc
->MovePositionOutsideChar(pos
- 1, -1), 1);
4615 TrimAndSetSelection(pos
, wordSelectAnchorStartPos
);
4617 // Select only the anchored word
4618 if (pos
>= originalAnchorPos
)
4619 TrimAndSetSelection(wordSelectAnchorEndPos
, wordSelectAnchorStartPos
);
4621 TrimAndSetSelection(wordSelectAnchorStartPos
, wordSelectAnchorEndPos
);
4625 void Editor::DwellEnd(bool mouseMoved
) {
4627 ticksToDwell
= dwellDelay
;
4629 ticksToDwell
= TimeForever
;
4630 if (dwelling
&& (dwellDelay
< TimeForever
)) {
4632 NotifyDwelling(ptMouseLast
, dwelling
);
4634 FineTickerCancel(TickReason::dwell
);
4637 void Editor::MouseLeave() {
4638 SetHotSpotRange(nullptr);
4639 SetHoverIndicatorPosition(Sci::invalidPosition
);
4640 if (!HaveMouseCapture()) {
4641 ptMouseLast
= Point(-1, -1);
4646 static constexpr bool AllowVirtualSpace(VirtualSpace virtualSpaceOptions
, bool rectangular
) noexcept
{
4647 return (!rectangular
&& (FlagSet(virtualSpaceOptions
, VirtualSpace::UserAccessible
)))
4648 || (rectangular
&& (FlagSet(virtualSpaceOptions
, VirtualSpace::RectangularSelection
)));
4651 void Editor::ButtonDownWithModifiers(Point pt
, unsigned int curTime
, KeyMod modifiers
) {
4652 SetHoverIndicatorPoint(pt
);
4653 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
4655 const bool ctrl
= FlagSet(modifiers
, KeyMod::Ctrl
);
4656 const bool shift
= FlagSet(modifiers
, KeyMod::Shift
);
4657 const bool alt
= FlagSet(modifiers
, KeyMod::Alt
);
4658 SelectionPosition newPos
= SPositionFromLocation(pt
, false, false, AllowVirtualSpace(virtualSpaceOptions
, alt
));
4659 newPos
= MovePositionOutsideChar(newPos
, sel
.MainCaret() - newPos
.Position());
4660 SelectionPosition newCharPos
= SPositionFromLocation(pt
, false, true, false);
4661 newCharPos
= MovePositionOutsideChar(newCharPos
, -1);
4662 inDragDrop
= DragDrop::none
;
4663 sel
.SetMoveExtends(false);
4665 if (NotifyMarginClick(pt
, modifiers
))
4668 NotifyIndicatorClick(true, newPos
.Position(), modifiers
);
4670 const bool inSelMargin
= PointInSelMargin(pt
);
4671 // In margin ctrl+(double)click should always select everything
4672 if (ctrl
&& inSelMargin
) {
4674 lastClickTime
= curTime
;
4678 if (shift
&& !inSelMargin
) {
4679 SetSelection(newPos
);
4681 if ((curTime
< (lastClickTime
+Platform::DoubleClickTime())) && Close(pt
, lastClick
, doubleClickCloseThreshold
)) {
4682 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
4683 SetMouseCapture(true);
4684 FineTickerStart(TickReason::scroll
, 100, 10);
4685 if (!ctrl
|| !multipleSelection
|| (selectionUnit
!= TextUnit::character
&& selectionUnit
!= TextUnit::word
))
4686 SetEmptySelection(newPos
.Position());
4687 bool doubleClick
= false;
4689 // Inside margin selection type should be either subLine or wholeLine.
4690 if (selectionUnit
== TextUnit::subLine
) {
4691 // If it is subLine, we're inside a *double* click and word wrap is enabled,
4692 // so we switch to wholeLine in order to select whole line.
4693 selectionUnit
= TextUnit::wholeLine
;
4694 } else if (selectionUnit
!= TextUnit::subLine
&& selectionUnit
!= TextUnit::wholeLine
) {
4695 // If it is neither, reset selection type to line selection.
4696 selectionUnit
= (Wrapping() && (FlagSet(marginOptions
, MarginOption::SubLineSelect
))) ? TextUnit::subLine
: TextUnit::wholeLine
;
4699 if (selectionUnit
== TextUnit::character
) {
4700 selectionUnit
= TextUnit::word
;
4702 } else if (selectionUnit
== TextUnit::word
) {
4703 // Since we ended up here, we're inside a *triple* click, which should always select
4704 // whole line regardless of word wrap being enabled or not.
4705 selectionUnit
= TextUnit::wholeLine
;
4707 selectionUnit
= TextUnit::character
;
4708 originalAnchorPos
= sel
.MainCaret();
4712 if (selectionUnit
== TextUnit::word
) {
4713 Sci::Position charPos
= originalAnchorPos
;
4714 if (sel
.MainCaret() == originalAnchorPos
) {
4715 charPos
= PositionFromLocation(pt
, false, true);
4716 charPos
= MovePositionOutsideChar(charPos
, -1);
4719 Sci::Position startWord
;
4720 Sci::Position endWord
;
4721 if ((sel
.MainCaret() >= originalAnchorPos
) && !pdoc
->IsLineEndPosition(charPos
)) {
4722 startWord
= pdoc
->ExtendWordSelect(pdoc
->MovePositionOutsideChar(charPos
+ 1, 1), -1);
4723 endWord
= pdoc
->ExtendWordSelect(charPos
, 1);
4725 // Selecting backwards, or anchor beyond last character on line. In these cases,
4726 // we select the word containing the character to the *left* of the anchor.
4727 if (charPos
> pdoc
->LineStart(pdoc
->LineFromPosition(charPos
))) {
4728 startWord
= pdoc
->ExtendWordSelect(charPos
, -1);
4729 endWord
= pdoc
->ExtendWordSelect(startWord
, 1);
4731 // Anchor at start of line; select nothing to begin with.
4732 startWord
= charPos
;
4737 wordSelectAnchorStartPos
= startWord
;
4738 wordSelectAnchorEndPos
= endWord
;
4739 wordSelectInitialCaretPos
= sel
.MainCaret();
4740 WordSelection(wordSelectInitialCaretPos
);
4741 } else if (selectionUnit
== TextUnit::subLine
|| selectionUnit
== TextUnit::wholeLine
) {
4742 lineAnchorPos
= newPos
.Position();
4743 LineSelection(lineAnchorPos
, lineAnchorPos
, selectionUnit
== TextUnit::wholeLine
);
4744 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
4746 SetEmptySelection(sel
.MainCaret());
4748 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
4750 NotifyDoubleClick(pt
, modifiers
);
4751 if (PositionIsHotspot(newCharPos
.Position()))
4752 NotifyHotSpotDoubleClicked(newCharPos
.Position(), modifiers
);
4754 } else { // Single click
4756 if (sel
.IsRectangular() || (sel
.Count() > 1)) {
4757 InvalidateWholeSelection();
4760 sel
.selType
= Selection::SelTypes::stream
;
4762 // Single click in margin: select wholeLine or only subLine if word wrap is enabled
4763 lineAnchorPos
= newPos
.Position();
4764 selectionUnit
= (Wrapping() && (FlagSet(marginOptions
, MarginOption::SubLineSelect
))) ? TextUnit::subLine
: TextUnit::wholeLine
;
4765 LineSelection(lineAnchorPos
, lineAnchorPos
, selectionUnit
== TextUnit::wholeLine
);
4767 // Single shift+click in margin: select from line anchor to clicked line
4768 if (sel
.MainAnchor() > sel
.MainCaret())
4769 lineAnchorPos
= sel
.MainAnchor() - 1;
4771 lineAnchorPos
= sel
.MainAnchor();
4772 // Reset selection type if there is an empty selection.
4773 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
4774 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
4775 // This ensures that we continue selecting in the same selection mode.
4776 if (sel
.Empty() || (selectionUnit
!= TextUnit::subLine
&& selectionUnit
!= TextUnit::wholeLine
))
4777 selectionUnit
= (Wrapping() && (FlagSet(marginOptions
, MarginOption::SubLineSelect
))) ? TextUnit::subLine
: TextUnit::wholeLine
;
4778 LineSelection(newPos
.Position(), lineAnchorPos
, selectionUnit
== TextUnit::wholeLine
);
4781 SetDragPosition(SelectionPosition(Sci::invalidPosition
));
4782 SetMouseCapture(true);
4783 FineTickerStart(TickReason::scroll
, 100, 10);
4785 if (PointIsHotspot(pt
)) {
4786 NotifyHotSpotClicked(newCharPos
.Position(), modifiers
);
4787 hotSpotClickPos
= newCharPos
.Position();
4790 if (PointInSelection(pt
) && !SelectionEmpty())
4791 inDragDrop
= DragDrop::initial
;
4793 inDragDrop
= DragDrop::none
;
4795 SetMouseCapture(true);
4796 FineTickerStart(TickReason::scroll
, 100, 10);
4797 if (inDragDrop
!= DragDrop::initial
) {
4798 SetDragPosition(SelectionPosition(Sci::invalidPosition
));
4800 if (ctrl
&& multipleSelection
) {
4801 const SelectionRange
range(newPos
);
4802 sel
.TentativeSelection(range
);
4803 InvalidateSelection(range
, true);
4805 InvalidateSelection(SelectionRange(newPos
), true);
4806 if (sel
.Count() > 1)
4808 if ((sel
.Count() > 1) || (sel
.selType
!= Selection::SelTypes::stream
))
4810 sel
.selType
= alt
? Selection::SelTypes::rectangle
: Selection::SelTypes::stream
;
4811 SetSelection(newPos
, newPos
);
4814 SelectionPosition anchorCurrent
= newPos
;
4816 anchorCurrent
= sel
.IsRectangular() ?
4817 sel
.Rectangular().anchor
: sel
.RangeMain().anchor
;
4818 sel
.selType
= alt
? Selection::SelTypes::rectangle
: Selection::SelTypes::stream
;
4819 selectionUnit
= TextUnit::character
;
4820 originalAnchorPos
= sel
.MainCaret();
4821 sel
.Rectangular() = SelectionRange(newPos
, anchorCurrent
);
4822 SetRectangularRange();
4826 lastClickTime
= curTime
;
4828 lastXChosen
= static_cast<int>(pt
.x
) + xOffset
;
4829 ShowCaretAtCurrentPosition();
4832 void Editor::RightButtonDownWithModifiers(Point pt
, unsigned int, KeyMod modifiers
) {
4833 if (NotifyMarginRightClick(pt
, modifiers
))
4837 bool Editor::PositionIsHotspot(Sci::Position position
) const noexcept
{
4838 return vs
.styles
[pdoc
->StyleIndexAt(position
)].hotspot
;
4841 bool Editor::PointIsHotspot(Point pt
) {
4842 const Sci::Position pos
= PositionFromLocation(pt
, true, true);
4843 if (pos
== Sci::invalidPosition
)
4845 return PositionIsHotspot(pos
);
4848 void Editor::SetHoverIndicatorPosition(Sci::Position position
) {
4849 const Sci::Position hoverIndicatorPosPrev
= hoverIndicatorPos
;
4850 hoverIndicatorPos
= Sci::invalidPosition
;
4851 if (!vs
.indicatorsDynamic
)
4853 if (position
!= Sci::invalidPosition
) {
4854 for (const IDecoration
*deco
: pdoc
->decorations
->View()) {
4855 if (vs
.indicators
[deco
->Indicator()].IsDynamic()) {
4856 if (pdoc
->decorations
->ValueAt(deco
->Indicator(), position
)) {
4857 hoverIndicatorPos
= position
;
4862 if (hoverIndicatorPosPrev
!= hoverIndicatorPos
) {
4867 void Editor::SetHoverIndicatorPoint(Point pt
) {
4868 if (!vs
.indicatorsDynamic
) {
4869 SetHoverIndicatorPosition(Sci::invalidPosition
);
4871 SetHoverIndicatorPosition(PositionFromLocation(pt
, true, true));
4875 void Editor::SetHotSpotRange(const Point
*pt
) {
4877 const Sci::Position pos
= PositionFromLocation(*pt
, false, true);
4879 // If we don't limit this to word characters then the
4880 // range can encompass more than the run range and then
4881 // the underline will not be drawn properly.
4883 hsNew
.start
= pdoc
->ExtendStyleRange(pos
, -1, hotspotSingleLine
);
4884 hsNew
.end
= pdoc
->ExtendStyleRange(pos
, 1, hotspotSingleLine
);
4886 // Only invalidate the range if the hotspot range has changed...
4887 if (!(hsNew
== hotspot
)) {
4888 if (hotspot
.Valid()) {
4889 InvalidateRange(hotspot
.start
, hotspot
.end
);
4892 InvalidateRange(hotspot
.start
, hotspot
.end
);
4895 if (hotspot
.Valid()) {
4896 InvalidateRange(hotspot
.start
, hotspot
.end
);
4898 hotspot
= Range(Sci::invalidPosition
);
4902 void Editor::ButtonMoveWithModifiers(Point pt
, unsigned int, KeyMod modifiers
) {
4903 if (ptMouseLast
!= pt
) {
4907 SelectionPosition movePos
= SPositionFromLocation(pt
, false, false,
4908 AllowVirtualSpace(virtualSpaceOptions
, sel
.IsRectangular()));
4909 movePos
= MovePositionOutsideChar(movePos
, sel
.MainCaret() - movePos
.Position());
4911 if (inDragDrop
== DragDrop::initial
) {
4912 if (DragThreshold(ptMouseLast
, pt
)) {
4913 SetMouseCapture(false);
4914 FineTickerCancel(TickReason::scroll
);
4915 SetDragPosition(movePos
);
4916 CopySelectionRange(&drag
);
4923 PRectangle rcClient
= GetClientRectangle();
4924 const Point ptOrigin
= GetVisibleOriginInMain();
4925 rcClient
.Move(0, -ptOrigin
.y
);
4926 if ((dwellDelay
< TimeForever
) && rcClient
.Contains(pt
)) {
4927 FineTickerStart(TickReason::dwell
, dwellDelay
, dwellDelay
/10);
4929 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
4930 if (HaveMouseCapture()) {
4932 // Slow down autoscrolling/selection
4933 autoScrollTimer
.ticksToWait
-= timer
.tickSize
;
4934 if (autoScrollTimer
.ticksToWait
> 0)
4936 autoScrollTimer
.ticksToWait
= autoScrollDelay
;
4939 if (posDrag
.IsValid()) {
4940 SetDragPosition(movePos
);
4942 if (selectionUnit
== TextUnit::character
) {
4943 if (sel
.selType
== Selection::SelTypes::stream
&& FlagSet(modifiers
, KeyMod::Alt
) && mouseSelectionRectangularSwitch
) {
4944 sel
.selType
= Selection::SelTypes::rectangle
;
4946 if (sel
.IsRectangular()) {
4947 sel
.Rectangular() = SelectionRange(movePos
, sel
.Rectangular().anchor
);
4948 SetSelection(movePos
, sel
.RangeMain().anchor
);
4949 } else if (sel
.Count() > 1) {
4950 InvalidateSelection(sel
.RangeMain(), false);
4951 const SelectionRange
range(movePos
, sel
.RangeMain().anchor
);
4952 sel
.TentativeSelection(range
);
4953 InvalidateSelection(range
, true);
4955 SetSelection(movePos
, sel
.RangeMain().anchor
);
4957 } else if (selectionUnit
== TextUnit::word
) {
4958 // Continue selecting by word
4959 if (movePos
.Position() == wordSelectInitialCaretPos
) { // Didn't move
4960 // No need to do anything. Previously this case was lumped
4961 // in with "Moved forward", but that can be harmful in this
4962 // case: a handler for the NotifyDoubleClick re-adjusts
4963 // the selection for a fancier definition of "word" (for
4964 // example, in Perl it is useful to include the leading
4965 // '$', '%' or '@' on variables for word selection). In this
4966 // the ButtonMove() called via TickFor() for auto-scrolling
4967 // could result in the fancier word selection adjustment
4970 wordSelectInitialCaretPos
= -1;
4971 WordSelection(movePos
.Position());
4974 // Continue selecting by line
4975 LineSelection(movePos
.Position(), lineAnchorPos
, selectionUnit
== TextUnit::wholeLine
);
4980 const Sci::Line lineMove
= DisplayFromPosition(movePos
.Position());
4981 if (pt
.y
>= rcClient
.bottom
) {
4982 ScrollTo(lineMove
- LinesOnScreen() + 1);
4984 } else if (pt
.y
< rcClient
.top
) {
4988 EnsureCaretVisible(false, false, true);
4990 if (hotspot
.Valid() && !PointIsHotspot(pt
))
4991 SetHotSpotRange(nullptr);
4993 if (hotSpotClickPos
!= Sci::invalidPosition
&& PositionFromLocation(pt
, true, true) != hotSpotClickPos
) {
4994 if (inDragDrop
== DragDrop::none
) {
4995 DisplayCursor(Window::Cursor::text
);
4997 hotSpotClickPos
= Sci::invalidPosition
;
5001 if (vs
.fixedColumnWidth
> 0) { // There is a margin
5002 if (PointInSelMargin(pt
)) {
5003 DisplayCursor(GetMarginCursor(pt
));
5004 SetHotSpotRange(nullptr);
5005 SetHoverIndicatorPosition(Sci::invalidPosition
);
5006 return; // No need to test for selection
5009 // Display regular (drag) cursor over selection
5010 if (PointInSelection(pt
) && !SelectionEmpty()) {
5011 DisplayCursor(Window::Cursor::arrow
);
5012 SetHoverIndicatorPosition(Sci::invalidPosition
);
5014 SetHoverIndicatorPoint(pt
);
5015 if (PointIsHotspot(pt
)) {
5016 DisplayCursor(Window::Cursor::hand
);
5017 SetHotSpotRange(&pt
);
5019 if (hoverIndicatorPos
!= Sci::invalidPosition
)
5020 DisplayCursor(Window::Cursor::hand
);
5022 DisplayCursor(Window::Cursor::text
);
5023 SetHotSpotRange(nullptr);
5029 void Editor::ButtonUpWithModifiers(Point pt
, unsigned int curTime
, KeyMod modifiers
) {
5030 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
5031 SelectionPosition newPos
= SPositionFromLocation(pt
, false, false,
5032 AllowVirtualSpace(virtualSpaceOptions
, sel
.IsRectangular()));
5033 if (hoverIndicatorPos
!= Sci::invalidPosition
)
5034 InvalidateRange(newPos
.Position(), newPos
.Position() + 1);
5035 newPos
= MovePositionOutsideChar(newPos
, sel
.MainCaret() - newPos
.Position());
5036 if (inDragDrop
== DragDrop::initial
) {
5037 inDragDrop
= DragDrop::none
;
5038 SetEmptySelection(newPos
);
5039 selectionUnit
= TextUnit::character
;
5040 originalAnchorPos
= sel
.MainCaret();
5042 if (hotSpotClickPos
!= Sci::invalidPosition
&& PointIsHotspot(pt
)) {
5043 hotSpotClickPos
= Sci::invalidPosition
;
5044 SelectionPosition newCharPos
= SPositionFromLocation(pt
, false, true, false);
5045 newCharPos
= MovePositionOutsideChar(newCharPos
, -1);
5046 NotifyHotSpotReleaseClick(newCharPos
.Position(), modifiers
& KeyMod::Ctrl
);
5048 if (HaveMouseCapture()) {
5049 if (PointInSelMargin(pt
)) {
5050 DisplayCursor(GetMarginCursor(pt
));
5052 DisplayCursor(Window::Cursor::text
);
5053 SetHotSpotRange(nullptr);
5056 SetMouseCapture(false);
5057 FineTickerCancel(TickReason::scroll
);
5058 NotifyIndicatorClick(false, newPos
.Position(), modifiers
);
5059 if (inDragDrop
== DragDrop::dragging
) {
5060 const SelectionPosition selStart
= SelectionStart();
5061 const SelectionPosition selEnd
= SelectionEnd();
5062 if (selStart
< selEnd
) {
5063 if (drag
.Length()) {
5064 const Sci::Position length
= drag
.Length();
5065 if (FlagSet(modifiers
, KeyMod::Ctrl
)) {
5066 const Sci::Position lengthInserted
= pdoc
->InsertString(
5067 newPos
.Position(), drag
.Data(), length
);
5068 if (lengthInserted
> 0) {
5069 SetSelection(newPos
.Position(), newPos
.Position() + lengthInserted
);
5071 } else if (newPos
< selStart
) {
5072 pdoc
->DeleteChars(selStart
.Position(), drag
.Length());
5073 const Sci::Position lengthInserted
= pdoc
->InsertString(
5074 newPos
.Position(), drag
.Data(), length
);
5075 if (lengthInserted
> 0) {
5076 SetSelection(newPos
.Position(), newPos
.Position() + lengthInserted
);
5078 } else if (newPos
> selEnd
) {
5079 pdoc
->DeleteChars(selStart
.Position(), drag
.Length());
5080 newPos
.Add(-static_cast<Sci::Position
>(drag
.Length()));
5081 const Sci::Position lengthInserted
= pdoc
->InsertString(
5082 newPos
.Position(), drag
.Data(), length
);
5083 if (lengthInserted
> 0) {
5084 SetSelection(newPos
.Position(), newPos
.Position() + lengthInserted
);
5087 SetEmptySelection(newPos
.Position());
5091 selectionUnit
= TextUnit::character
;
5094 if (selectionUnit
== TextUnit::character
) {
5095 if (sel
.Count() > 1) {
5097 SelectionRange(newPos
, sel
.Range(sel
.Count() - 1).anchor
);
5098 InvalidateWholeSelection();
5100 SetSelection(newPos
, sel
.RangeMain().anchor
);
5103 sel
.CommitTentative();
5105 SetRectangularRange();
5106 lastClickTime
= curTime
;
5108 lastXChosen
= static_cast<int>(pt
.x
) + xOffset
;
5109 if (sel
.selType
== Selection::SelTypes::stream
) {
5112 inDragDrop
= DragDrop::none
;
5113 EnsureCaretVisible(false);
5117 bool Editor::Idle() {
5120 bool needWrap
= Wrapping() && wrapPending
.NeedsWrap();
5123 // Wrap lines during idle.
5124 WrapLines(WrapScope::wsIdle
);
5126 needWrap
= wrapPending
.NeedsWrap();
5127 } else if (needIdleStyling
) {
5131 // Add more idle things to do here, but make sure idleDone is
5132 // set correctly before the function returns. returning
5133 // false will stop calling this idle function until SetIdle() is
5136 const bool idleDone
= !needWrap
&& !needIdleStyling
; // && thatDone && theOtherThingDone...
5141 void Editor::TickFor(TickReason reason
) {
5143 case TickReason::caret
:
5144 caret
.on
= !caret
.on
;
5149 case TickReason::scroll
:
5151 ButtonMoveWithModifiers(ptMouseLast
, 0, KeyMod::Norm
);
5153 case TickReason::widen
:
5155 FineTickerCancel(TickReason::widen
);
5157 case TickReason::dwell
:
5158 if ((!HaveMouseCapture()) &&
5159 (ptMouseLast
.y
>= 0)) {
5161 NotifyDwelling(ptMouseLast
, dwelling
);
5163 FineTickerCancel(TickReason::dwell
);
5166 // tickPlatform handled by subclass
5171 // FineTickerStart is be overridden by subclasses that support fine ticking so
5172 // this method should never be called.
5173 bool Editor::FineTickerRunning(TickReason
) {
5178 // FineTickerStart is be overridden by subclasses that support fine ticking so
5179 // this method should never be called.
5180 void Editor::FineTickerStart(TickReason
, int, int) {
5184 // FineTickerCancel is be overridden by subclasses that support fine ticking so
5185 // this method should never be called.
5186 void Editor::FineTickerCancel(TickReason
) {
5190 void Editor::SetFocusState(bool focusState
) {
5191 const bool changing
= hasFocus
!= focusState
;
5192 hasFocus
= focusState
;
5196 NotifyFocus(hasFocus
);
5200 ShowCaretAtCurrentPosition();
5203 void Editor::UpdateBaseElements() {
5204 // Overridden by subclasses
5207 Sci::Position
Editor::PositionAfterArea(PRectangle rcArea
) const {
5208 // The start of the document line after the display line after the area
5209 // This often means that the line after a modification is restyled which helps
5210 // detect multiline comment additions and heals single line comments
5211 const Sci::Line lineAfter
= TopLineOfMain() + static_cast<Sci::Line
>(rcArea
.bottom
- 1) / vs
.lineHeight
+ 1;
5212 if (lineAfter
< pcs
->LinesDisplayed())
5213 return pdoc
->LineStart(pcs
->DocFromDisplay(lineAfter
) + 1);
5215 return pdoc
->Length();
5218 // Style to a position within the view. If this causes a change at end of last line then
5219 // affects later lines so style all the viewed text.
5220 void Editor::StyleToPositionInView(Sci::Position pos
) {
5221 Sci::Position endWindow
= PositionAfterArea(GetClientDrawingRectangle());
5222 if (pos
> endWindow
)
5224 const int styleAtEnd
= pdoc
->StyleIndexAt(pos
-1);
5225 pdoc
->EnsureStyledTo(pos
);
5226 if ((endWindow
> pos
) && (styleAtEnd
!= pdoc
->StyleIndexAt(pos
-1))) {
5227 // Style at end of line changed so is multi-line change like starting a comment
5228 // so require rest of window to be styled.
5229 DiscardOverdraw(); // Prepared bitmaps may be invalid
5230 // DiscardOverdraw may have truncated client drawing area so recalculate endWindow
5231 endWindow
= PositionAfterArea(GetClientDrawingRectangle());
5232 pdoc
->EnsureStyledTo(endWindow
);
5236 Sci::Position
Editor::PositionAfterMaxStyling(Sci::Position posMax
, bool scrolling
) const {
5237 if (SynchronousStylingToVisible()) {
5238 // Both states do not limit styling
5242 // Try to keep time taken by styling reasonable so interaction remains smooth.
5243 // When scrolling, allow less time to ensure responsive
5244 const double secondsAllowed
= scrolling
? 0.005 : 0.02;
5246 const size_t actionsInAllowedTime
= std::clamp
<Sci::Line
>(
5247 pdoc
->durationStyleOneByte
.ActionsInAllowedTime(secondsAllowed
),
5249 const Sci::Line lineLast
= pdoc
->LineFromPositionAfter(pdoc
->SciLineFromPosition(pdoc
->GetEndStyled()), actionsInAllowedTime
);
5250 const Sci::Line stylingMaxLine
= std::min(lineLast
, pdoc
->LinesTotal());
5252 return std::min(pdoc
->LineStart(stylingMaxLine
), posMax
);
5255 void Editor::StartIdleStyling(bool truncatedLastStyling
) {
5256 if ((idleStyling
== IdleStyling::All
) || (idleStyling
== IdleStyling::AfterVisible
)) {
5257 if (pdoc
->GetEndStyled() < pdoc
->Length()) {
5258 // Style remainder of document in idle time
5259 needIdleStyling
= true;
5261 } else if (truncatedLastStyling
) {
5262 needIdleStyling
= true;
5265 if (needIdleStyling
) {
5270 // Style for an area but bound the amount of styling to remain responsive
5271 void Editor::StyleAreaBounded(PRectangle rcArea
, bool scrolling
) {
5272 const Sci::Position posAfterArea
= PositionAfterArea(rcArea
);
5273 const Sci::Position posAfterMax
= PositionAfterMaxStyling(posAfterArea
, scrolling
);
5274 if (posAfterMax
< posAfterArea
) {
5275 // Idle styling may be performed before current visible area
5276 // Style a bit now then style further in idle time
5277 pdoc
->StyleToAdjustingLineDuration(posAfterMax
);
5279 // Can style all wanted now.
5280 StyleToPositionInView(posAfterArea
);
5282 StartIdleStyling(posAfterMax
< posAfterArea
);
5285 void Editor::IdleStyle() {
5286 const Sci::Position posAfterArea
= PositionAfterArea(GetClientRectangle());
5287 const Sci::Position endGoal
= (idleStyling
>= IdleStyling::AfterVisible
) ?
5288 pdoc
->Length() : posAfterArea
;
5289 const Sci::Position posAfterMax
= PositionAfterMaxStyling(endGoal
, false);
5290 pdoc
->StyleToAdjustingLineDuration(posAfterMax
);
5291 if (pdoc
->GetEndStyled() >= endGoal
) {
5292 needIdleStyling
= false;
5296 void Editor::IdleWork() {
5297 // Style the line after the modification as this allows modifications that change just the
5298 // line of the modification to heal instead of propagating to the rest of the window.
5299 if (FlagSet(workNeeded
.items
, WorkItems::style
)) {
5300 StyleToPositionInView(pdoc
->LineStart(pdoc
->LineFromPosition(workNeeded
.upTo
) + 2));
5306 void Editor::QueueIdleWork(WorkItems items
, Sci::Position upTo
) {
5307 workNeeded
.Need(items
, upTo
);
5310 int Editor::SupportsFeature(Supports feature
) {
5311 AutoSurface
surface(this);
5312 return surface
->SupportsFeature(feature
);
5315 bool Editor::PaintContains(PRectangle rc
) {
5319 return rcPaint
.Contains(rc
);
5323 bool Editor::PaintContainsMargin() {
5324 if (HasMarginWindow()) {
5325 // With separate margin view, paint of text view
5326 // never contains margin.
5329 PRectangle rcSelMargin
= GetClientRectangle();
5330 rcSelMargin
.right
= static_cast<XYPOSITION
>(vs
.textStart
);
5331 return PaintContains(rcSelMargin
);
5334 void Editor::CheckForChangeOutsidePaint(Range r
) {
5335 if (paintState
== PaintState::painting
&& !paintingAllText
) {
5336 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
5340 PRectangle rcRange
= RectangleFromRange(r
, 0);
5341 const PRectangle rcText
= GetTextRectangle();
5342 if (rcRange
.top
< rcText
.top
) {
5343 rcRange
.top
= rcText
.top
;
5345 if (rcRange
.bottom
> rcText
.bottom
) {
5346 rcRange
.bottom
= rcText
.bottom
;
5349 if (!PaintContains(rcRange
)) {
5351 paintAbandonedByStyling
= true;
5356 void Editor::SetBraceHighlight(Sci::Position pos0
, Sci::Position pos1
, int matchStyle
) {
5357 if ((pos0
!= braces
[0]) || (pos1
!= braces
[1]) || (matchStyle
!= bracesMatchStyle
)) {
5358 if ((braces
[0] != pos0
) || (matchStyle
!= bracesMatchStyle
)) {
5359 CheckForChangeOutsidePaint(Range(braces
[0]));
5360 CheckForChangeOutsidePaint(Range(pos0
));
5363 if ((braces
[1] != pos1
) || (matchStyle
!= bracesMatchStyle
)) {
5364 CheckForChangeOutsidePaint(Range(braces
[1]));
5365 CheckForChangeOutsidePaint(Range(pos1
));
5368 bracesMatchStyle
= matchStyle
;
5369 if (paintState
== PaintState::notPainting
) {
5375 void Editor::SetAnnotationHeights(Sci::Line start
, Sci::Line end
) {
5376 if (vs
.annotationVisible
!= AnnotationVisible::Hidden
) {
5378 bool changedHeight
= false;
5379 for (Sci::Line line
=start
; line
<end
&& line
<pdoc
->LinesTotal(); line
++) {
5380 int linesWrapped
= 1;
5382 AutoSurface
surface(this);
5383 std::shared_ptr
<LineLayout
> ll
= view
.RetrieveLineLayout(line
, *this);
5384 if (surface
&& ll
) {
5385 view
.LayoutLine(*this, surface
, vs
, ll
.get(), wrapWidth
);
5386 linesWrapped
= ll
->lines
;
5389 if (pcs
->SetHeight(line
, pdoc
->AnnotationLines(line
) + linesWrapped
))
5390 changedHeight
= true;
5392 if (changedHeight
) {
5394 SetVerticalScrollPos();
5400 void Editor::SetDocPointer(Document
*document
) {
5401 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
5402 pdoc
->RemoveWatcher(this, nullptr);
5405 pdoc
= new Document(DocumentOption::Default
);
5410 pcs
= ContractionStateCreate(pdoc
->IsLarge());
5412 // Ensure all positions within document
5414 targetRange
= SelectionSegment();
5416 braces
[0] = Sci::invalidPosition
;
5417 braces
[1] = Sci::invalidPosition
;
5419 vs
.ReleaseAllExtendedStyles();
5421 SetRepresentations();
5423 // Reset the contraction state to fully shown.
5425 pcs
->InsertLines(0, pdoc
->LinesTotal() - 1);
5426 SetAnnotationHeights(0, pdoc
->LinesTotal());
5427 view
.llc
.Deallocate();
5430 hotspot
= Range(Sci::invalidPosition
);
5431 hoverIndicatorPos
= Sci::invalidPosition
;
5433 view
.ClearAllTabstops();
5435 pdoc
->AddWatcher(this, nullptr);
5440 void Editor::SetAnnotationVisible(AnnotationVisible visible
) {
5441 if (vs
.annotationVisible
!= visible
) {
5442 const bool changedFromOrToHidden
= ((vs
.annotationVisible
!= AnnotationVisible::Hidden
) != (visible
!= AnnotationVisible::Hidden
));
5443 vs
.annotationVisible
= visible
;
5444 if (changedFromOrToHidden
) {
5445 const int dir
= (vs
.annotationVisible
!= AnnotationVisible::Hidden
) ? 1 : -1;
5446 for (Sci::Line line
=0; line
<pdoc
->LinesTotal(); line
++) {
5447 const int annotationLines
= pdoc
->AnnotationLines(line
);
5448 if (annotationLines
> 0) {
5449 pcs
->SetHeight(line
, pcs
->GetHeight(line
) + annotationLines
* dir
);
5458 void Editor::SetEOLAnnotationVisible(EOLAnnotationVisible visible
) {
5459 if (vs
.eolAnnotationVisible
!= visible
) {
5460 vs
.eolAnnotationVisible
= visible
;
5466 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
5468 Sci::Line
Editor::ExpandLine(Sci::Line line
) {
5469 const Sci::Line lineMaxSubord
= pdoc
->GetLastChild(line
);
5471 Sci::Line lineStart
= line
;
5472 while (line
<= lineMaxSubord
) {
5473 const FoldLevel level
= pdoc
->GetFoldLevel(line
);
5474 if (LevelIsHeader(level
)) {
5475 pcs
->SetVisible(lineStart
, line
, true);
5476 if (pcs
->GetExpanded(line
)) {
5477 line
= ExpandLine(line
);
5479 line
= pdoc
->GetLastChild(line
);
5481 lineStart
= line
+ 1;
5485 if (lineStart
<= lineMaxSubord
) {
5486 pcs
->SetVisible(lineStart
, lineMaxSubord
, true);
5488 return lineMaxSubord
;
5491 void Editor::SetFoldExpanded(Sci::Line lineDoc
, bool expanded
) {
5492 if (pcs
->SetExpanded(lineDoc
, expanded
)) {
5497 void Editor::FoldLine(Sci::Line line
, FoldAction action
) {
5499 if (action
== FoldAction::Toggle
) {
5500 if (!LevelIsHeader(pdoc
->GetFoldLevel(line
))) {
5501 line
= pdoc
->GetFoldParent(line
);
5505 action
= (pcs
->GetExpanded(line
)) ? FoldAction::Contract
: FoldAction::Expand
;
5508 if (action
== FoldAction::Contract
) {
5509 const Sci::Line lineMaxSubord
= pdoc
->GetLastChild(line
);
5510 if (lineMaxSubord
> line
) {
5511 pcs
->SetExpanded(line
, false);
5512 pcs
->SetVisible(line
+ 1, lineMaxSubord
, false);
5514 const Sci::Line lineCurrent
=
5515 pdoc
->SciLineFromPosition(sel
.MainCaret());
5516 if (lineCurrent
> line
&& lineCurrent
<= lineMaxSubord
) {
5517 // This does not re-expand the fold
5518 EnsureCaretVisible();
5523 if (!(pcs
->GetVisible(line
))) {
5524 EnsureLineVisible(line
, false);
5527 pcs
->SetExpanded(line
, true);
5536 void Editor::FoldExpand(Sci::Line line
, FoldAction action
, FoldLevel level
) {
5537 bool expanding
= action
== FoldAction::Expand
;
5538 if (action
== FoldAction::Toggle
) {
5539 expanding
= !pcs
->GetExpanded(line
);
5541 // Ensure child lines lexed and fold information extracted before
5542 // flipping the state.
5543 pdoc
->GetLastChild(line
, LevelNumberPart(level
));
5544 SetFoldExpanded(line
, expanding
);
5545 if (expanding
&& (pcs
->HiddenLines() == 0))
5548 const Sci::Line lineMaxSubord
= pdoc
->GetLastChild(line
, LevelNumberPart(level
));
5550 pcs
->SetVisible(line
, lineMaxSubord
, expanding
);
5551 while (line
<= lineMaxSubord
) {
5552 const FoldLevel levelLine
= pdoc
->GetFoldLevel(line
);
5553 if (LevelIsHeader(levelLine
)) {
5554 SetFoldExpanded(line
, expanding
);
5562 Sci::Line
Editor::ContractedFoldNext(Sci::Line lineStart
) const {
5563 for (Sci::Line line
= lineStart
; line
<pdoc
->LinesTotal();) {
5564 if (!pcs
->GetExpanded(line
) && LevelIsHeader(pdoc
->GetFoldLevel(line
)))
5566 line
= pcs
->ContractedNext(line
+1);
5575 * Recurse up from this line to find any folds that prevent this line from being visible
5576 * and unfold them all.
5578 void Editor::EnsureLineVisible(Sci::Line lineDoc
, bool enforcePolicy
) {
5580 // In case in need of wrapping to ensure DisplayFromDoc works.
5581 if (lineDoc
>= wrapPending
.start
) {
5582 if (WrapLines(WrapScope::wsAll
)) {
5587 if (!pcs
->GetVisible(lineDoc
)) {
5588 // Back up to find a non-blank line
5589 Sci::Line lookLine
= lineDoc
;
5590 FoldLevel lookLineLevel
= pdoc
->GetFoldLevel(lookLine
);
5591 while ((lookLine
> 0) && LevelIsWhitespace(lookLineLevel
)) {
5592 lookLineLevel
= pdoc
->GetFoldLevel(--lookLine
);
5594 Sci::Line lineParent
= pdoc
->GetFoldParent(lookLine
);
5595 if (lineParent
< 0) {
5596 // Backed up to a top level line, so try to find parent of initial line
5597 lineParent
= pdoc
->GetFoldParent(lineDoc
);
5599 if (lineParent
>= 0) {
5600 if (lineDoc
!= lineParent
)
5601 EnsureLineVisible(lineParent
, enforcePolicy
);
5602 if (!pcs
->GetExpanded(lineParent
)) {
5603 pcs
->SetExpanded(lineParent
, true);
5604 ExpandLine(lineParent
);
5610 if (enforcePolicy
) {
5611 const Sci::Line lineDisplay
= pcs
->DisplayFromDoc(lineDoc
);
5612 if (FlagSet(visiblePolicy
.policy
, VisiblePolicy::Slop
)) {
5613 if ((topLine
> lineDisplay
) || ((FlagSet(visiblePolicy
.policy
, VisiblePolicy::Strict
)) && (topLine
+ visiblePolicy
.slop
> lineDisplay
))) {
5614 SetTopLine(std::clamp
<Sci::Line
>(lineDisplay
- visiblePolicy
.slop
, 0, MaxScrollPos()));
5615 SetVerticalScrollPos();
5617 } else if ((lineDisplay
> topLine
+ LinesOnScreen() - 1) ||
5618 ((FlagSet(visiblePolicy
.policy
, VisiblePolicy::Strict
)) && (lineDisplay
> topLine
+ LinesOnScreen() - 1 - visiblePolicy
.slop
))) {
5619 SetTopLine(std::clamp
<Sci::Line
>(lineDisplay
- LinesOnScreen() + 1 + visiblePolicy
.slop
, 0, MaxScrollPos()));
5620 SetVerticalScrollPos();
5624 if ((topLine
> lineDisplay
) || (lineDisplay
> topLine
+ LinesOnScreen() - 1) || (FlagSet(visiblePolicy
.policy
, VisiblePolicy::Strict
))) {
5625 SetTopLine(std::clamp
<Sci::Line
>(lineDisplay
- LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
5626 SetVerticalScrollPos();
5633 void Editor::FoldAll(FoldAction action
) {
5634 const Sci::Line maxLine
= pdoc
->LinesTotal();
5635 const bool contractAll
= FlagSet(action
, FoldAction::ContractEveryLevel
);
5636 action
= static_cast<FoldAction
>(static_cast<int>(action
) & ~static_cast<int>(FoldAction::ContractEveryLevel
));
5637 bool expanding
= action
== FoldAction::Expand
;
5639 pdoc
->EnsureStyledTo(pdoc
->Length());
5642 if (action
== FoldAction::Toggle
) {
5643 // Discover current state
5644 for (; line
< maxLine
; line
++) {
5645 if (LevelIsHeader(pdoc
->GetFoldLevel(line
))) {
5646 expanding
= !pcs
->GetExpanded(line
);
5652 pcs
->SetVisible(0, maxLine
-1, true);
5655 for (; line
< maxLine
; line
++) {
5656 const FoldLevel level
= pdoc
->GetFoldLevel(line
);
5657 if (LevelIsHeader(level
)) {
5658 if (FoldLevel::Base
== LevelNumberPart(level
)) {
5659 SetFoldExpanded(line
, false);
5660 const Sci::Line lineMaxSubord
= pdoc
->GetLastChild(line
);
5661 if (lineMaxSubord
> line
) {
5662 pcs
->SetVisible(line
+ 1, lineMaxSubord
, false);
5664 line
= lineMaxSubord
;
5667 } else if (contractAll
) {
5668 SetFoldExpanded(line
, false);
5677 void Editor::FoldChanged(Sci::Line line
, FoldLevel levelNow
, FoldLevel levelPrev
) {
5678 if (LevelIsHeader(levelNow
)) {
5679 if (!LevelIsHeader(levelPrev
)) {
5680 // Adding a fold point.
5681 if (pcs
->SetExpanded(line
, true)) {
5684 FoldExpand(line
, FoldAction::Expand
, levelPrev
);
5686 } else if (LevelIsHeader(levelPrev
)) {
5687 const Sci::Line prevLine
= line
- 1;
5688 const FoldLevel prevLineLevel
= pdoc
->GetFoldLevel(prevLine
);
5690 // Combining two blocks where the first block is collapsed (e.g. by deleting the line(s) which separate(s) the two blocks)
5691 if ((LevelNumber(prevLineLevel
) == LevelNumber(levelNow
)) && !pcs
->GetVisible(prevLine
))
5692 FoldLine(pdoc
->GetFoldParent(prevLine
), FoldAction::Expand
);
5694 if (!pcs
->GetExpanded(line
)) {
5695 // Removing the fold from one that has been contracted so should expand
5696 // otherwise lines are left invisible with no way to make them visible
5697 if (pcs
->SetExpanded(line
, true)) {
5700 // Combining two blocks where the second one is collapsed (e.g. by adding characters in the line which separates the two blocks)
5701 FoldExpand(line
, FoldAction::Expand
, levelPrev
);
5704 if (!LevelIsWhitespace(levelNow
) &&
5705 (LevelNumber(levelPrev
) > LevelNumber(levelNow
))) {
5706 if (pcs
->HiddenLines()) {
5707 // See if should still be hidden
5708 const Sci::Line parentLine
= pdoc
->GetFoldParent(line
);
5709 if ((parentLine
< 0) || (pcs
->GetExpanded(parentLine
) && pcs
->GetVisible(parentLine
))) {
5710 pcs
->SetVisible(line
, line
, true);
5717 // Combining two blocks where the first one is collapsed (e.g. by adding characters in the line which separates the two blocks)
5718 if (!LevelIsWhitespace(levelNow
) && (LevelNumber(levelPrev
) < LevelNumber(levelNow
))) {
5719 if (pcs
->HiddenLines()) {
5720 const Sci::Line parentLine
= pdoc
->GetFoldParent(line
);
5721 if (!pcs
->GetExpanded(parentLine
) && pcs
->GetVisible(line
))
5722 FoldLine(parentLine
, FoldAction::Expand
);
5727 void Editor::NeedShown(Sci::Position pos
, Sci::Position len
) {
5728 if (FlagSet(foldAutomatic
, AutomaticFold::Show
)) {
5729 const Sci::Line lineStart
= pdoc
->SciLineFromPosition(pos
);
5730 const Sci::Line lineEnd
= pdoc
->SciLineFromPosition(pos
+len
);
5731 for (Sci::Line line
= lineStart
; line
<= lineEnd
; line
++) {
5732 EnsureLineVisible(line
, false);
5735 NotifyNeedShown(pos
, len
);
5739 Sci::Position
Editor::GetTag(char *tagValue
, int tagNumber
) {
5740 const char *text
= nullptr;
5741 Sci::Position length
= 0;
5742 if ((tagNumber
>= 1) && (tagNumber
<= 9)) {
5743 char name
[3] = "\\?";
5744 name
[1] = static_cast<char>(tagNumber
+ '0');
5746 text
= pdoc
->SubstituteByPosition(name
, &length
);
5750 memcpy(tagValue
, text
, length
+ 1);
5757 Sci::Position
Editor::ReplaceTarget(ReplaceType replaceType
, std::string_view text
) {
5759 if (replaceType
== ReplaceType::patterns
) {
5760 Sci::Position length
= text
.length();
5761 const char *p
= pdoc
->SubstituteByPosition(text
.data(), &length
);
5765 text
= std::string_view(p
, length
);
5768 if (replaceType
== ReplaceType::minimal
) {
5769 // Check for prefix and suffix and reduce text and target to match.
5770 // This is performed with Range which doesn't support virtual space.
5771 Range
range(targetRange
.start
.Position(), targetRange
.end
.Position());
5772 pdoc
->TrimReplacement(text
, range
);
5773 // Re-apply virtual space to start if start position didn't change.
5774 // Don't bother with end as its virtual space is not used
5775 const SelectionPosition
start(range
.start
== targetRange
.start
.Position() ?
5776 targetRange
.start
: SelectionPosition(range
.start
));
5777 targetRange
= SelectionSegment(start
, SelectionPosition(range
.end
));
5780 // Remove the text inside the range
5781 if (targetRange
.Length() > 0)
5782 pdoc
->DeleteChars(targetRange
.start
.Position(), targetRange
.Length());
5783 targetRange
.end
= targetRange
.start
;
5785 // Realize virtual space of target start
5786 const Sci::Position startAfterSpaceInsertion
= RealizeVirtualSpace(targetRange
.start
.Position(), targetRange
.start
.VirtualSpace());
5787 targetRange
.start
.SetPosition(startAfterSpaceInsertion
);
5788 targetRange
.end
= targetRange
.start
;
5790 // Insert the new text
5791 const Sci::Position lengthInserted
= pdoc
->InsertString(targetRange
.start
.Position(), text
);
5792 targetRange
.end
.SetPosition(targetRange
.start
.Position() + lengthInserted
);
5793 return text
.length();
5796 bool Editor::IsUnicodeMode() const noexcept
{
5797 return pdoc
&& (CpUtf8
== pdoc
->dbcsCodePage
);
5800 int Editor::CodePage() const noexcept
{
5802 return pdoc
->dbcsCodePage
;
5807 std::unique_ptr
<Surface
> Editor::CreateMeasurementSurface() const {
5808 if (!wMain
.GetID()) {
5811 std::unique_ptr
<Surface
> surf
= Surface::Allocate(technology
);
5812 surf
->Init(wMain
.GetID());
5813 surf
->SetMode(CurrentSurfaceMode());
5817 std::unique_ptr
<Surface
> Editor::CreateDrawingSurface(SurfaceID sid
, std::optional
<Scintilla::Technology
> technologyOpt
) const {
5818 if (!wMain
.GetID()) {
5821 std::unique_ptr
<Surface
> surf
= Surface::Allocate(technologyOpt
? *technologyOpt
: technology
);
5822 surf
->Init(sid
, wMain
.GetID());
5823 surf
->SetMode(CurrentSurfaceMode());
5827 Sci::Line
Editor::WrapCount(Sci::Line line
) {
5828 AutoSurface
surface(this);
5829 std::shared_ptr
<LineLayout
> ll
= view
.RetrieveLineLayout(line
, *this);
5831 if (surface
&& ll
) {
5832 view
.LayoutLine(*this, surface
, vs
, ll
.get(), wrapWidth
);
5839 void Editor::AddStyledText(const char *buffer
, Sci::Position appendLength
) {
5840 // The buffer consists of alternating character bytes and style bytes
5841 const Sci::Position textLength
= appendLength
/ 2;
5842 std::string
text(textLength
, '\0');
5843 for (Sci::Position i
= 0; i
< textLength
; i
++) {
5844 text
[i
] = buffer
[i
*2];
5846 const Sci::Position lengthInserted
= pdoc
->InsertString(CurrentPosition(), text
);
5847 for (Sci::Position i
= 0; i
< textLength
; i
++) {
5848 text
[i
] = buffer
[i
*2+1];
5850 pdoc
->StartStyling(CurrentPosition());
5851 pdoc
->SetStyles(textLength
, text
.c_str());
5852 SetEmptySelection(sel
.MainCaret() + lengthInserted
);
5855 Sci::Position
Editor::GetStyledText(char *buffer
, Sci::Position cpMin
, Sci::Position cpMax
) const noexcept
{
5856 Sci::Position iPlace
= 0;
5857 for (Sci::Position iChar
= cpMin
; iChar
< cpMax
; iChar
++) {
5858 buffer
[iPlace
++] = pdoc
->CharAt(iChar
);
5859 buffer
[iPlace
++] = pdoc
->StyleAtNoExcept(iChar
);
5861 buffer
[iPlace
] = '\0';
5862 buffer
[iPlace
+ 1] = '\0';
5866 Sci::Position
Editor::GetTextRange(char *buffer
, Sci::Position cpMin
, Sci::Position cpMax
) const {
5867 const Sci::Position cpEnd
= (cpMax
== -1) ? pdoc
->Length() : cpMax
;
5868 PLATFORM_ASSERT(cpEnd
<= pdoc
->Length());
5869 const Sci::Position len
= cpEnd
- cpMin
; // No -1 as cpMin and cpMax are referring to inter character positions
5870 pdoc
->GetCharRange(buffer
, cpMin
, len
);
5871 // Spec says copied text is terminated with a NUL
5873 return len
; // Not including NUL
5876 bool Editor::ValidMargin(uptr_t wParam
) const noexcept
{
5877 return wParam
< vs
.ms
.size();
5880 void Editor::StyleSetMessage(Message iMessage
, uptr_t wParam
, sptr_t lParam
) {
5881 vs
.EnsureStyle(wParam
);
5883 case Message::StyleSetFore
:
5884 vs
.styles
[wParam
].fore
= ColourRGBA::FromIpRGB(lParam
);
5886 case Message::StyleSetBack
:
5887 vs
.styles
[wParam
].back
= ColourRGBA::FromIpRGB(lParam
);
5889 case Message::StyleSetBold
:
5890 vs
.styles
[wParam
].weight
= lParam
!= 0 ? FontWeight::Bold
: FontWeight::Normal
;
5892 case Message::StyleSetWeight
:
5893 vs
.styles
[wParam
].weight
= static_cast<FontWeight
>(lParam
);
5895 case Message::StyleSetItalic
:
5896 vs
.styles
[wParam
].italic
= lParam
!= 0;
5898 case Message::StyleSetEOLFilled
:
5899 vs
.styles
[wParam
].eolFilled
= lParam
!= 0;
5901 case Message::StyleSetSize
:
5902 vs
.styles
[wParam
].size
= static_cast<int>(lParam
* FontSizeMultiplier
);
5904 case Message::StyleSetSizeFractional
:
5905 vs
.styles
[wParam
].size
= static_cast<int>(lParam
);
5907 case Message::StyleSetFont
:
5909 vs
.SetStyleFontName(static_cast<int>(wParam
), ConstCharPtrFromSPtr(lParam
));
5912 case Message::StyleSetUnderline
:
5913 vs
.styles
[wParam
].underline
= lParam
!= 0;
5915 case Message::StyleSetCase
:
5916 vs
.styles
[wParam
].caseForce
= static_cast<Style::CaseForce
>(lParam
);
5918 case Message::StyleSetCharacterSet
:
5919 vs
.styles
[wParam
].characterSet
= static_cast<CharacterSet
>(lParam
);
5920 pdoc
->SetCaseFolder(nullptr);
5922 case Message::StyleSetVisible
:
5923 vs
.styles
[wParam
].visible
= lParam
!= 0;
5925 case Message::StyleSetInvisibleRepresentation
: {
5926 const char *utf8
= ConstCharPtrFromSPtr(lParam
);
5927 char *rep
= vs
.styles
[wParam
].invisibleRepresentation
;
5928 const int classified
= UTF8Classify(utf8
);
5929 if (!(classified
& UTF8MaskInvalid
)) {
5931 const int len
= classified
& UTF8MaskWidth
;
5932 for (int i
=0; i
<len
&& i
<UTF8MaxBytes
; i
++)
5938 case Message::StyleSetChangeable
:
5939 vs
.styles
[wParam
].changeable
= lParam
!= 0;
5941 case Message::StyleSetHotSpot
:
5942 vs
.styles
[wParam
].hotspot
= lParam
!= 0;
5944 case Message::StyleSetCheckMonospaced
:
5945 vs
.styles
[wParam
].checkMonospaced
= lParam
!= 0;
5950 InvalidateStyleRedraw();
5953 sptr_t
Editor::StyleGetMessage(Message iMessage
, uptr_t wParam
, sptr_t lParam
) {
5954 vs
.EnsureStyle(wParam
);
5956 case Message::StyleGetFore
:
5957 return vs
.styles
[wParam
].fore
.OpaqueRGB();
5958 case Message::StyleGetBack
:
5959 return vs
.styles
[wParam
].back
.OpaqueRGB();
5960 case Message::StyleGetBold
:
5961 return vs
.styles
[wParam
].weight
> FontWeight::Normal
;
5962 case Message::StyleGetWeight
:
5963 return static_cast<sptr_t
>(vs
.styles
[wParam
].weight
);
5964 case Message::StyleGetItalic
:
5965 return vs
.styles
[wParam
].italic
? 1 : 0;
5966 case Message::StyleGetEOLFilled
:
5967 return vs
.styles
[wParam
].eolFilled
? 1 : 0;
5968 case Message::StyleGetSize
:
5969 return vs
.styles
[wParam
].size
/ FontSizeMultiplier
;
5970 case Message::StyleGetSizeFractional
:
5971 return vs
.styles
[wParam
].size
;
5972 case Message::StyleGetFont
:
5973 return StringResult(lParam
, vs
.styles
[wParam
].fontName
);
5974 case Message::StyleGetUnderline
:
5975 return vs
.styles
[wParam
].underline
? 1 : 0;
5976 case Message::StyleGetCase
:
5977 return static_cast<int>(vs
.styles
[wParam
].caseForce
);
5978 case Message::StyleGetCharacterSet
:
5979 return static_cast<sptr_t
>(vs
.styles
[wParam
].characterSet
);
5980 case Message::StyleGetVisible
:
5981 return vs
.styles
[wParam
].visible
? 1 : 0;
5982 case Message::StyleGetChangeable
:
5983 return vs
.styles
[wParam
].changeable
? 1 : 0;
5984 case Message::StyleGetInvisibleRepresentation
:
5985 return StringResult(lParam
, vs
.styles
[wParam
].invisibleRepresentation
);
5986 case Message::StyleGetHotSpot
:
5987 return vs
.styles
[wParam
].hotspot
? 1 : 0;
5988 case Message::StyleGetCheckMonospaced
:
5989 return vs
.styles
[wParam
].checkMonospaced
? 1 : 0;
5996 void Editor::SetSelectionNMessage(Message iMessage
, uptr_t wParam
, sptr_t lParam
) {
5997 if (wParam
>= sel
.Count()) {
6000 InvalidateRange(sel
.Range(wParam
).Start().Position(), sel
.Range(wParam
).End().Position());
6003 case Message::SetSelectionNCaret
:
6004 sel
.Range(wParam
).caret
.SetPosition(lParam
);
6007 case Message::SetSelectionNAnchor
:
6008 sel
.Range(wParam
).anchor
.SetPosition(lParam
);
6011 case Message::SetSelectionNCaretVirtualSpace
:
6012 sel
.Range(wParam
).caret
.SetVirtualSpace(lParam
);
6015 case Message::SetSelectionNAnchorVirtualSpace
:
6016 sel
.Range(wParam
).anchor
.SetVirtualSpace(lParam
);
6019 case Message::SetSelectionNStart
:
6020 sel
.Range(wParam
).anchor
.SetPosition(lParam
);
6023 case Message::SetSelectionNEnd
:
6024 sel
.Range(wParam
).caret
.SetPosition(lParam
);
6032 InvalidateRange(sel
.Range(wParam
).Start().Position(), sel
.Range(wParam
).End().Position());
6033 ContainerNeedsUpdate(Update::Selection
);
6036 sptr_t
Editor::StringResult(sptr_t lParam
, const char *val
) noexcept
{
6037 const size_t len
= val
? strlen(val
) : 0;
6039 char *ptr
= CharPtrFromSPtr(lParam
);
6041 memcpy(ptr
, val
, len
+1);
6045 return len
; // Not including NUL
6048 sptr_t
Editor::BytesResult(sptr_t lParam
, const unsigned char *val
, size_t len
) noexcept
{
6049 // No NUL termination: len is number of valid/displayed bytes
6050 if ((lParam
) && (len
> 0)) {
6051 char *ptr
= CharPtrFromSPtr(lParam
);
6053 memcpy(ptr
, val
, len
);
6057 return val
? len
: 0;
6060 sptr_t
Editor::WndProc(Message iMessage
, uptr_t wParam
, sptr_t lParam
) {
6061 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
6063 // Optional macro recording hook
6065 NotifyMacroRecord(iMessage
, wParam
, lParam
);
6069 case Message::GetText
: {
6071 return pdoc
->Length();
6072 char *ptr
= CharPtrFromSPtr(lParam
);
6073 const Sci_Position len
= std::min
<Sci_Position
>(wParam
, pdoc
->Length());
6074 pdoc
->GetCharRange(ptr
, 0, len
);
6079 case Message::SetText
: {
6083 pdoc
->DeleteChars(0, pdoc
->Length());
6084 SetEmptySelection(0);
6085 const char *text
= ConstCharPtrFromSPtr(lParam
);
6086 pdoc
->InsertString(0, text
, strlen(text
));
6090 case Message::GetTextLength
:
6091 return pdoc
->Length();
6102 case Message::CopyAllowLine
:
6106 case Message::VerticalCentreCaret
:
6107 VerticalCentreCaret();
6110 case Message::MoveSelectedLinesUp
:
6111 MoveSelectedLinesUp();
6114 case Message::MoveSelectedLinesDown
:
6115 MoveSelectedLinesDown();
6118 case Message::CopyRange
:
6119 CopyRangeToClipboard(PositionFromUPtr(wParam
), lParam
);
6122 case Message::CopyText
:
6123 CopyText(wParam
, ConstCharPtrFromSPtr(lParam
));
6126 case Message::Paste
:
6128 if ((caretSticky
== CaretSticky::Off
) || (caretSticky
== CaretSticky::WhiteSpace
)) {
6131 EnsureCaretVisible();
6134 case Message::ReplaceRectangular
: {
6137 ClearSelection(); // want to replace rectangular selection contents
6139 InsertPasteShape(ConstCharPtrFromSPtr(lParam
), PositionFromUPtr(wParam
), PasteShape::rectangular
);
6143 case Message::Clear
:
6146 EnsureCaretVisible();
6154 case Message::CanUndo
:
6155 return (pdoc
->CanUndo() && !pdoc
->IsReadOnly()) ? 1 : 0;
6157 case Message::EmptyUndoBuffer
:
6158 pdoc
->DeleteUndoHistory();
6161 case Message::GetFirstVisibleLine
:
6164 case Message::SetFirstVisibleLine
:
6165 ScrollTo(LineFromUPtr(wParam
));
6168 case Message::GetLine
: { // Risk of overwriting the end of the buffer
6169 const Sci::Position lineStart
=
6170 pdoc
->LineStart(LineFromUPtr(wParam
));
6171 const Sci::Position lineEnd
=
6172 pdoc
->LineStart(LineFromUPtr(wParam
+ 1));
6173 // not NUL terminated
6174 const Sci::Position len
= lineEnd
- lineStart
;
6178 char *ptr
= CharPtrFromSPtr(lParam
);
6179 pdoc
->GetCharRange(ptr
, lineStart
, len
);
6183 case Message::GetLineCount
:
6184 if (pdoc
->LinesTotal() == 0)
6187 return pdoc
->LinesTotal();
6189 case Message::AllocateLines
:
6190 pdoc
->AllocateLines(wParam
);
6193 case Message::GetModify
:
6194 return !pdoc
->IsSavePoint();
6196 case Message::SetSel
: {
6197 Sci::Position nStart
= PositionFromUPtr(wParam
);
6198 Sci::Position nEnd
= lParam
;
6200 nEnd
= pdoc
->Length();
6202 nStart
= nEnd
; // Remove selection
6203 InvalidateSelection(SelectionRange(nStart
, nEnd
));
6205 sel
.selType
= Selection::SelTypes::stream
;
6206 SetSelection(nEnd
, nStart
);
6207 EnsureCaretVisible();
6211 case Message::GetSelText
: {
6212 SelectionText selectedText
;
6213 CopySelectionRange(&selectedText
);
6215 char *ptr
= CharPtrFromSPtr(lParam
);
6216 size_t iChar
= selectedText
.Length();
6218 memcpy(ptr
, selectedText
.Data(), iChar
);
6222 return selectedText
.Length();
6225 case Message::LineFromPosition
:
6226 if (PositionFromUPtr(wParam
) < 0)
6228 return pdoc
->LineFromPosition(PositionFromUPtr(wParam
));
6230 case Message::PositionFromLine
:
6231 if (LineFromUPtr(wParam
) < 0)
6232 wParam
= pdoc
->LineFromPosition(SelectionStart().Position());
6234 return 0; // Even if there is no text, there is a first line that starts at 0
6235 if (LineFromUPtr(wParam
) > pdoc
->LinesTotal())
6237 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
6239 return pdoc
->LineStart(LineFromUPtr(wParam
));
6241 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
6242 case Message::LineLength
:
6243 if ((LineFromUPtr(wParam
) < 0) ||
6244 (LineFromUPtr(wParam
) > pdoc
->LineFromPosition(pdoc
->Length())))
6246 return pdoc
->LineStart(LineFromUPtr(wParam
) + 1) - pdoc
->LineStart(LineFromUPtr(wParam
));
6248 case Message::ReplaceSel
: {
6253 const char *replacement
= ConstCharPtrFromSPtr(lParam
);
6254 const Sci::Position lengthInserted
= pdoc
->InsertString(
6255 sel
.MainCaret(), replacement
, strlen(replacement
));
6256 SetEmptySelection(sel
.MainCaret() + lengthInserted
);
6258 EnsureCaretVisible();
6262 case Message::SetTargetStart
:
6263 targetRange
.start
.SetPosition(PositionFromUPtr(wParam
));
6266 case Message::GetTargetStart
:
6267 return targetRange
.start
.Position();
6269 case Message::SetTargetStartVirtualSpace
:
6270 targetRange
.start
.SetVirtualSpace(PositionFromUPtr(wParam
));
6273 case Message::GetTargetStartVirtualSpace
:
6274 return targetRange
.start
.VirtualSpace();
6276 case Message::SetTargetEnd
:
6277 targetRange
.end
.SetPosition(PositionFromUPtr(wParam
));
6280 case Message::GetTargetEnd
:
6281 return targetRange
.end
.Position();
6283 case Message::SetTargetEndVirtualSpace
:
6284 targetRange
.end
.SetVirtualSpace(PositionFromUPtr(wParam
));
6287 case Message::GetTargetEndVirtualSpace
:
6288 return targetRange
.end
.VirtualSpace();
6290 case Message::SetTargetRange
:
6291 targetRange
.start
.SetPosition(PositionFromUPtr(wParam
));
6292 targetRange
.end
.SetPosition(lParam
);
6295 case Message::TargetWholeDocument
:
6296 targetRange
.start
.SetPosition(0);
6297 targetRange
.end
.SetPosition(pdoc
->Length());
6300 case Message::TargetFromSelection
:
6301 targetRange
.start
= sel
.RangeMain().Start();
6302 targetRange
.end
= sel
.RangeMain().End();
6305 case Message::GetTargetText
: {
6306 std::string text
= RangeText(targetRange
.start
.Position(), targetRange
.end
.Position());
6307 return BytesResult(lParam
, reinterpret_cast<const unsigned char *>(text
.c_str()), text
.length());
6310 case Message::ReplaceTarget
:
6311 PLATFORM_ASSERT(lParam
);
6312 return ReplaceTarget(ReplaceType::basic
, ViewFromParams(lParam
, wParam
));
6314 case Message::ReplaceTargetRE
:
6315 PLATFORM_ASSERT(lParam
);
6316 return ReplaceTarget(ReplaceType::patterns
, ViewFromParams(lParam
, wParam
));
6318 case Message::ReplaceTargetMinimal
:
6319 PLATFORM_ASSERT(lParam
);
6320 return ReplaceTarget(ReplaceType::minimal
, ViewFromParams(lParam
, wParam
));
6322 case Message::SearchInTarget
:
6323 PLATFORM_ASSERT(lParam
);
6324 return SearchInTarget(ConstCharPtrFromSPtr(lParam
), PositionFromUPtr(wParam
));
6326 case Message::SetSearchFlags
:
6327 searchFlags
= static_cast<FindOption
>(wParam
);
6330 case Message::GetSearchFlags
:
6331 return static_cast<sptr_t
>(searchFlags
);
6333 case Message::GetTag
:
6334 return GetTag(CharPtrFromSPtr(lParam
), static_cast<int>(wParam
));
6336 case Message::PositionBefore
:
6337 return pdoc
->MovePositionOutsideChar(PositionFromUPtr(wParam
) - 1, -1, true);
6339 case Message::PositionAfter
:
6340 return pdoc
->MovePositionOutsideChar(PositionFromUPtr(wParam
) + 1, 1, true);
6342 case Message::PositionRelative
:
6343 return std::clamp
<Sci::Position
>(pdoc
->GetRelativePosition(
6344 PositionFromUPtr(wParam
), lParam
),
6347 case Message::PositionRelativeCodeUnits
:
6348 return std::clamp
<Sci::Position
>(pdoc
->GetRelativePositionUTF16(
6349 PositionFromUPtr(wParam
), lParam
),
6352 case Message::LineScroll
:
6353 ScrollTo(topLine
+ lParam
);
6354 HorizontalScrollTo(xOffset
+ static_cast<int>(static_cast<int>(wParam
) * vs
.spaceWidth
));
6357 case Message::SetXOffset
:
6358 xOffset
= static_cast<int>(wParam
);
6359 ContainerNeedsUpdate(Update::HScroll
);
6360 SetHorizontalScrollPos();
6364 case Message::GetXOffset
:
6367 case Message::ChooseCaretX
:
6371 case Message::ScrollCaret
:
6372 EnsureCaretVisible();
6375 case Message::SetReadOnly
:
6376 pdoc
->SetReadOnly(wParam
!= 0);
6379 case Message::GetReadOnly
:
6380 return pdoc
->IsReadOnly();
6382 case Message::CanPaste
:
6385 case Message::PointXFromPosition
:
6389 const Point pt
= LocationFromPosition(lParam
);
6390 // Convert to view-relative
6391 return static_cast<int>(pt
.x
) - vs
.textStart
+ vs
.fixedColumnWidth
;
6394 case Message::PointYFromPosition
:
6398 const Point pt
= LocationFromPosition(lParam
);
6399 return static_cast<int>(pt
.y
);
6402 case Message::FindText
:
6403 return FindText(wParam
, lParam
);
6405 case Message::FindTextFull
:
6406 return FindTextFull(wParam
, lParam
);
6408 case Message::GetTextRange
:
6409 if (TextRange
*tr
= static_cast<TextRange
*>(PtrFromSPtr(lParam
))) {
6410 return GetTextRange(tr
->lpstrText
, tr
->chrg
.cpMin
, tr
->chrg
.cpMax
);
6414 case Message::GetTextRangeFull
:
6415 if (TextRangeFull
*tr
= static_cast<TextRangeFull
*>(PtrFromSPtr(lParam
))) {
6416 return GetTextRange(tr
->lpstrText
, tr
->chrg
.cpMin
, tr
->chrg
.cpMax
);
6420 case Message::HideSelection
:
6421 vs
.selection
.visible
= wParam
== 0;
6425 case Message::GetSelectionHidden
:
6426 return !vs
.selection
.visible
;
6429 case Message::FormatRange
:
6430 case Message::FormatRangeFull
:
6431 return FormatRange(iMessage
, wParam
, lParam
);
6433 case Message::GetMarginLeft
:
6434 return vs
.leftMarginWidth
;
6436 case Message::GetMarginRight
:
6437 return vs
.rightMarginWidth
;
6439 case Message::SetMarginLeft
:
6440 lastXChosen
+= static_cast<int>(lParam
) - vs
.leftMarginWidth
;
6441 vs
.leftMarginWidth
= static_cast<int>(lParam
);
6442 InvalidateStyleRedraw();
6445 case Message::SetMarginRight
:
6446 vs
.rightMarginWidth
= static_cast<int>(lParam
);
6447 InvalidateStyleRedraw();
6450 // Control specific messages
6452 case Message::AddText
: {
6455 const Sci::Position lengthInserted
= pdoc
->InsertString(
6456 CurrentPosition(), ConstCharPtrFromSPtr(lParam
), PositionFromUPtr(wParam
));
6457 SetEmptySelection(sel
.MainCaret() + lengthInserted
);
6461 case Message::AddStyledText
:
6463 AddStyledText(ConstCharPtrFromSPtr(lParam
), PositionFromUPtr(wParam
));
6466 case Message::InsertText
: {
6469 Sci::Position insertPos
= PositionFromUPtr(wParam
);
6470 if (insertPos
== -1)
6471 insertPos
= CurrentPosition();
6472 Sci::Position newCurrent
= CurrentPosition();
6473 const char *sz
= ConstCharPtrFromSPtr(lParam
);
6474 const Sci::Position lengthInserted
= pdoc
->InsertString(insertPos
, sz
, strlen(sz
));
6475 if (newCurrent
> insertPos
)
6476 newCurrent
+= lengthInserted
;
6477 SetEmptySelection(newCurrent
);
6481 case Message::ChangeInsertion
:
6482 PLATFORM_ASSERT(lParam
);
6483 pdoc
->ChangeInsertion(ConstCharPtrFromSPtr(lParam
), PositionFromUPtr(wParam
));
6486 case Message::AppendText
:
6487 pdoc
->InsertString(pdoc
->Length(),
6488 ConstCharPtrFromSPtr(lParam
), PositionFromUPtr(wParam
));
6491 case Message::ClearAll
:
6495 case Message::DeleteRange
:
6496 pdoc
->DeleteChars(PositionFromUPtr(wParam
), lParam
);
6499 case Message::ClearDocumentStyle
:
6500 ClearDocumentStyle();
6503 case Message::SetUndoCollection
:
6504 pdoc
->SetUndoCollection(wParam
!= 0);
6507 case Message::GetUndoCollection
:
6508 return pdoc
->IsCollectingUndo();
6510 case Message::BeginUndoAction
:
6511 pdoc
->BeginUndoAction();
6514 case Message::EndUndoAction
:
6515 pdoc
->EndUndoAction();
6518 case Message::GetCaretPeriod
:
6519 return caret
.period
;
6521 case Message::SetCaretPeriod
:
6522 CaretSetPeriod(static_cast<int>(wParam
));
6525 case Message::GetWordChars
:
6526 return pdoc
->GetCharsOfClass(CharacterClass::word
, UCharPtrFromSPtr(lParam
));
6528 case Message::SetWordChars
: {
6529 pdoc
->SetDefaultCharClasses(false);
6532 pdoc
->SetCharClasses(ConstUCharPtrFromSPtr(lParam
), CharacterClass::word
);
6536 case Message::GetWhitespaceChars
:
6537 return pdoc
->GetCharsOfClass(CharacterClass::space
, UCharPtrFromSPtr(lParam
));
6539 case Message::SetWhitespaceChars
: {
6542 pdoc
->SetCharClasses(ConstUCharPtrFromSPtr(lParam
), CharacterClass::space
);
6546 case Message::GetPunctuationChars
:
6547 return pdoc
->GetCharsOfClass(CharacterClass::punctuation
, UCharPtrFromSPtr(lParam
));
6549 case Message::SetPunctuationChars
: {
6552 pdoc
->SetCharClasses(ConstUCharPtrFromSPtr(lParam
), CharacterClass::punctuation
);
6556 case Message::SetCharsDefault
:
6557 pdoc
->SetDefaultCharClasses(true);
6560 case Message::SetCharacterCategoryOptimization
:
6561 pdoc
->SetCharacterCategoryOptimization(static_cast<int>(wParam
));
6564 case Message::GetCharacterCategoryOptimization
:
6565 return pdoc
->CharacterCategoryOptimization();
6567 case Message::GetLength
:
6568 return pdoc
->Length();
6570 case Message::Allocate
:
6571 pdoc
->Allocate(PositionFromUPtr(wParam
));
6574 case Message::GetCharAt
:
6575 return pdoc
->CharAt(PositionFromUPtr(wParam
));
6577 case Message::SetCurrentPos
:
6578 if (sel
.IsRectangular()) {
6579 sel
.Rectangular().caret
.SetPosition(PositionFromUPtr(wParam
));
6580 SetRectangularRange();
6583 SetSelection(PositionFromUPtr(wParam
), sel
.MainAnchor());
6587 case Message::GetCurrentPos
:
6588 return sel
.IsRectangular() ? sel
.Rectangular().caret
.Position() : sel
.MainCaret();
6590 case Message::SetAnchor
:
6591 if (sel
.IsRectangular()) {
6592 sel
.Rectangular().anchor
.SetPosition(PositionFromUPtr(wParam
));
6593 SetRectangularRange();
6596 SetSelection(sel
.MainCaret(), PositionFromUPtr(wParam
));
6600 case Message::GetAnchor
:
6601 return sel
.IsRectangular() ? sel
.Rectangular().anchor
.Position() : sel
.MainAnchor();
6603 case Message::SetSelectionStart
:
6604 SetSelection(std::max(sel
.MainCaret(), PositionFromUPtr(wParam
)), PositionFromUPtr(wParam
));
6607 case Message::GetSelectionStart
:
6608 return sel
.LimitsForRectangularElseMain().start
.Position();
6610 case Message::SetSelectionEnd
:
6611 SetSelection(PositionFromUPtr(wParam
), std::min(sel
.MainAnchor(), PositionFromUPtr(wParam
)));
6614 case Message::GetSelectionEnd
:
6615 return sel
.LimitsForRectangularElseMain().end
.Position();
6617 case Message::SetEmptySelection
:
6618 SetEmptySelection(PositionFromUPtr(wParam
));
6621 case Message::SetPrintMagnification
:
6622 view
.printParameters
.magnification
= static_cast<int>(wParam
);
6625 case Message::GetPrintMagnification
:
6626 return view
.printParameters
.magnification
;
6628 case Message::SetPrintColourMode
:
6629 view
.printParameters
.colourMode
= static_cast<PrintOption
>(wParam
);
6632 case Message::GetPrintColourMode
:
6633 return static_cast<sptr_t
>(view
.printParameters
.colourMode
);
6635 case Message::SetPrintWrapMode
:
6636 view
.printParameters
.wrapState
= (static_cast<Wrap
>(wParam
) == Wrap::Word
) ? Wrap::Word
: Wrap::None
;
6639 case Message::GetPrintWrapMode
:
6640 return static_cast<sptr_t
>(view
.printParameters
.wrapState
);
6642 case Message::GetStyleAt
:
6643 if (PositionFromUPtr(wParam
) >= pdoc
->Length())
6646 return pdoc
->StyleAt(PositionFromUPtr(wParam
));
6648 case Message::GetStyleIndexAt
:
6649 if (PositionFromUPtr(wParam
) >= pdoc
->Length())
6652 return pdoc
->StyleIndexAt(PositionFromUPtr(wParam
));
6658 case Message::SelectAll
:
6662 case Message::SetSavePoint
:
6663 pdoc
->SetSavePoint();
6666 case Message::GetStyledText
:
6667 if (TextRange
*tr
= static_cast<TextRange
*>(PtrFromSPtr(lParam
))) {
6668 return GetStyledText(tr
->lpstrText
, tr
->chrg
.cpMin
, tr
->chrg
.cpMax
);
6672 case Message::GetStyledTextFull
:
6673 if (TextRangeFull
*tr
= static_cast<TextRangeFull
*>(PtrFromSPtr(lParam
))) {
6674 return GetStyledText(tr
->lpstrText
, tr
->chrg
.cpMin
, tr
->chrg
.cpMax
);
6678 case Message::CanRedo
:
6679 return (pdoc
->CanRedo() && !pdoc
->IsReadOnly()) ? 1 : 0;
6681 case Message::MarkerLineFromHandle
:
6682 return pdoc
->LineFromHandle(static_cast<int>(wParam
));
6684 case Message::MarkerDeleteHandle
:
6685 pdoc
->DeleteMarkFromHandle(static_cast<int>(wParam
));
6688 case Message::MarkerHandleFromLine
:
6689 return pdoc
->MarkerHandleFromLine(LineFromUPtr(wParam
), static_cast<int>(lParam
));
6691 case Message::MarkerNumberFromLine
:
6692 return pdoc
->MarkerNumberFromLine(LineFromUPtr(wParam
), static_cast<int>(lParam
));
6694 case Message::GetViewWS
:
6695 return static_cast<sptr_t
>(vs
.viewWhitespace
);
6697 case Message::SetViewWS
:
6698 vs
.viewWhitespace
= static_cast<WhiteSpace
>(wParam
);
6702 case Message::GetTabDrawMode
:
6703 return static_cast<sptr_t
>(vs
.tabDrawMode
);
6705 case Message::SetTabDrawMode
:
6706 vs
.tabDrawMode
= static_cast<TabDrawMode
>(wParam
);
6710 case Message::GetWhitespaceSize
:
6711 return vs
.whitespaceSize
;
6713 case Message::SetWhitespaceSize
:
6714 vs
.whitespaceSize
= static_cast<int>(wParam
);
6718 case Message::PositionFromPoint
:
6719 return PositionFromLocation(PointFromParameters(wParam
, lParam
), false, false);
6721 case Message::PositionFromPointClose
:
6722 return PositionFromLocation(PointFromParameters(wParam
, lParam
), true, false);
6724 case Message::CharPositionFromPoint
:
6725 return PositionFromLocation(PointFromParameters(wParam
, lParam
), false, true);
6727 case Message::CharPositionFromPointClose
:
6728 return PositionFromLocation(PointFromParameters(wParam
, lParam
), true, true);
6730 case Message::GotoLine
:
6731 GoToLine(LineFromUPtr(wParam
));
6734 case Message::GotoPos
:
6735 SetEmptySelection(PositionFromUPtr(wParam
));
6736 EnsureCaretVisible();
6739 case Message::GetCurLine
: {
6740 const Sci::Line lineCurrentPos
= pdoc
->SciLineFromPosition(sel
.MainCaret());
6741 const Sci::Position lineStart
= pdoc
->LineStart(lineCurrentPos
);
6742 const Sci::Position lineEnd
= pdoc
->LineStart(lineCurrentPos
+ 1);
6744 return lineEnd
- lineStart
;
6746 char *ptr
= CharPtrFromSPtr(lParam
);
6747 const Sci::Position len
= std::min
<uptr_t
>(lineEnd
- lineStart
, wParam
);
6748 pdoc
->GetCharRange(ptr
, lineStart
, len
);
6750 return sel
.MainCaret() - lineStart
;
6753 case Message::GetEndStyled
:
6754 return pdoc
->GetEndStyled();
6756 case Message::GetEOLMode
:
6757 return static_cast<sptr_t
>(pdoc
->eolMode
);
6759 case Message::SetEOLMode
:
6760 pdoc
->eolMode
= static_cast<EndOfLine
>(wParam
);
6763 case Message::SetLineEndTypesAllowed
:
6764 if (pdoc
->SetLineEndTypesAllowed(static_cast<LineEndType
>(wParam
))) {
6766 pcs
->InsertLines(0, pdoc
->LinesTotal() - 1);
6767 SetAnnotationHeights(0, pdoc
->LinesTotal());
6768 InvalidateStyleRedraw();
6772 case Message::GetLineEndTypesAllowed
:
6773 return static_cast<sptr_t
>(pdoc
->GetLineEndTypesAllowed());
6775 case Message::GetLineEndTypesActive
:
6776 return static_cast<sptr_t
>(pdoc
->GetLineEndTypesActive());
6778 case Message::StartStyling
:
6779 pdoc
->StartStyling(PositionFromUPtr(wParam
));
6782 case Message::SetStyling
:
6783 if (PositionFromUPtr(wParam
) < 0)
6784 errorStatus
= Status::Failure
;
6786 pdoc
->SetStyleFor(PositionFromUPtr(wParam
), static_cast<char>(lParam
));
6789 case Message::SetStylingEx
: // Specify a complete styling buffer
6792 pdoc
->SetStyles(PositionFromUPtr(wParam
), ConstCharPtrFromSPtr(lParam
));
6795 case Message::SetBufferedDraw
:
6796 view
.bufferedDraw
= wParam
!= 0;
6799 case Message::GetBufferedDraw
:
6800 return view
.bufferedDraw
;
6802 #ifdef INCLUDE_DEPRECATED_FEATURES
6803 case SCI_GETTWOPHASEDRAW
:
6804 return view
.phasesDraw
== EditView::phasesTwo
;
6806 case SCI_SETTWOPHASEDRAW
:
6807 if (view
.SetTwoPhaseDraw(wParam
!= 0))
6808 InvalidateStyleRedraw();
6812 case Message::GetPhasesDraw
:
6813 return static_cast<sptr_t
>(view
.phasesDraw
);
6815 case Message::SetPhasesDraw
:
6816 if (view
.SetPhasesDraw(static_cast<int>(wParam
)))
6817 InvalidateStyleRedraw();
6820 case Message::SetFontQuality
:
6821 vs
.extraFontFlag
= static_cast<FontQuality
>(
6822 (static_cast<int>(vs
.extraFontFlag
) & ~static_cast<int>(FontQuality::QualityMask
)) |
6823 (wParam
& static_cast<int>(FontQuality::QualityMask
)));
6824 InvalidateStyleRedraw();
6827 case Message::GetFontQuality
:
6828 return static_cast<int>(vs
.extraFontFlag
) & static_cast<int>(FontQuality::QualityMask
);
6830 case Message::SetTabWidth
:
6832 pdoc
->tabInChars
= static_cast<int>(wParam
);
6833 if (pdoc
->indentInChars
== 0)
6834 pdoc
->actualIndentInChars
= pdoc
->tabInChars
;
6836 InvalidateStyleRedraw();
6839 case Message::GetTabWidth
:
6840 return pdoc
->tabInChars
;
6842 case Message::SetTabMinimumWidth
:
6843 SetAppearance(view
.tabWidthMinimumPixels
, static_cast<int>(wParam
));
6846 case Message::GetTabMinimumWidth
:
6847 return view
.tabWidthMinimumPixels
;
6849 case Message::ClearTabStops
:
6850 if (view
.ClearTabstops(LineFromUPtr(wParam
))) {
6851 const DocModification
mh(ModificationFlags::ChangeTabStops
, 0, 0, 0, nullptr, LineFromUPtr(wParam
));
6852 NotifyModified(pdoc
, mh
, nullptr);
6856 case Message::AddTabStop
:
6857 if (view
.AddTabstop(LineFromUPtr(wParam
), static_cast<int>(lParam
))) {
6858 const DocModification
mh(ModificationFlags::ChangeTabStops
, 0, 0, 0, nullptr, LineFromUPtr(wParam
));
6859 NotifyModified(pdoc
, mh
, nullptr);
6863 case Message::GetNextTabStop
:
6864 return view
.GetNextTabstop(LineFromUPtr(wParam
), static_cast<int>(lParam
));
6866 case Message::SetIndent
:
6867 pdoc
->indentInChars
= static_cast<int>(wParam
);
6868 if (pdoc
->indentInChars
!= 0)
6869 pdoc
->actualIndentInChars
= pdoc
->indentInChars
;
6871 pdoc
->actualIndentInChars
= pdoc
->tabInChars
;
6872 InvalidateStyleRedraw();
6875 case Message::GetIndent
:
6876 return pdoc
->indentInChars
;
6878 case Message::SetUseTabs
:
6879 pdoc
->useTabs
= wParam
!= 0;
6880 InvalidateStyleRedraw();
6883 case Message::GetUseTabs
:
6884 return pdoc
->useTabs
;
6886 case Message::SetLineIndentation
:
6887 pdoc
->SetLineIndentation(LineFromUPtr(wParam
), lParam
);
6890 case Message::GetLineIndentation
:
6891 return pdoc
->GetLineIndentation(LineFromUPtr(wParam
));
6893 case Message::GetLineIndentPosition
:
6894 return pdoc
->GetLineIndentPosition(LineFromUPtr(wParam
));
6896 case Message::SetTabIndents
:
6897 pdoc
->tabIndents
= wParam
!= 0;
6900 case Message::GetTabIndents
:
6901 return pdoc
->tabIndents
;
6903 case Message::SetBackSpaceUnIndents
:
6904 pdoc
->backspaceUnindents
= wParam
!= 0;
6907 case Message::GetBackSpaceUnIndents
:
6908 return pdoc
->backspaceUnindents
;
6910 case Message::SetMouseDwellTime
:
6911 dwellDelay
= static_cast<int>(wParam
);
6912 ticksToDwell
= dwellDelay
;
6915 case Message::GetMouseDwellTime
:
6918 case Message::WordStartPosition
:
6919 return pdoc
->ExtendWordSelect(PositionFromUPtr(wParam
), -1, lParam
!= 0);
6921 case Message::WordEndPosition
:
6922 return pdoc
->ExtendWordSelect(PositionFromUPtr(wParam
), 1, lParam
!= 0);
6924 case Message::IsRangeWord
:
6925 return pdoc
->IsWordAt(PositionFromUPtr(wParam
), lParam
);
6927 case Message::SetIdleStyling
:
6928 idleStyling
= static_cast<IdleStyling
>(wParam
);
6931 case Message::GetIdleStyling
:
6932 return static_cast<sptr_t
>(idleStyling
);
6934 case Message::SetWrapMode
:
6935 if (vs
.SetWrapState(static_cast<Wrap
>(wParam
))) {
6937 ContainerNeedsUpdate(Update::HScroll
);
6938 InvalidateStyleRedraw();
6939 ReconfigureScrollBars();
6943 case Message::GetWrapMode
:
6944 return static_cast<sptr_t
>(vs
.wrap
.state
);
6946 case Message::SetWrapVisualFlags
:
6947 if (vs
.SetWrapVisualFlags(static_cast<WrapVisualFlag
>(wParam
))) {
6948 InvalidateStyleRedraw();
6949 ReconfigureScrollBars();
6953 case Message::GetWrapVisualFlags
:
6954 return static_cast<sptr_t
>(vs
.wrap
.visualFlags
);
6956 case Message::SetWrapVisualFlagsLocation
:
6957 if (vs
.SetWrapVisualFlagsLocation(static_cast<WrapVisualLocation
>(wParam
))) {
6958 InvalidateStyleRedraw();
6962 case Message::GetWrapVisualFlagsLocation
:
6963 return static_cast<sptr_t
>(vs
.wrap
.visualFlagsLocation
);
6965 case Message::SetWrapStartIndent
:
6966 if (vs
.SetWrapVisualStartIndent(static_cast<int>(wParam
))) {
6967 InvalidateStyleRedraw();
6968 ReconfigureScrollBars();
6972 case Message::GetWrapStartIndent
:
6973 return vs
.wrap
.visualStartIndent
;
6975 case Message::SetWrapIndentMode
:
6976 if (vs
.SetWrapIndentMode(static_cast<WrapIndentMode
>(wParam
))) {
6977 InvalidateStyleRedraw();
6978 ReconfigureScrollBars();
6982 case Message::GetWrapIndentMode
:
6983 return static_cast<sptr_t
>(vs
.wrap
.indentMode
);
6985 case Message::SetLayoutCache
:
6986 if (static_cast<LineCache
>(wParam
) <= LineCache::Document
) {
6987 view
.llc
.SetLevel(static_cast<LineCache
>(wParam
));
6991 case Message::GetLayoutCache
:
6992 return static_cast<sptr_t
>(view
.llc
.GetLevel());
6994 case Message::SetPositionCache
:
6995 view
.posCache
->SetSize(wParam
);
6998 case Message::GetPositionCache
:
6999 return view
.posCache
->GetSize();
7001 case Message::SetLayoutThreads
:
7002 view
.SetLayoutThreads(static_cast<unsigned int>(wParam
));
7005 case Message::GetLayoutThreads
:
7006 return view
.GetLayoutThreads();
7008 case Message::SetScrollWidth
:
7009 PLATFORM_ASSERT(wParam
> 0);
7010 if ((wParam
> 0) && (wParam
!= static_cast<unsigned int>(scrollWidth
))) {
7011 view
.lineWidthMaxSeen
= 0;
7012 scrollWidth
= static_cast<int>(wParam
);
7017 case Message::GetScrollWidth
:
7020 case Message::SetScrollWidthTracking
:
7021 trackLineWidth
= wParam
!= 0;
7024 case Message::GetScrollWidthTracking
:
7025 return trackLineWidth
;
7027 case Message::LinesJoin
:
7031 case Message::LinesSplit
:
7032 LinesSplit(static_cast<int>(wParam
));
7035 case Message::TextWidth
:
7036 PLATFORM_ASSERT(wParam
< vs
.styles
.size());
7037 PLATFORM_ASSERT(lParam
);
7038 return TextWidth(wParam
, ConstCharPtrFromSPtr(lParam
));
7040 case Message::TextHeight
:
7042 return vs
.lineHeight
;
7044 case Message::SetEndAtLastLine
:
7045 PLATFORM_ASSERT((wParam
== 0) || (wParam
== 1));
7046 if (endAtLastLine
!= (wParam
!= 0)) {
7047 endAtLastLine
= wParam
!= 0;
7052 case Message::GetEndAtLastLine
:
7053 return endAtLastLine
;
7055 case Message::SetCaretSticky
:
7056 PLATFORM_ASSERT(static_cast<CaretSticky
>(wParam
) <= CaretSticky::WhiteSpace
);
7057 if (static_cast<CaretSticky
>(wParam
) <= CaretSticky::WhiteSpace
) {
7058 caretSticky
= static_cast<CaretSticky
>(wParam
);
7062 case Message::GetCaretSticky
:
7063 return static_cast<sptr_t
>(caretSticky
);
7065 case Message::ToggleCaretSticky
:
7066 caretSticky
= (caretSticky
== CaretSticky::Off
) ? CaretSticky::On
: CaretSticky::Off
;
7069 case Message::GetColumn
:
7070 return pdoc
->GetColumn(PositionFromUPtr(wParam
));
7072 case Message::FindColumn
:
7073 return pdoc
->FindColumn(LineFromUPtr(wParam
), lParam
);
7075 case Message::SetHScrollBar
:
7076 if (horizontalScrollBarVisible
!= (wParam
!= 0)) {
7077 horizontalScrollBarVisible
= wParam
!= 0;
7079 ReconfigureScrollBars();
7083 case Message::GetHScrollBar
:
7084 return horizontalScrollBarVisible
;
7086 case Message::SetVScrollBar
:
7087 if (verticalScrollBarVisible
!= (wParam
!= 0)) {
7088 verticalScrollBarVisible
= wParam
!= 0;
7090 ReconfigureScrollBars();
7091 if (verticalScrollBarVisible
)
7092 SetVerticalScrollPos();
7096 case Message::GetVScrollBar
:
7097 return verticalScrollBarVisible
;
7099 case Message::SetIndentationGuides
:
7100 vs
.viewIndentationGuides
= static_cast<IndentView
>(wParam
);
7104 case Message::GetIndentationGuides
:
7105 return static_cast<sptr_t
>(vs
.viewIndentationGuides
);
7107 case Message::SetHighlightGuide
:
7108 if ((highlightGuideColumn
!= static_cast<int>(wParam
)) || (wParam
> 0)) {
7109 highlightGuideColumn
= static_cast<int>(wParam
);
7114 case Message::GetHighlightGuide
:
7115 return highlightGuideColumn
;
7117 case Message::GetLineEndPosition
:
7118 return pdoc
->LineEnd(LineFromUPtr(wParam
));
7120 case Message::SetCodePage
:
7121 if (ValidCodePage(static_cast<int>(wParam
))) {
7122 if (pdoc
->SetDBCSCodePage(static_cast<int>(wParam
))) {
7124 pcs
->InsertLines(0, pdoc
->LinesTotal() - 1);
7125 SetAnnotationHeights(0, pdoc
->LinesTotal());
7126 InvalidateStyleRedraw();
7127 SetRepresentations();
7132 case Message::GetCodePage
:
7133 return pdoc
->dbcsCodePage
;
7135 case Message::SetIMEInteraction
:
7136 imeInteraction
= static_cast<IMEInteraction
>(wParam
);
7139 case Message::GetIMEInteraction
:
7140 return static_cast<sptr_t
>(imeInteraction
);
7142 case Message::SetBidirectional
:
7143 // Message::SetBidirectional is implemented on platform subclasses if they support bidirectional text.
7146 case Message::GetBidirectional
:
7147 return static_cast<sptr_t
>(bidirectional
);
7149 case Message::GetLineCharacterIndex
:
7150 return static_cast<sptr_t
>(pdoc
->LineCharacterIndex());
7152 case Message::AllocateLineCharacterIndex
:
7153 pdoc
->AllocateLineCharacterIndex(static_cast<LineCharacterIndexType
>(wParam
));
7156 case Message::ReleaseLineCharacterIndex
:
7157 pdoc
->ReleaseLineCharacterIndex(static_cast<LineCharacterIndexType
>(wParam
));
7160 case Message::LineFromIndexPosition
:
7161 return pdoc
->LineFromPositionIndex(PositionFromUPtr(wParam
), static_cast<LineCharacterIndexType
>(lParam
));
7163 case Message::IndexPositionFromLine
:
7164 return pdoc
->IndexLineStart(LineFromUPtr(wParam
), static_cast<LineCharacterIndexType
>(lParam
));
7166 // Marker definition and setting
7167 case Message::MarkerDefine
:
7168 if (wParam
<= MarkerMax
) {
7169 vs
.markers
[wParam
].markType
= static_cast<MarkerSymbol
>(lParam
);
7170 vs
.CalcLargestMarkerHeight();
7172 InvalidateStyleData();
7176 case Message::MarkerSymbolDefined
:
7177 if (wParam
<= MarkerMax
)
7178 return static_cast<sptr_t
>(vs
.markers
[wParam
].markType
);
7182 case Message::MarkerSetFore
:
7183 if (wParam
<= MarkerMax
)
7184 vs
.markers
[wParam
].fore
= ColourRGBA::FromIpRGB(lParam
);
7185 InvalidateStyleData();
7188 case Message::MarkerSetBack
:
7189 if (wParam
<= MarkerMax
)
7190 vs
.markers
[wParam
].back
= ColourRGBA::FromIpRGB(lParam
);
7191 InvalidateStyleData();
7194 case Message::MarkerSetBackSelected
:
7195 if (wParam
<= MarkerMax
)
7196 vs
.markers
[wParam
].backSelected
= ColourRGBA::FromIpRGB(lParam
);
7197 InvalidateStyleData();
7200 case Message::MarkerSetForeTranslucent
:
7201 if (wParam
<= MarkerMax
)
7202 vs
.markers
[wParam
].fore
= ColourRGBA(static_cast<int>(lParam
));
7203 InvalidateStyleData();
7206 case Message::MarkerSetBackTranslucent
:
7207 if (wParam
<= MarkerMax
)
7208 vs
.markers
[wParam
].back
= ColourRGBA(static_cast<int>(lParam
));
7209 InvalidateStyleData();
7212 case Message::MarkerSetBackSelectedTranslucent
:
7213 if (wParam
<= MarkerMax
)
7214 vs
.markers
[wParam
].backSelected
= ColourRGBA(static_cast<int>(lParam
));
7215 InvalidateStyleData();
7218 case Message::MarkerSetStrokeWidth
:
7219 if (wParam
<= MarkerMax
)
7220 vs
.markers
[wParam
].strokeWidth
= lParam
/ 100.0f
;
7221 InvalidateStyleData();
7224 case Message::MarkerEnableHighlight
:
7225 marginView
.highlightDelimiter
.isEnabled
= wParam
== 1;
7228 case Message::MarkerSetAlpha
:
7229 if (wParam
<= MarkerMax
) {
7230 if (static_cast<Alpha
>(lParam
) == Alpha::NoAlpha
) {
7231 SetAppearance(vs
.markers
[wParam
].alpha
, Alpha::Opaque
);
7232 SetAppearance(vs
.markers
[wParam
].layer
, Layer::Base
);
7234 SetAppearance(vs
.markers
[wParam
].alpha
, static_cast<Alpha
>(lParam
));
7235 SetAppearance(vs
.markers
[wParam
].layer
, Layer::OverText
);
7239 case Message::MarkerSetLayer
:
7240 if (wParam
<= MarkerMax
) {
7241 SetAppearance(vs
.markers
[wParam
].layer
, static_cast<Layer
>(lParam
));
7244 case Message::MarkerGetLayer
:
7245 if (wParam
<= MarkerMax
) {
7246 return static_cast<sptr_t
>(vs
.markers
[wParam
].layer
);
7249 case Message::MarkerAdd
: {
7250 const int markerID
= pdoc
->AddMark(LineFromUPtr(wParam
), static_cast<int>(lParam
));
7253 case Message::MarkerAddSet
:
7255 pdoc
->AddMarkSet(LineFromUPtr(wParam
), static_cast<int>(lParam
));
7258 case Message::MarkerDelete
:
7259 pdoc
->DeleteMark(LineFromUPtr(wParam
), static_cast<int>(lParam
));
7262 case Message::MarkerDeleteAll
:
7263 pdoc
->DeleteAllMarks(static_cast<int>(wParam
));
7266 case Message::MarkerGet
:
7267 return GetMark(LineFromUPtr(wParam
));
7269 case Message::MarkerNext
:
7270 return pdoc
->MarkerNext(LineFromUPtr(wParam
), static_cast<int>(lParam
));
7272 case Message::MarkerPrevious
: {
7273 for (Sci::Line iLine
= LineFromUPtr(wParam
); iLine
>= 0; iLine
--) {
7274 if ((GetMark(iLine
) & lParam
) != 0)
7280 case Message::MarkerDefinePixmap
:
7281 if (wParam
<= MarkerMax
) {
7282 vs
.markers
[wParam
].SetXPM(ConstCharPtrFromSPtr(lParam
));
7283 vs
.CalcLargestMarkerHeight();
7285 InvalidateStyleData();
7289 case Message::RGBAImageSetWidth
:
7290 sizeRGBAImage
.x
= static_cast<XYPOSITION
>(wParam
);
7293 case Message::RGBAImageSetHeight
:
7294 sizeRGBAImage
.y
= static_cast<XYPOSITION
>(wParam
);
7297 case Message::RGBAImageSetScale
:
7298 scaleRGBAImage
= static_cast<float>(wParam
);
7301 case Message::MarkerDefineRGBAImage
:
7302 if (wParam
<= MarkerMax
) {
7303 vs
.markers
[wParam
].SetRGBAImage(sizeRGBAImage
, scaleRGBAImage
/ 100.0f
, ConstUCharPtrFromSPtr(lParam
));
7304 vs
.CalcLargestMarkerHeight();
7306 InvalidateStyleData();
7310 case Message::SetMarginTypeN
:
7311 if (ValidMargin(wParam
)) {
7312 vs
.ms
[wParam
].style
= static_cast<MarginType
>(lParam
);
7313 InvalidateStyleRedraw();
7317 case Message::GetMarginTypeN
:
7318 if (ValidMargin(wParam
))
7319 return static_cast<sptr_t
>(vs
.ms
[wParam
].style
);
7323 case Message::SetMarginWidthN
:
7324 if (ValidMargin(wParam
)) {
7325 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
7326 if (vs
.ms
[wParam
].width
!= lParam
) {
7327 lastXChosen
+= static_cast<int>(lParam
) - vs
.ms
[wParam
].width
;
7328 vs
.ms
[wParam
].width
= static_cast<int>(lParam
);
7329 InvalidateStyleRedraw();
7334 case Message::GetMarginWidthN
:
7335 if (ValidMargin(wParam
))
7336 return vs
.ms
[wParam
].width
;
7340 case Message::SetMarginMaskN
:
7341 if (ValidMargin(wParam
)) {
7342 vs
.ms
[wParam
].mask
= static_cast<int>(lParam
);
7343 InvalidateStyleRedraw();
7347 case Message::GetMarginMaskN
:
7348 if (ValidMargin(wParam
))
7349 return vs
.ms
[wParam
].mask
;
7353 case Message::SetMarginSensitiveN
:
7354 if (ValidMargin(wParam
)) {
7355 vs
.ms
[wParam
].sensitive
= lParam
!= 0;
7356 InvalidateStyleRedraw();
7360 case Message::GetMarginSensitiveN
:
7361 if (ValidMargin(wParam
))
7362 return vs
.ms
[wParam
].sensitive
? 1 : 0;
7366 case Message::SetMarginCursorN
:
7367 if (ValidMargin(wParam
))
7368 vs
.ms
[wParam
].cursor
= static_cast<CursorShape
>(lParam
);
7371 case Message::GetMarginCursorN
:
7372 if (ValidMargin(wParam
))
7373 return static_cast<sptr_t
>(vs
.ms
[wParam
].cursor
);
7377 case Message::SetMarginBackN
:
7378 if (ValidMargin(wParam
)) {
7379 vs
.ms
[wParam
].back
= ColourRGBA::FromIpRGB(lParam
);
7380 InvalidateStyleRedraw();
7384 case Message::GetMarginBackN
:
7385 if (ValidMargin(wParam
))
7386 return vs
.ms
[wParam
].back
.OpaqueRGB();
7390 case Message::SetMargins
:
7392 vs
.ms
.resize(wParam
);
7395 case Message::GetMargins
:
7396 return vs
.ms
.size();
7398 case Message::StyleClearAll
:
7400 InvalidateStyleRedraw();
7403 case Message::StyleSetFore
:
7404 case Message::StyleSetBack
:
7405 case Message::StyleSetBold
:
7406 case Message::StyleSetWeight
:
7407 case Message::StyleSetItalic
:
7408 case Message::StyleSetEOLFilled
:
7409 case Message::StyleSetSize
:
7410 case Message::StyleSetSizeFractional
:
7411 case Message::StyleSetFont
:
7412 case Message::StyleSetUnderline
:
7413 case Message::StyleSetCase
:
7414 case Message::StyleSetCharacterSet
:
7415 case Message::StyleSetVisible
:
7416 case Message::StyleSetChangeable
:
7417 case Message::StyleSetHotSpot
:
7418 case Message::StyleSetCheckMonospaced
:
7419 case Message::StyleSetInvisibleRepresentation
:
7420 StyleSetMessage(iMessage
, wParam
, lParam
);
7423 case Message::StyleGetFore
:
7424 case Message::StyleGetBack
:
7425 case Message::StyleGetBold
:
7426 case Message::StyleGetWeight
:
7427 case Message::StyleGetItalic
:
7428 case Message::StyleGetEOLFilled
:
7429 case Message::StyleGetSize
:
7430 case Message::StyleGetSizeFractional
:
7431 case Message::StyleGetFont
:
7432 case Message::StyleGetUnderline
:
7433 case Message::StyleGetCase
:
7434 case Message::StyleGetCharacterSet
:
7435 case Message::StyleGetVisible
:
7436 case Message::StyleGetChangeable
:
7437 case Message::StyleGetHotSpot
:
7438 case Message::StyleGetCheckMonospaced
:
7439 case Message::StyleGetInvisibleRepresentation
:
7440 return StyleGetMessage(iMessage
, wParam
, lParam
);
7442 case Message::StyleResetDefault
:
7443 vs
.ResetDefaultStyle();
7444 InvalidateStyleRedraw();
7447 case Message::SetElementColour
:
7448 if (vs
.SetElementColour(static_cast<Element
>(wParam
), ColourRGBA(static_cast<int>(lParam
)))) {
7449 InvalidateStyleRedraw();
7453 case Message::GetElementColour
:
7454 return vs
.ElementColour(static_cast<Element
>(wParam
)).value_or(ColourRGBA()).AsInteger();
7456 case Message::ResetElementColour
:
7457 if (vs
.ResetElement(static_cast<Element
>(wParam
))) {
7458 InvalidateStyleRedraw();
7462 case Message::GetElementIsSet
:
7463 return vs
.ElementColour(static_cast<Element
>(wParam
)).has_value();
7465 case Message::GetElementAllowsTranslucent
:
7466 return vs
.ElementAllowsTranslucent(static_cast<Element
>(wParam
));
7468 case Message::GetElementBaseColour
:
7469 return vs
.elementBaseColours
[static_cast<Element
>(wParam
)].value_or(ColourRGBA()).AsInteger();
7471 case Message::SetFontLocale
:
7473 vs
.SetFontLocaleName(ConstCharPtrFromSPtr(lParam
));
7474 InvalidateStyleRedraw();
7478 case Message::GetFontLocale
:
7479 return StringResult(lParam
, vs
.localeName
.c_str());
7481 #ifdef INCLUDE_DEPRECATED_FEATURES
7482 case SCI_SETSTYLEBITS
:
7483 vs
.EnsureStyle(0xff);
7486 case SCI_GETSTYLEBITS
:
7490 case Message::SetLineState
:
7491 return pdoc
->SetLineState(LineFromUPtr(wParam
), static_cast<int>(lParam
));
7493 case Message::GetLineState
:
7494 return pdoc
->GetLineState(LineFromUPtr(wParam
));
7496 case Message::GetMaxLineState
:
7497 return pdoc
->GetMaxLineState();
7499 case Message::GetCaretLineVisible
:
7500 return vs
.ElementColour(Element::CaretLineBack
) ? 1 : 0;
7501 case Message::SetCaretLineVisible
:
7503 if (!vs
.elementColours
.count(Element::CaretLineBack
)) {
7504 vs
.elementColours
[Element::CaretLineBack
] = ColourRGBA(0xFF, 0xFF, 0);
7505 InvalidateStyleRedraw();
7508 if (vs
.ResetElement(Element::CaretLineBack
)) {
7509 InvalidateStyleRedraw();
7513 case Message::GetCaretLineVisibleAlways
:
7514 return vs
.caretLine
.alwaysShow
;
7515 case Message::SetCaretLineVisibleAlways
:
7516 vs
.caretLine
.alwaysShow
= wParam
!= 0;
7517 InvalidateStyleRedraw();
7520 case Message::GetCaretLineHighlightSubLine
:
7521 return vs
.caretLine
.subLine
;
7522 case Message::SetCaretLineHighlightSubLine
:
7523 vs
.caretLine
.subLine
= wParam
!= 0;
7524 InvalidateStyleRedraw();
7527 case Message::GetCaretLineFrame
:
7528 return vs
.caretLine
.frame
;
7529 case Message::SetCaretLineFrame
:
7530 vs
.caretLine
.frame
= static_cast<int>(wParam
);
7531 InvalidateStyleRedraw();
7533 case Message::GetCaretLineBack
:
7534 return vs
.ElementColourForced(Element::CaretLineBack
).OpaqueRGB();
7536 case Message::SetCaretLineBack
:
7537 vs
.SetElementRGB(Element::CaretLineBack
, static_cast<int>(wParam
));
7538 InvalidateStyleRedraw();
7541 case Message::GetCaretLineLayer
:
7542 return static_cast<sptr_t
>(vs
.caretLine
.layer
);
7544 case Message::SetCaretLineLayer
:
7545 if (vs
.caretLine
.layer
!= static_cast<Layer
>(wParam
)) {
7546 vs
.caretLine
.layer
= static_cast<Layer
>(wParam
);
7547 UpdateBaseElements();
7548 InvalidateStyleRedraw();
7552 case Message::GetCaretLineBackAlpha
:
7553 if (vs
.caretLine
.layer
== Layer::Base
)
7554 return static_cast<sptr_t
>(Alpha::NoAlpha
);
7555 return vs
.ElementColour(Element::CaretLineBack
).value_or(ColourRGBA()).GetAlpha();
7557 case Message::SetCaretLineBackAlpha
: {
7558 const Layer layerNew
= (static_cast<Alpha
>(wParam
) == Alpha::NoAlpha
) ? Layer::Base
: Layer::OverText
;
7559 vs
.caretLine
.layer
= layerNew
;
7560 if (vs
.ElementColour(Element::CaretLineBack
)) {
7561 vs
.SetElementAlpha(Element::CaretLineBack
, static_cast<int>(wParam
));
7563 InvalidateStyleRedraw();
7569 case Message::VisibleFromDocLine
:
7570 return pcs
->DisplayFromDoc(LineFromUPtr(wParam
));
7572 case Message::DocLineFromVisible
:
7573 return pcs
->DocFromDisplay(LineFromUPtr(wParam
));
7575 case Message::WrapCount
:
7576 return WrapCount(LineFromUPtr(wParam
));
7578 case Message::SetFoldLevel
: {
7579 const int prev
= pdoc
->SetLevel(LineFromUPtr(wParam
), static_cast<int>(lParam
));
7580 if (prev
!= static_cast<int>(lParam
))
7585 case Message::GetFoldLevel
:
7586 return pdoc
->GetLevel(LineFromUPtr(wParam
));
7588 case Message::GetLastChild
:
7589 return pdoc
->GetLastChild(LineFromUPtr(wParam
), OptionalFoldLevel(lParam
));
7591 case Message::GetFoldParent
:
7592 return pdoc
->GetFoldParent(LineFromUPtr(wParam
));
7594 case Message::ShowLines
:
7595 pcs
->SetVisible(LineFromUPtr(wParam
), lParam
, true);
7600 case Message::HideLines
:
7601 pcs
->SetVisible(LineFromUPtr(wParam
), lParam
, false);
7606 case Message::GetLineVisible
:
7607 return pcs
->GetVisible(LineFromUPtr(wParam
));
7609 case Message::GetAllLinesVisible
:
7610 return pcs
->HiddenLines() ? 0 : 1;
7612 case Message::SetFoldExpanded
:
7613 SetFoldExpanded(LineFromUPtr(wParam
), lParam
!= 0);
7616 case Message::GetFoldExpanded
:
7617 return pcs
->GetExpanded(LineFromUPtr(wParam
));
7619 case Message::SetAutomaticFold
:
7620 foldAutomatic
= static_cast<AutomaticFold
>(wParam
);
7623 case Message::GetAutomaticFold
:
7624 return static_cast<sptr_t
>(foldAutomatic
);
7626 case Message::SetFoldFlags
:
7627 foldFlags
= static_cast<FoldFlag
>(wParam
);
7631 case Message::ToggleFoldShowText
:
7632 pcs
->SetFoldDisplayText(LineFromUPtr(wParam
), ConstCharPtrFromSPtr(lParam
));
7633 FoldLine(LineFromUPtr(wParam
), FoldAction::Toggle
);
7636 case Message::FoldDisplayTextSetStyle
:
7637 foldDisplayTextStyle
= static_cast<FoldDisplayTextStyle
>(wParam
);
7641 case Message::FoldDisplayTextGetStyle
:
7642 return static_cast<sptr_t
>(foldDisplayTextStyle
);
7644 case Message::SetDefaultFoldDisplayText
:
7645 SetDefaultFoldDisplayText(ConstCharPtrFromSPtr(lParam
));
7649 case Message::GetDefaultFoldDisplayText
:
7650 return StringResult(lParam
, GetDefaultFoldDisplayText());
7652 case Message::ToggleFold
:
7653 FoldLine(LineFromUPtr(wParam
), FoldAction::Toggle
);
7656 case Message::FoldLine
:
7657 FoldLine(LineFromUPtr(wParam
), static_cast<FoldAction
>(lParam
));
7660 case Message::FoldChildren
:
7661 FoldExpand(LineFromUPtr(wParam
), static_cast<FoldAction
>(lParam
), pdoc
->GetFoldLevel(LineFromUPtr(wParam
)));
7664 case Message::FoldAll
:
7665 FoldAll(static_cast<FoldAction
>(wParam
));
7668 case Message::ExpandChildren
:
7669 FoldExpand(LineFromUPtr(wParam
), FoldAction::Expand
, static_cast<FoldLevel
>(lParam
));
7672 case Message::ContractedFoldNext
:
7673 return ContractedFoldNext(LineFromUPtr(wParam
));
7675 case Message::EnsureVisible
:
7676 EnsureLineVisible(LineFromUPtr(wParam
), false);
7679 case Message::EnsureVisibleEnforcePolicy
:
7680 EnsureLineVisible(LineFromUPtr(wParam
), true);
7683 case Message::ScrollRange
:
7684 ScrollRange(SelectionRange(PositionFromUPtr(wParam
), lParam
));
7687 case Message::SearchAnchor
:
7691 case Message::SearchNext
:
7692 case Message::SearchPrev
:
7693 return SearchText(iMessage
, wParam
, lParam
);
7695 case Message::SetXCaretPolicy
:
7696 caretPolicies
.x
= CaretPolicySlop(wParam
, lParam
);
7699 case Message::SetYCaretPolicy
:
7700 caretPolicies
.y
= CaretPolicySlop(wParam
, lParam
);
7703 case Message::SetVisiblePolicy
:
7704 visiblePolicy
= VisiblePolicySlop(wParam
, lParam
);
7707 case Message::LinesOnScreen
:
7708 return LinesOnScreen();
7710 case Message::SetSelFore
:
7711 vs
.elementColours
[Element::SelectionText
] = OptionalColour(wParam
, lParam
);
7712 vs
.elementColours
[Element::SelectionAdditionalText
] = OptionalColour(wParam
, lParam
);
7713 InvalidateStyleRedraw();
7716 case Message::SetSelBack
:
7718 vs
.SetElementRGB(Element::SelectionBack
, static_cast<int>(lParam
));
7719 vs
.SetElementRGB(Element::SelectionAdditionalBack
, static_cast<int>(lParam
));
7721 vs
.ResetElement(Element::SelectionBack
);
7722 vs
.ResetElement(Element::SelectionAdditionalBack
);
7724 InvalidateStyleRedraw();
7727 case Message::SetSelAlpha
: {
7728 const Layer layerNew
= (static_cast<Alpha
>(wParam
) == Alpha::NoAlpha
) ? Layer::Base
: Layer::OverText
;
7729 if (vs
.selection
.layer
!= layerNew
) {
7730 vs
.selection
.layer
= layerNew
;
7731 UpdateBaseElements();
7733 const int alpha
= static_cast<int>(wParam
);
7734 vs
.SetElementAlpha(Element::SelectionBack
, alpha
);
7735 vs
.SetElementAlpha(Element::SelectionAdditionalBack
, alpha
);
7736 vs
.SetElementAlpha(Element::SelectionSecondaryBack
, alpha
);
7737 vs
.SetElementAlpha(Element::SelectionInactiveBack
, alpha
);
7738 InvalidateStyleRedraw();
7742 case Message::GetSelAlpha
:
7743 if (vs
.selection
.layer
== Layer::Base
)
7744 return static_cast<sptr_t
>(Alpha::NoAlpha
);
7745 return vs
.ElementColourForced(Element::SelectionBack
).GetAlpha();
7747 case Message::GetSelEOLFilled
:
7748 return vs
.selection
.eolFilled
;
7750 case Message::SetSelEOLFilled
:
7751 vs
.selection
.eolFilled
= wParam
!= 0;
7752 InvalidateStyleRedraw();
7755 case Message::SetWhitespaceFore
:
7756 if (vs
.SetElementColourOptional(Element::WhiteSpace
, wParam
, lParam
)) {
7757 InvalidateStyleRedraw();
7761 case Message::SetWhitespaceBack
:
7762 if (vs
.SetElementColourOptional(Element::WhiteSpaceBack
, wParam
, lParam
)) {
7763 InvalidateStyleRedraw();
7767 case Message::SetSelectionLayer
:
7768 if (vs
.selection
.layer
!= static_cast<Layer
>(wParam
)) {
7769 vs
.selection
.layer
= static_cast<Layer
>(wParam
);
7770 UpdateBaseElements();
7771 InvalidateStyleRedraw();
7775 case Message::GetSelectionLayer
:
7776 return static_cast<sptr_t
>(vs
.selection
.layer
);
7778 case Message::SetCaretFore
:
7779 vs
.elementColours
[Element::Caret
] = ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam
));
7780 InvalidateStyleRedraw();
7783 case Message::GetCaretFore
:
7784 return vs
.ElementColourForced(Element::Caret
).OpaqueRGB();
7786 case Message::SetCaretStyle
:
7787 if (static_cast<CaretStyle
>(wParam
) <= (CaretStyle::Block
| CaretStyle::OverstrikeBlock
| CaretStyle::Curses
| CaretStyle::BlockAfter
))
7788 vs
.caret
.style
= static_cast<CaretStyle
>(wParam
);
7790 /* Default to the line caret */
7791 vs
.caret
.style
= CaretStyle::Line
;
7792 InvalidateStyleRedraw();
7795 case Message::GetCaretStyle
:
7796 return static_cast<sptr_t
>(vs
.caret
.style
);
7798 case Message::SetCaretWidth
:
7799 vs
.caret
.width
= std::clamp(static_cast<int>(wParam
), 0, 20);
7800 InvalidateStyleRedraw();
7803 case Message::GetCaretWidth
:
7804 return vs
.caret
.width
;
7806 case Message::AssignCmdKey
:
7807 kmap
.AssignCmdKey(static_cast<Keys
>(LowShortFromWParam(wParam
)),
7808 static_cast<KeyMod
>(HighShortFromWParam(wParam
)), static_cast<Message
>(lParam
));
7811 case Message::ClearCmdKey
:
7812 kmap
.AssignCmdKey(static_cast<Keys
>(LowShortFromWParam(wParam
)),
7813 static_cast<KeyMod
>(HighShortFromWParam(wParam
)), Message::Null
);
7816 case Message::ClearAllCmdKeys
:
7820 case Message::IndicSetStyle
:
7821 if (wParam
<= IndicatorMax
) {
7822 vs
.indicators
[wParam
].sacNormal
.style
= static_cast<IndicatorStyle
>(lParam
);
7823 vs
.indicators
[wParam
].sacHover
.style
= static_cast<IndicatorStyle
>(lParam
);
7824 InvalidateStyleRedraw();
7828 case Message::IndicGetStyle
:
7829 return (wParam
<= IndicatorMax
) ?
7830 static_cast<sptr_t
>(vs
.indicators
[wParam
].sacNormal
.style
) : 0;
7832 case Message::IndicSetFore
:
7833 if (wParam
<= IndicatorMax
) {
7834 vs
.indicators
[wParam
].sacNormal
.fore
= ColourRGBA::FromIpRGB(lParam
);
7835 vs
.indicators
[wParam
].sacHover
.fore
= ColourRGBA::FromIpRGB(lParam
);
7836 InvalidateStyleRedraw();
7840 case Message::IndicGetFore
:
7841 return (wParam
<= IndicatorMax
) ?
7842 vs
.indicators
[wParam
].sacNormal
.fore
.OpaqueRGB() : 0;
7844 case Message::IndicSetHoverStyle
:
7845 if (wParam
<= IndicatorMax
) {
7846 vs
.indicators
[wParam
].sacHover
.style
= static_cast<IndicatorStyle
>(lParam
);
7847 InvalidateStyleRedraw();
7851 case Message::IndicGetHoverStyle
:
7852 return (wParam
<= IndicatorMax
) ?
7853 static_cast<sptr_t
>(vs
.indicators
[wParam
].sacHover
.style
) : 0;
7855 case Message::IndicSetHoverFore
:
7856 if (wParam
<= IndicatorMax
) {
7857 vs
.indicators
[wParam
].sacHover
.fore
= ColourRGBA::FromIpRGB(lParam
);
7858 InvalidateStyleRedraw();
7862 case Message::IndicGetHoverFore
:
7863 return (wParam
<= IndicatorMax
) ?
7864 vs
.indicators
[wParam
].sacHover
.fore
.OpaqueRGB() : 0;
7866 case Message::IndicSetFlags
:
7867 if (wParam
<= IndicatorMax
) {
7868 vs
.indicators
[wParam
].SetFlags(static_cast<IndicFlag
>(lParam
));
7869 InvalidateStyleRedraw();
7873 case Message::IndicGetFlags
:
7874 return (wParam
<= IndicatorMax
) ?
7875 static_cast<sptr_t
>(vs
.indicators
[wParam
].Flags()) : 0;
7877 case Message::IndicSetUnder
:
7878 if (wParam
<= IndicatorMax
) {
7879 vs
.indicators
[wParam
].under
= lParam
!= 0;
7880 InvalidateStyleRedraw();
7884 case Message::IndicGetUnder
:
7885 return (wParam
<= IndicatorMax
) ?
7886 vs
.indicators
[wParam
].under
: 0;
7888 case Message::IndicSetAlpha
:
7889 if (wParam
<= IndicatorMax
&& lParam
>=0 && lParam
<= 255) {
7890 vs
.indicators
[wParam
].fillAlpha
= static_cast<int>(lParam
);
7891 InvalidateStyleRedraw();
7895 case Message::IndicGetAlpha
:
7896 return (wParam
<= IndicatorMax
)
7897 ? vs
.indicators
[wParam
].fillAlpha
: 0;
7899 case Message::IndicSetOutlineAlpha
:
7900 if (wParam
<= IndicatorMax
&& lParam
>=0 && lParam
<= 255) {
7901 vs
.indicators
[wParam
].outlineAlpha
= static_cast<int>(lParam
);
7902 InvalidateStyleRedraw();
7906 case Message::IndicGetOutlineAlpha
:
7907 return (wParam
<= IndicatorMax
) ? vs
.indicators
[wParam
].outlineAlpha
: 0;
7909 case Message::IndicSetStrokeWidth
:
7910 if (wParam
<= IndicatorMax
&& lParam
>= 0 && lParam
<= 1000) {
7911 vs
.indicators
[wParam
].strokeWidth
= lParam
/ 100.0f
;
7912 InvalidateStyleRedraw();
7916 case Message::IndicGetStrokeWidth
:
7917 if (wParam
<= IndicatorMax
) {
7918 return std::lround(vs
.indicators
[wParam
].strokeWidth
* 100);
7922 case Message::SetIndicatorCurrent
:
7923 pdoc
->DecorationSetCurrentIndicator(static_cast<int>(wParam
));
7925 case Message::GetIndicatorCurrent
:
7926 return pdoc
->decorations
->GetCurrentIndicator();
7927 case Message::SetIndicatorValue
:
7928 pdoc
->decorations
->SetCurrentValue(static_cast<int>(wParam
));
7930 case Message::GetIndicatorValue
:
7931 return pdoc
->decorations
->GetCurrentValue();
7933 case Message::IndicatorFillRange
:
7934 pdoc
->DecorationFillRange(PositionFromUPtr(wParam
),
7935 pdoc
->decorations
->GetCurrentValue(), lParam
);
7938 case Message::IndicatorClearRange
:
7939 pdoc
->DecorationFillRange(PositionFromUPtr(wParam
), 0,
7943 case Message::IndicatorAllOnFor
:
7944 return pdoc
->decorations
->AllOnFor(PositionFromUPtr(wParam
));
7946 case Message::IndicatorValueAt
:
7947 return pdoc
->decorations
->ValueAt(static_cast<int>(wParam
), lParam
);
7949 case Message::IndicatorStart
:
7950 return pdoc
->decorations
->Start(static_cast<int>(wParam
), lParam
);
7952 case Message::IndicatorEnd
:
7953 return pdoc
->decorations
->End(static_cast<int>(wParam
), lParam
);
7955 case Message::LineDown
:
7956 case Message::LineDownExtend
:
7957 case Message::ParaDown
:
7958 case Message::ParaDownExtend
:
7959 case Message::LineUp
:
7960 case Message::LineUpExtend
:
7961 case Message::ParaUp
:
7962 case Message::ParaUpExtend
:
7963 case Message::CharLeft
:
7964 case Message::CharLeftExtend
:
7965 case Message::CharRight
:
7966 case Message::CharRightExtend
:
7967 case Message::WordLeft
:
7968 case Message::WordLeftExtend
:
7969 case Message::WordRight
:
7970 case Message::WordRightExtend
:
7971 case Message::WordLeftEnd
:
7972 case Message::WordLeftEndExtend
:
7973 case Message::WordRightEnd
:
7974 case Message::WordRightEndExtend
:
7976 case Message::HomeExtend
:
7977 case Message::LineEnd
:
7978 case Message::LineEndExtend
:
7979 case Message::HomeWrap
:
7980 case Message::HomeWrapExtend
:
7981 case Message::LineEndWrap
:
7982 case Message::LineEndWrapExtend
:
7983 case Message::DocumentStart
:
7984 case Message::DocumentStartExtend
:
7985 case Message::DocumentEnd
:
7986 case Message::DocumentEndExtend
:
7987 case Message::ScrollToStart
:
7988 case Message::ScrollToEnd
:
7990 case Message::StutteredPageUp
:
7991 case Message::StutteredPageUpExtend
:
7992 case Message::StutteredPageDown
:
7993 case Message::StutteredPageDownExtend
:
7995 case Message::PageUp
:
7996 case Message::PageUpExtend
:
7997 case Message::PageDown
:
7998 case Message::PageDownExtend
:
7999 case Message::EditToggleOvertype
:
8000 case Message::Cancel
:
8001 case Message::DeleteBack
:
8003 case Message::BackTab
:
8004 case Message::NewLine
:
8005 case Message::FormFeed
:
8006 case Message::VCHome
:
8007 case Message::VCHomeExtend
:
8008 case Message::VCHomeWrap
:
8009 case Message::VCHomeWrapExtend
:
8010 case Message::VCHomeDisplay
:
8011 case Message::VCHomeDisplayExtend
:
8012 case Message::ZoomIn
:
8013 case Message::ZoomOut
:
8014 case Message::DelWordLeft
:
8015 case Message::DelWordRight
:
8016 case Message::DelWordRightEnd
:
8017 case Message::DelLineLeft
:
8018 case Message::DelLineRight
:
8019 case Message::LineCopy
:
8020 case Message::LineCut
:
8021 case Message::LineDelete
:
8022 case Message::LineTranspose
:
8023 case Message::LineReverse
:
8024 case Message::LineDuplicate
:
8025 case Message::LowerCase
:
8026 case Message::UpperCase
:
8027 case Message::LineScrollDown
:
8028 case Message::LineScrollUp
:
8029 case Message::WordPartLeft
:
8030 case Message::WordPartLeftExtend
:
8031 case Message::WordPartRight
:
8032 case Message::WordPartRightExtend
:
8033 case Message::DeleteBackNotLine
:
8034 case Message::HomeDisplay
:
8035 case Message::HomeDisplayExtend
:
8036 case Message::LineEndDisplay
:
8037 case Message::LineEndDisplayExtend
:
8038 case Message::LineDownRectExtend
:
8039 case Message::LineUpRectExtend
:
8040 case Message::CharLeftRectExtend
:
8041 case Message::CharRightRectExtend
:
8042 case Message::HomeRectExtend
:
8043 case Message::VCHomeRectExtend
:
8044 case Message::LineEndRectExtend
:
8045 case Message::PageUpRectExtend
:
8046 case Message::PageDownRectExtend
:
8047 case Message::SelectionDuplicate
:
8048 return KeyCommand(iMessage
);
8050 case Message::BraceHighlight
:
8051 SetBraceHighlight(PositionFromUPtr(wParam
), lParam
, StyleBraceLight
);
8054 case Message::BraceHighlightIndicator
:
8055 if (lParam
>= 0 && static_cast<size_t>(lParam
) <= IndicatorMax
) {
8056 vs
.braceHighlightIndicatorSet
= wParam
!= 0;
8057 vs
.braceHighlightIndicator
= static_cast<int>(lParam
);
8061 case Message::BraceBadLight
:
8062 SetBraceHighlight(PositionFromUPtr(wParam
), -1, StyleBraceBad
);
8065 case Message::BraceBadLightIndicator
:
8066 if (lParam
>= 0 && static_cast<size_t>(lParam
) <= IndicatorMax
) {
8067 vs
.braceBadLightIndicatorSet
= wParam
!= 0;
8068 vs
.braceBadLightIndicator
= static_cast<int>(lParam
);
8072 case Message::BraceMatch
:
8073 // wParam is position of char to find brace for,
8074 // lParam is maximum amount of text to restyle to find it
8075 return pdoc
->BraceMatch(PositionFromUPtr(wParam
), lParam
, 0, false);
8077 case Message::BraceMatchNext
:
8078 return pdoc
->BraceMatch(PositionFromUPtr(wParam
), 0, lParam
, true);
8080 case Message::GetViewEOL
:
8083 case Message::SetViewEOL
:
8084 vs
.viewEOL
= wParam
!= 0;
8085 InvalidateStyleRedraw();
8088 case Message::SetZoom
: {
8089 const int zoomLevel
= static_cast<int>(wParam
);
8090 if (zoomLevel
!= vs
.zoomLevel
) {
8091 vs
.zoomLevel
= zoomLevel
;
8092 InvalidateStyleRedraw();
8098 case Message::GetZoom
:
8099 return vs
.zoomLevel
;
8101 case Message::GetEdgeColumn
:
8102 return vs
.theEdge
.column
;
8104 case Message::SetEdgeColumn
:
8105 vs
.theEdge
.column
= static_cast<int>(wParam
);
8106 InvalidateStyleRedraw();
8109 case Message::GetEdgeMode
:
8110 return static_cast<sptr_t
>(vs
.edgeState
);
8112 case Message::SetEdgeMode
:
8113 vs
.edgeState
= static_cast<EdgeVisualStyle
>(wParam
);
8114 InvalidateStyleRedraw();
8117 case Message::GetEdgeColour
:
8118 return vs
.theEdge
.colour
.OpaqueRGB();
8120 case Message::SetEdgeColour
:
8121 vs
.theEdge
.colour
= ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam
));
8122 InvalidateStyleRedraw();
8125 case Message::MultiEdgeAddLine
:
8126 vs
.AddMultiEdge(static_cast<int>(wParam
), ColourRGBA::FromIpRGB(lParam
));
8127 InvalidateStyleRedraw();
8130 case Message::MultiEdgeClearAll
:
8131 std::vector
<EdgeProperties
>().swap(vs
.theMultiEdge
); // Free vector and memory, C++03 compatible
8132 InvalidateStyleRedraw();
8135 case Message::GetMultiEdgeColumn
: {
8136 const size_t which
= wParam
;
8137 // size_t is unsigned so this also handles negative inputs.
8138 if (which
>= vs
.theMultiEdge
.size()) {
8141 return vs
.theMultiEdge
[which
].column
;
8144 case Message::GetAccessibility
:
8145 return static_cast<sptr_t
>(Accessibility::Disabled
);
8147 case Message::SetAccessibility
:
8148 // May be implemented by platform code.
8151 case Message::GetDocPointer
:
8152 return reinterpret_cast<sptr_t
>(pdoc
);
8154 case Message::SetDocPointer
:
8156 SetDocPointer(static_cast<Document
*>(PtrFromSPtr(lParam
)));
8159 case Message::CreateDocument
: {
8160 Document
*doc
= new Document(static_cast<DocumentOption
>(lParam
));
8162 doc
->Allocate(PositionFromUPtr(wParam
));
8163 pcs
= ContractionStateCreate(pdoc
->IsLarge());
8164 return reinterpret_cast<sptr_t
>(doc
);
8167 case Message::AddRefDocument
:
8168 (static_cast<Document
*>(PtrFromSPtr(lParam
)))->AddRef();
8171 case Message::ReleaseDocument
:
8172 (static_cast<Document
*>(PtrFromSPtr(lParam
)))->Release();
8175 case Message::GetDocumentOptions
:
8176 return static_cast<sptr_t
>(pdoc
->Options());
8178 case Message::CreateLoader
: {
8179 Document
*doc
= new Document(static_cast<DocumentOption
>(lParam
));
8181 doc
->Allocate(PositionFromUPtr(wParam
));
8182 doc
->SetUndoCollection(false);
8183 pcs
= ContractionStateCreate(pdoc
->IsLarge());
8184 return reinterpret_cast<sptr_t
>(static_cast<ILoader
*>(doc
));
8187 case Message::SetModEventMask
:
8188 modEventMask
= static_cast<ModificationFlags
>(wParam
);
8191 case Message::GetModEventMask
:
8192 return static_cast<sptr_t
>(modEventMask
);
8194 case Message::SetCommandEvents
:
8195 commandEvents
= static_cast<bool>(wParam
);
8198 case Message::GetCommandEvents
:
8199 return commandEvents
;
8201 case Message::ConvertEOLs
:
8202 pdoc
->ConvertLineEnds(static_cast<EndOfLine
>(wParam
));
8203 SetSelection(sel
.MainCaret(), sel
.MainAnchor()); // Ensure selection inside document
8206 case Message::SetLengthForEncode
:
8207 lengthForEncode
= PositionFromUPtr(wParam
);
8210 case Message::SelectionIsRectangle
:
8211 return sel
.selType
== Selection::SelTypes::rectangle
? 1 : 0;
8213 case Message::SetSelectionMode
: {
8214 switch (static_cast<SelectionMode
>(wParam
)) {
8215 case SelectionMode::Stream
:
8216 sel
.SetMoveExtends(!sel
.MoveExtends() || (sel
.selType
!= Selection::SelTypes::stream
));
8217 sel
.selType
= Selection::SelTypes::stream
;
8219 case SelectionMode::Rectangle
:
8220 sel
.SetMoveExtends(!sel
.MoveExtends() || (sel
.selType
!= Selection::SelTypes::rectangle
));
8221 sel
.selType
= Selection::SelTypes::rectangle
;
8222 sel
.Rectangular() = sel
.RangeMain(); // adjust current selection
8224 case SelectionMode::Lines
:
8225 sel
.SetMoveExtends(!sel
.MoveExtends() || (sel
.selType
!= Selection::SelTypes::lines
));
8226 sel
.selType
= Selection::SelTypes::lines
;
8227 SetSelection(sel
.RangeMain().caret
, sel
.RangeMain().anchor
); // adjust current selection
8229 case SelectionMode::Thin
:
8230 sel
.SetMoveExtends(!sel
.MoveExtends() || (sel
.selType
!= Selection::SelTypes::thin
));
8231 sel
.selType
= Selection::SelTypes::thin
;
8234 sel
.SetMoveExtends(!sel
.MoveExtends() || (sel
.selType
!= Selection::SelTypes::stream
));
8235 sel
.selType
= Selection::SelTypes::stream
;
8237 InvalidateWholeSelection();
8240 case Message::GetSelectionMode
:
8241 switch (sel
.selType
) {
8242 case Selection::SelTypes::stream
:
8243 return static_cast<sptr_t
>(SelectionMode::Stream
);
8244 case Selection::SelTypes::rectangle
:
8245 return static_cast<sptr_t
>(SelectionMode::Rectangle
);
8246 case Selection::SelTypes::lines
:
8247 return static_cast<sptr_t
>(SelectionMode::Lines
);
8248 case Selection::SelTypes::thin
:
8249 return static_cast<sptr_t
>(SelectionMode::Thin
);
8251 return static_cast<sptr_t
>(SelectionMode::Stream
);
8253 case Message::GetMoveExtendsSelection
:
8254 return sel
.MoveExtends();
8255 case Message::GetLineSelStartPosition
:
8256 case Message::GetLineSelEndPosition
: {
8257 const SelectionSegment
segmentLine(
8258 SelectionPosition(pdoc
->LineStart(LineFromUPtr(wParam
))),
8259 SelectionPosition(pdoc
->LineEnd(LineFromUPtr(wParam
))));
8260 for (size_t r
=0; r
<sel
.Count(); r
++) {
8261 const SelectionSegment portion
= sel
.Range(r
).Intersect(segmentLine
);
8262 if (portion
.start
.IsValid()) {
8263 return (iMessage
== Message::GetLineSelStartPosition
) ? portion
.start
.Position() : portion
.end
.Position();
8266 return Sci::invalidPosition
;
8269 case Message::SetOvertype
:
8270 if (inOverstrike
!= (wParam
!= 0)) {
8271 inOverstrike
= wParam
!= 0;
8272 ContainerNeedsUpdate(Update::Selection
);
8273 ShowCaretAtCurrentPosition();
8278 case Message::GetOvertype
:
8279 return inOverstrike
? 1 : 0;
8281 case Message::SetFocus
:
8282 SetFocusState(wParam
!= 0);
8285 case Message::GetFocus
:
8288 case Message::SetStatus
:
8289 errorStatus
= static_cast<Status
>(wParam
);
8292 case Message::GetStatus
:
8293 return static_cast<sptr_t
>(errorStatus
);
8295 case Message::SetMouseDownCaptures
:
8296 mouseDownCaptures
= wParam
!= 0;
8299 case Message::GetMouseDownCaptures
:
8300 return mouseDownCaptures
;
8302 case Message::SetMouseWheelCaptures
:
8303 mouseWheelCaptures
= wParam
!= 0;
8306 case Message::GetMouseWheelCaptures
:
8307 return mouseWheelCaptures
;
8309 case Message::SetCursor
:
8310 cursorMode
= static_cast<CursorShape
>(wParam
);
8311 DisplayCursor(Window::Cursor::text
);
8314 case Message::GetCursor
:
8315 return static_cast<sptr_t
>(cursorMode
);
8317 case Message::SetControlCharSymbol
:
8318 vs
.controlCharSymbol
= static_cast<int>(wParam
);
8319 InvalidateStyleRedraw();
8322 case Message::GetControlCharSymbol
:
8323 return vs
.controlCharSymbol
;
8325 case Message::SetRepresentation
:
8326 reprs
.SetRepresentation(ConstCharPtrFromUPtr(wParam
), ConstCharPtrFromSPtr(lParam
));
8329 case Message::GetRepresentation
: {
8330 const Representation
*repr
= reprs
.RepresentationFromCharacter(
8331 ConstCharPtrFromUPtr(wParam
));
8333 return StringResult(lParam
, repr
->stringRep
.c_str());
8338 case Message::ClearRepresentation
:
8339 reprs
.ClearRepresentation(ConstCharPtrFromUPtr(wParam
));
8342 case Message::ClearAllRepresentations
:
8343 SetRepresentations();
8346 case Message::SetRepresentationAppearance
:
8347 reprs
.SetRepresentationAppearance(ConstCharPtrFromUPtr(wParam
), static_cast<RepresentationAppearance
>(lParam
));
8350 case Message::GetRepresentationAppearance
: {
8351 const Representation
*repr
= reprs
.RepresentationFromCharacter(
8352 ConstCharPtrFromUPtr(wParam
));
8354 return static_cast<sptr_t
>(repr
->appearance
);
8358 case Message::SetRepresentationColour
:
8359 reprs
.SetRepresentationColour(ConstCharPtrFromUPtr(wParam
), ColourRGBA(static_cast<int>(lParam
)));
8362 case Message::GetRepresentationColour
: {
8363 const Representation
*repr
= reprs
.RepresentationFromCharacter(
8364 ConstCharPtrFromUPtr(wParam
));
8366 return repr
->colour
.AsInteger();
8371 case Message::StartRecord
:
8372 recordingMacro
= true;
8375 case Message::StopRecord
:
8376 recordingMacro
= false;
8379 case Message::MoveCaretInsideView
:
8380 MoveCaretInsideView();
8383 case Message::SetFoldMarginColour
:
8384 vs
.foldmarginColour
= OptionalColour(wParam
, lParam
);
8385 InvalidateStyleRedraw();
8388 case Message::SetFoldMarginHiColour
:
8389 vs
.foldmarginHighlightColour
= OptionalColour(wParam
, lParam
);
8390 InvalidateStyleRedraw();
8393 case Message::SetHotspotActiveFore
:
8394 if (vs
.SetElementColourOptional(Element::HotSpotActive
, wParam
, lParam
)) {
8395 InvalidateStyleRedraw();
8399 case Message::GetHotspotActiveFore
:
8400 return vs
.ElementColour(Element::HotSpotActive
).value_or(ColourRGBA()).OpaqueRGB();
8402 case Message::SetHotspotActiveBack
:
8403 if (vs
.SetElementColourOptional(Element::HotSpotActiveBack
, wParam
, lParam
)) {
8404 InvalidateStyleRedraw();
8408 case Message::GetHotspotActiveBack
:
8409 return vs
.ElementColour(Element::HotSpotActiveBack
).value_or(ColourRGBA()).OpaqueRGB();
8411 case Message::SetHotspotActiveUnderline
:
8412 vs
.hotspotUnderline
= wParam
!= 0;
8413 InvalidateStyleRedraw();
8416 case Message::GetHotspotActiveUnderline
:
8417 return vs
.hotspotUnderline
? 1 : 0;
8419 case Message::SetHotspotSingleLine
:
8420 hotspotSingleLine
= wParam
!= 0;
8421 InvalidateStyleRedraw();
8424 case Message::GetHotspotSingleLine
:
8425 return hotspotSingleLine
? 1 : 0;
8427 case Message::SetPasteConvertEndings
:
8428 convertPastes
= wParam
!= 0;
8431 case Message::GetPasteConvertEndings
:
8432 return convertPastes
? 1 : 0;
8434 case Message::GetCharacterPointer
:
8435 return reinterpret_cast<sptr_t
>(pdoc
->BufferPointer());
8437 case Message::GetRangePointer
:
8438 return reinterpret_cast<sptr_t
>(pdoc
->RangePointer(
8439 PositionFromUPtr(wParam
), lParam
));
8441 case Message::GetGapPosition
:
8442 return pdoc
->GapPosition();
8444 case Message::SetChangeHistory
:
8445 changeHistoryOption
= static_cast<ChangeHistoryOption
>(wParam
);
8446 pdoc
->ChangeHistorySet(wParam
& 1);
8449 case Message::GetChangeHistory
:
8450 return static_cast<sptr_t
>(changeHistoryOption
);
8452 case Message::SetExtraAscent
:
8453 vs
.extraAscent
= static_cast<int>(wParam
);
8454 InvalidateStyleRedraw();
8457 case Message::GetExtraAscent
:
8458 return vs
.extraAscent
;
8460 case Message::SetExtraDescent
:
8461 vs
.extraDescent
= static_cast<int>(wParam
);
8462 InvalidateStyleRedraw();
8465 case Message::GetExtraDescent
:
8466 return vs
.extraDescent
;
8468 case Message::MarginSetStyleOffset
:
8469 vs
.marginStyleOffset
= static_cast<int>(wParam
);
8470 InvalidateStyleRedraw();
8473 case Message::MarginGetStyleOffset
:
8474 return vs
.marginStyleOffset
;
8476 case Message::SetMarginOptions
:
8477 marginOptions
= static_cast<MarginOption
>(wParam
);
8480 case Message::GetMarginOptions
:
8481 return static_cast<sptr_t
>(marginOptions
);
8483 case Message::MarginSetText
:
8484 pdoc
->MarginSetText(LineFromUPtr(wParam
), ConstCharPtrFromSPtr(lParam
));
8487 case Message::MarginGetText
: {
8488 const StyledText st
= pdoc
->MarginStyledText(LineFromUPtr(wParam
));
8489 return BytesResult(lParam
, reinterpret_cast<const unsigned char *>(st
.text
), st
.length
);
8492 case Message::MarginSetStyle
:
8493 pdoc
->MarginSetStyle(LineFromUPtr(wParam
), static_cast<int>(lParam
));
8496 case Message::MarginGetStyle
: {
8497 const StyledText st
= pdoc
->MarginStyledText(LineFromUPtr(wParam
));
8501 case Message::MarginSetStyles
:
8502 pdoc
->MarginSetStyles(LineFromUPtr(wParam
), ConstUCharPtrFromSPtr(lParam
));
8505 case Message::MarginGetStyles
: {
8506 const StyledText st
= pdoc
->MarginStyledText(LineFromUPtr(wParam
));
8507 return BytesResult(lParam
, st
.styles
, st
.length
);
8510 case Message::MarginTextClearAll
:
8511 pdoc
->MarginClearAll();
8514 case Message::AnnotationSetText
:
8515 pdoc
->AnnotationSetText(LineFromUPtr(wParam
), ConstCharPtrFromSPtr(lParam
));
8518 case Message::AnnotationGetText
: {
8519 const StyledText st
= pdoc
->AnnotationStyledText(LineFromUPtr(wParam
));
8520 return BytesResult(lParam
, reinterpret_cast<const unsigned char *>(st
.text
), st
.length
);
8523 case Message::AnnotationGetStyle
: {
8524 const StyledText st
= pdoc
->AnnotationStyledText(LineFromUPtr(wParam
));
8528 case Message::AnnotationSetStyle
:
8529 pdoc
->AnnotationSetStyle(LineFromUPtr(wParam
), static_cast<int>(lParam
));
8532 case Message::AnnotationSetStyles
:
8533 pdoc
->AnnotationSetStyles(LineFromUPtr(wParam
), ConstUCharPtrFromSPtr(lParam
));
8536 case Message::AnnotationGetStyles
: {
8537 const StyledText st
= pdoc
->AnnotationStyledText(LineFromUPtr(wParam
));
8538 return BytesResult(lParam
, st
.styles
, st
.length
);
8541 case Message::AnnotationGetLines
:
8542 return pdoc
->AnnotationLines(LineFromUPtr(wParam
));
8544 case Message::AnnotationClearAll
:
8545 pdoc
->AnnotationClearAll();
8548 case Message::AnnotationSetVisible
:
8549 SetAnnotationVisible(static_cast<AnnotationVisible
>(wParam
));
8552 case Message::AnnotationGetVisible
:
8553 return static_cast<sptr_t
>(vs
.annotationVisible
);
8555 case Message::AnnotationSetStyleOffset
:
8556 vs
.annotationStyleOffset
= static_cast<int>(wParam
);
8557 InvalidateStyleRedraw();
8560 case Message::AnnotationGetStyleOffset
:
8561 return vs
.annotationStyleOffset
;
8563 case Message::EOLAnnotationSetText
:
8564 pdoc
->EOLAnnotationSetText(LineFromUPtr(wParam
), ConstCharPtrFromSPtr(lParam
));
8567 case Message::EOLAnnotationGetText
: {
8568 const StyledText st
= pdoc
->EOLAnnotationStyledText(LineFromUPtr(wParam
));
8569 return BytesResult(lParam
, reinterpret_cast<const unsigned char *>(st
.text
), st
.length
);
8572 case Message::EOLAnnotationGetStyle
: {
8573 const StyledText st
= pdoc
->EOLAnnotationStyledText(LineFromUPtr(wParam
));
8577 case Message::EOLAnnotationSetStyle
:
8578 pdoc
->EOLAnnotationSetStyle(LineFromUPtr(wParam
), static_cast<int>(lParam
));
8581 case Message::EOLAnnotationClearAll
:
8582 pdoc
->EOLAnnotationClearAll();
8585 case Message::EOLAnnotationSetVisible
:
8586 SetEOLAnnotationVisible(static_cast<EOLAnnotationVisible
>(wParam
));
8589 case Message::EOLAnnotationGetVisible
:
8590 return static_cast<sptr_t
>(vs
.eolAnnotationVisible
);
8592 case Message::EOLAnnotationSetStyleOffset
:
8593 vs
.eolAnnotationStyleOffset
= static_cast<int>(wParam
);
8594 InvalidateStyleRedraw();
8597 case Message::EOLAnnotationGetStyleOffset
:
8598 return vs
.eolAnnotationStyleOffset
;
8600 case Message::ReleaseAllExtendedStyles
:
8601 vs
.ReleaseAllExtendedStyles();
8604 case Message::AllocateExtendedStyles
:
8605 return vs
.AllocateExtendedStyles(static_cast<int>(wParam
));
8607 case Message::SupportsFeature
:
8608 return SupportsFeature(static_cast<Supports
>(wParam
));
8610 case Message::AddUndoAction
:
8611 pdoc
->AddUndoAction(PositionFromUPtr(wParam
),
8612 FlagSet(static_cast<UndoFlags
>(lParam
), UndoFlags::MayCoalesce
));
8615 case Message::SetMouseSelectionRectangularSwitch
:
8616 mouseSelectionRectangularSwitch
= wParam
!= 0;
8619 case Message::GetMouseSelectionRectangularSwitch
:
8620 return mouseSelectionRectangularSwitch
;
8622 case Message::SetMultipleSelection
:
8623 multipleSelection
= wParam
!= 0;
8627 case Message::GetMultipleSelection
:
8628 return multipleSelection
;
8630 case Message::SetAdditionalSelectionTyping
:
8631 additionalSelectionTyping
= wParam
!= 0;
8635 case Message::GetAdditionalSelectionTyping
:
8636 return additionalSelectionTyping
;
8638 case Message::SetMultiPaste
:
8639 multiPasteMode
= static_cast<MultiPaste
>(wParam
);
8642 case Message::GetMultiPaste
:
8643 return static_cast<sptr_t
>(multiPasteMode
);
8645 case Message::SetAdditionalCaretsBlink
:
8646 view
.additionalCaretsBlink
= wParam
!= 0;
8650 case Message::GetAdditionalCaretsBlink
:
8651 return view
.additionalCaretsBlink
;
8653 case Message::SetAdditionalCaretsVisible
:
8654 view
.additionalCaretsVisible
= wParam
!= 0;
8658 case Message::GetAdditionalCaretsVisible
:
8659 return view
.additionalCaretsVisible
;
8661 case Message::GetSelections
:
8664 case Message::GetSelectionEmpty
:
8667 case Message::ClearSelections
:
8669 ContainerNeedsUpdate(Update::Selection
);
8673 case Message::SetSelection
:
8674 sel
.SetSelection(SelectionRange(PositionFromUPtr(wParam
), lParam
));
8678 case Message::AddSelection
:
8679 sel
.AddSelection(SelectionRange(PositionFromUPtr(wParam
), lParam
));
8680 ContainerNeedsUpdate(Update::Selection
);
8684 case Message::DropSelectionN
:
8685 sel
.DropSelection(wParam
);
8686 ContainerNeedsUpdate(Update::Selection
);
8690 case Message::SetMainSelection
:
8691 sel
.SetMain(wParam
);
8692 ContainerNeedsUpdate(Update::Selection
);
8696 case Message::GetMainSelection
:
8699 case Message::SetSelectionNCaret
:
8700 case Message::SetSelectionNAnchor
:
8701 case Message::SetSelectionNCaretVirtualSpace
:
8702 case Message::SetSelectionNAnchorVirtualSpace
:
8703 case Message::SetSelectionNStart
:
8704 case Message::SetSelectionNEnd
:
8705 SetSelectionNMessage(iMessage
, wParam
, lParam
);
8708 case Message::GetSelectionNCaret
:
8709 return sel
.Range(wParam
).caret
.Position();
8711 case Message::GetSelectionNAnchor
:
8712 return sel
.Range(wParam
).anchor
.Position();
8714 case Message::GetSelectionNCaretVirtualSpace
:
8715 return sel
.Range(wParam
).caret
.VirtualSpace();
8717 case Message::GetSelectionNAnchorVirtualSpace
:
8718 return sel
.Range(wParam
).anchor
.VirtualSpace();
8720 case Message::GetSelectionNStart
:
8721 return sel
.Range(wParam
).Start().Position();
8723 case Message::GetSelectionNStartVirtualSpace
:
8724 return sel
.Range(wParam
).Start().VirtualSpace();
8726 case Message::GetSelectionNEnd
:
8727 return sel
.Range(wParam
).End().Position();
8729 case Message::GetSelectionNEndVirtualSpace
:
8730 return sel
.Range(wParam
).End().VirtualSpace();
8732 case Message::SetRectangularSelectionCaret
:
8733 if (!sel
.IsRectangular())
8735 sel
.selType
= Selection::SelTypes::rectangle
;
8736 sel
.Rectangular().caret
.SetPosition(PositionFromUPtr(wParam
));
8737 SetRectangularRange();
8741 case Message::GetRectangularSelectionCaret
:
8742 return sel
.Rectangular().caret
.Position();
8744 case Message::SetRectangularSelectionAnchor
:
8745 if (!sel
.IsRectangular())
8747 sel
.selType
= Selection::SelTypes::rectangle
;
8748 sel
.Rectangular().anchor
.SetPosition(PositionFromUPtr(wParam
));
8749 SetRectangularRange();
8753 case Message::GetRectangularSelectionAnchor
:
8754 return sel
.Rectangular().anchor
.Position();
8756 case Message::SetRectangularSelectionCaretVirtualSpace
:
8757 if (!sel
.IsRectangular())
8759 sel
.selType
= Selection::SelTypes::rectangle
;
8760 sel
.Rectangular().caret
.SetVirtualSpace(PositionFromUPtr(wParam
));
8761 SetRectangularRange();
8765 case Message::GetRectangularSelectionCaretVirtualSpace
:
8766 return sel
.Rectangular().caret
.VirtualSpace();
8768 case Message::SetRectangularSelectionAnchorVirtualSpace
:
8769 if (!sel
.IsRectangular())
8771 sel
.selType
= Selection::SelTypes::rectangle
;
8772 sel
.Rectangular().anchor
.SetVirtualSpace(PositionFromUPtr(wParam
));
8773 SetRectangularRange();
8777 case Message::GetRectangularSelectionAnchorVirtualSpace
:
8778 return sel
.Rectangular().anchor
.VirtualSpace();
8780 case Message::SetVirtualSpaceOptions
:
8781 virtualSpaceOptions
= static_cast<VirtualSpace
>(wParam
);
8784 case Message::GetVirtualSpaceOptions
:
8785 return static_cast<sptr_t
>(virtualSpaceOptions
);
8787 case Message::SetAdditionalSelFore
:
8788 vs
.elementColours
[Element::SelectionAdditionalText
] = ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam
));
8789 InvalidateStyleRedraw();
8792 case Message::SetAdditionalSelBack
:
8793 vs
.SetElementRGB(Element::SelectionAdditionalBack
, static_cast<int>(wParam
));
8794 InvalidateStyleRedraw();
8797 case Message::SetAdditionalSelAlpha
:
8798 vs
.SetElementAlpha(Element::SelectionAdditionalBack
, static_cast<int>(wParam
));
8799 InvalidateStyleRedraw();
8802 case Message::GetAdditionalSelAlpha
:
8803 if (vs
.selection
.layer
== Layer::Base
)
8804 return static_cast<sptr_t
>(Alpha::NoAlpha
);
8805 return vs
.ElementColourForced(Element::SelectionAdditionalBack
).GetAlpha();
8807 case Message::SetAdditionalCaretFore
:
8808 vs
.elementColours
[Element::CaretAdditional
] = ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam
));
8809 InvalidateStyleRedraw();
8812 case Message::GetAdditionalCaretFore
:
8813 return vs
.ElementColourForced(Element::CaretAdditional
).OpaqueRGB();
8815 case Message::RotateSelection
:
8817 InvalidateWholeSelection();
8820 case Message::SwapMainAnchorCaret
:
8821 InvalidateSelection(sel
.RangeMain());
8822 sel
.RangeMain().Swap();
8825 case Message::MultipleSelectAddNext
:
8826 MultipleSelectAdd(AddNumber::one
);
8829 case Message::MultipleSelectAddEach
:
8830 MultipleSelectAdd(AddNumber::each
);
8833 case Message::ChangeLexerState
:
8834 pdoc
->ChangeLexerState(PositionFromUPtr(wParam
), lParam
);
8837 case Message::SetIdentifier
:
8838 SetCtrlID(static_cast<int>(wParam
));
8841 case Message::GetIdentifier
:
8844 case Message::SetTechnology
:
8845 // No action by default
8848 case Message::GetTechnology
:
8849 return static_cast<sptr_t
>(technology
);
8851 case Message::CountCharacters
:
8852 return pdoc
->CountCharacters(PositionFromUPtr(wParam
), lParam
);
8854 case Message::CountCodeUnits
:
8855 return pdoc
->CountUTF16(PositionFromUPtr(wParam
), lParam
);
8858 return DefWndProc(iMessage
, wParam
, lParam
);
8860 //Platform::DebugPrintf("end wnd proc\n");