Merge pull request #3720 from b4n/encodings-ui-improvements
[geany-mirror.git] / scintilla / src / Editor.cxx
blob80436fd21ead6ef5fa3609edd176f74d127511cf
1 // Scintilla source code edit control
2 /** @file Editor.cxx
3 ** Main code for the edit control.
4 **/
5 // Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <cstddef>
9 #include <cstdlib>
10 #include <cstdint>
11 #include <cassert>
12 #include <cstring>
13 #include <cstdio>
14 #include <cmath>
16 #include <stdexcept>
17 #include <string>
18 #include <string_view>
19 #include <vector>
20 #include <map>
21 #include <set>
22 #include <forward_list>
23 #include <optional>
24 #include <algorithm>
25 #include <iterator>
26 #include <memory>
27 #include <chrono>
28 #include <atomic>
29 #include <mutex>
30 #include <thread>
31 #include <future>
33 #include "ScintillaTypes.h"
34 #include "ScintillaMessages.h"
35 #include "ScintillaStructures.h"
36 #include "ILoader.h"
37 #include "ILexer.h"
39 #include "Debugging.h"
40 #include "Geometry.h"
41 #include "Platform.h"
43 #include "CharacterType.h"
44 #include "CharacterCategoryMap.h"
45 #include "Position.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"
52 #include "PerLine.h"
53 #include "KeyMap.h"
54 #include "Indicator.h"
55 #include "LineMarker.h"
56 #include "Style.h"
57 #include "ViewStyle.h"
58 #include "CharClassify.h"
59 #include "Decoration.h"
60 #include "CaseFolder.h"
61 #include "Document.h"
62 #include "UniConversion.h"
63 #include "DBCS.h"
64 #include "Selection.h"
65 #include "PositionCache.h"
66 #include "EditModel.h"
67 #include "MarginView.h"
68 #include "EditView.h"
69 #include "Editor.h"
70 #include "ElapsedPeriod.h"
72 using namespace Scintilla;
73 using namespace Scintilla::Internal;
75 namespace {
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 {
92 return
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 {
101 return
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))
120 return false;
122 return true;
125 Editor::Editor() : durationWrapOneByte(0.000001, 0.00000001, 0.00001) {
126 ctrlID = 0;
128 stylesValid = false;
129 technology = Technology::Default;
130 scaleRGBAImage = 100.0f;
132 cursorMode = CursorShape::Normal;
134 errorStatus = Status::Ok;
135 mouseDownCaptures = true;
136 mouseWheelCaptures = true;
138 lastClickTime = 0;
139 doubleClickCloseThreshold = Point(3, 3);
140 dwellDelay = TimeForever;
141 ticksToDwell = TimeForever;
142 dwelling = false;
143 ptMouseLast.x = 0;
144 ptMouseLast.y = 0;
145 inDragDrop = DragDrop::none;
146 dropWentOutside = false;
147 posDrop = SelectionPosition(Sci::invalidPosition);
148 hotSpotClickPos = Sci::invalidPosition;
149 selectionUnit = TextUnit::character;
151 lastXChosen = 0;
152 lineAnchorPos = 0;
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 };
163 searchAnchor = 0;
165 xCaretMargin = 50;
166 horizontalScrollBarVisible = true;
167 scrollWidth = 2000;
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;
181 topLine = 0;
182 posTopLine = 0;
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();
209 Editor::~Editor() {
210 pdoc->RemoveWatcher(this, nullptr);
213 void Editor::Finalise() {
214 SetIdle(false);
215 CancelModes();
218 void Editor::SetRepresentations() {
219 reprs.SetDefaultRepresentations(pdoc->dbcsCodePage);
222 void Editor::DropGraphics() noexcept {
223 marginView.DropGraphics();
224 view.DropGraphics();
227 void Editor::InvalidateStyleData() noexcept {
228 stylesValid = false;
229 vs.technology = technology;
230 DropGraphics();
231 view.llc.Invalidate(LineLayout::ValidLevel::invalid);
232 view.posCache->Clear();
235 void Editor::InvalidateStyleRedraw() {
236 NeedWrapping();
237 InvalidateStyleData();
238 Redraw();
241 void Editor::RefreshStyleData() {
242 if (!stylesValid) {
243 stylesValid = true;
244 AutoSurface surface(this);
245 if (surface) {
246 vs.Refresh(*surface, pdoc->tabInChars);
248 SetScrollBars();
249 SetRectangularRange();
253 bool Editor::HasMarginWindow() const noexcept {
254 return wMargin.Created();
257 Point Editor::GetVisibleOriginInMain() const {
258 return Point(0, 0);
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;
267 } else {
268 ptDocument.x += xOffset;
269 ptDocument.y += topLine * vs.lineHeight;
271 return ptDocument;
274 Sci::Line Editor::TopLineOfMain() const noexcept {
275 if (HasMarginWindow())
276 return 0;
277 else
278 return topLine;
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;
298 return rc;
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;
310 if (retVal < 1)
311 return 1;
312 else
313 return retVal;
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();
320 if (endAtLastLine) {
321 retVal -= LinesOnScreen();
322 } else {
323 retVal--;
325 if (retVal < 0) {
326 return 0;
327 } else {
328 return retVal;
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());
337 } else {
338 // If not at end of line then set offset to 0
339 if (!pdoc->IsLineEndPosition(sp.Position()))
340 sp.SetVirtualSpace(0);
341 return sp;
345 Point Editor::LocationFromPosition(SelectionPosition pos, PointEnd pe) {
346 const PRectangle rcClient = GetTextRectangle();
347 RefreshStyleData();
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) {
362 RefreshStyleData();
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);
375 if (pt.y < 0)
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) {
393 RefreshStyleData();
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) {
453 return;
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()) {
469 return;
472 if (HasMarginWindow() && markersInText) {
473 Redraw();
474 return;
476 if (redrawPendingMargin) {
477 return;
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;
485 if (line != -1) {
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;
491 rcLine.top -= delta;
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;
500 if (!allAfter)
501 rcMarkers.bottom = rcLine.bottom;
502 if (rcMarkers.Empty())
503 return;
505 if (HasMarginWindow()) {
506 const Point ptOrigin = GetVisibleOriginInMain();
507 rcMarkers.Move(-ptOrigin.x, -ptOrigin.y);
508 wMargin.InvalidateRectangle(rcMarkers);
509 } else {
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();
523 PRectangle rc;
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);
533 return rc;
536 void Editor::InvalidateRange(Sci::Position start, Sci::Position end) {
537 if (redrawPendingText) {
538 return;
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 {
548 return sel.Empty();
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) {
564 xCaret = xAnchor;
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);
580 else
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);
591 } else {
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())));
630 } else {
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();
652 ClaimSelection();
653 SetHoverIndicatorPosition(sel.MainCaret());
655 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
656 RedrawSelMargin();
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()) {
673 sel.Rectangular() =
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);
678 } else {
679 sel.RangeMain() =
680 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
682 ClaimSelection();
683 SetHoverIndicatorPosition(sel.MainCaret());
685 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
686 RedrawSelMargin();
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);
697 sel.Clear();
698 sel.RangeMain() = rangeNew;
699 SetRectangularRange();
700 ClaimSelection();
701 SetHoverIndicatorPosition(sel.MainCaret());
703 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
704 RedrawSelMargin();
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);
720 } else {
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));
739 } else {
740 // No overlap
741 searchRanges.push_back(rangeTarget);
744 for (const Range range : searchRanges) {
745 Sci::Position searchStart = range.start;
746 const Sci::Position searchEnd = range.end;
747 for (;;) {
748 Sci::Position lengthFound = selectedText.length();
749 const Sci::Position pos = pdoc->FindText(searchStart, searchEnd,
750 selectedText.c_str(), searchFlags, &lengthFound);
751 if (pos >= 0) {
752 sel.AddSelection(SelectionRange(pos + lengthFound, pos));
753 ContainerNeedsUpdate(Update::Selection);
754 ScrollRange(sel.RangeMain());
755 Redraw();
756 if (addNumber == AddNumber::one)
757 return;
758 searchStart = pos + lengthFound;
759 } else {
760 break;
767 bool Editor::RangeContainsProtected(Sci::Position start, Sci::Position end) const noexcept {
768 if (vs.ProtectionActive()) {
769 if (start > end) {
770 std::swap(start, end);
772 for (Sci::Position pos = start; pos < end; pos++) {
773 if (vs.styles[pdoc->StyleIndexAt(pos)].IsProtected())
774 return true;
777 return false;
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())) {
784 return true;
787 return false;
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()) {
802 if (moveDir > 0) {
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()))
806 pos.Add(1);
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()))
812 pos.Add(-1);
816 return pos;
819 void Editor::MovedCaret(SelectionPosition newPos, SelectionPosition previousPos,
820 bool ensureVisible, CaretPolicies policies) {
821 const Sci::Line currentLine = pdoc->SciLineFromPosition(newPos.Position());
822 if (ensureVisible) {
823 // In case in need of wrapping to ensure DisplayFromDoc works.
824 if (currentLine >= wrapPending.start) {
825 if (WrapLines(WrapScope::wsAll)) {
826 Redraw();
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);
835 } else {
836 SetXYScroll(newXY);
840 ShowCaretAtCurrentPosition();
841 NotifyCaretMove();
843 ClaimSelection();
844 SetHoverIndicatorPosition(sel.MainCaret());
845 QueueIdleWork(WorkItems::updateUI);
847 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
848 RedrawSelMargin();
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();
868 sel.Clear();
869 sel.Rectangular() = rangeMain;
871 if (selt != Selection::SelTypes::none) {
872 sel.selType = selt;
874 if (selt != Selection::SelTypes::none || sel.MoveExtends()) {
875 SetSelection(newPos);
876 } else {
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)) {
892 return pos;
893 } else {
894 Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
895 if (moveDir > 0) {
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)));
900 } else {
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
929 #ifndef UNDER_CE
930 const Sci::Line linesToMove = topLine - topLineNew;
931 const bool performBlit = (std::abs(linesToMove) <= 10) && (paintState == PaintState::notPainting);
932 willRedrawAll = !performBlit;
933 #endif
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);
938 #ifndef UNDER_CE
939 // Perform redraw rather than scroll if many lines would be redrawn anyway.
940 if (performBlit) {
941 ScrollText(linesToMove);
942 } else {
943 Redraw();
945 willRedrawAll = false;
946 #else
947 Redraw();
948 #endif
949 if (moveThumb) {
950 SetVerticalScrollPos();
955 void Editor::ScrollText(Sci::Line /* linesToMove */) {
956 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
957 Redraw();
960 void Editor::HorizontalScrollTo(int xPos) {
961 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
962 if (xPos < 0)
963 xPos = 0;
964 if (!Wrapping() && (xOffset != xPos)) {
965 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()) {
987 return;
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) {
1014 return;
1017 UndoGroup ug(pdoc);
1019 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
1020 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
1021 ClearSelection();
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);
1031 if (appendEol)
1032 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
1033 ClearSelection();
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);
1041 if (appendEol) {
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.
1105 | | | | |
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()) {
1134 return newXY;
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;
1153 if (bStrict) {
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;
1160 } else {
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);
1164 if (bEven) {
1165 yMarginB = yMarginT;
1166 } else {
1167 yMarginB = linesOnScreen - yMarginT - 1;
1170 yMoveT = yMarginT;
1171 if (bEven) {
1172 if (bJump) {
1173 yMoveT = std::clamp<Sci::Line>(policies.y.slop * 3, 1, halfScreen);
1175 yMoveB = yMoveT;
1176 } else {
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);
1189 if (bEven) {
1190 yMoveB = yMoveT;
1191 } else {
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;
1202 } else { // No slop
1203 if (!bStrict && !bJump) {
1204 // Minimal move
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
1210 if (bEven) {
1211 newXY.topLine = lineCaret - linesOnScreen + 1;
1212 } else {
1213 newXY.topLine = lineCaret;
1216 } else { // Strict or going out of display
1217 if (bEven) {
1218 // Always centre caret
1219 newXY.topLine = lineCaret - halfScreen;
1220 } else {
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());
1232 } else {
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
1250 int xMoveL = 0;
1251 int xMoveR = 0;
1252 if (bStrict) {
1253 int xMarginL = 0;
1254 int xMarginR = 0;
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;
1259 } else {
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);
1263 if (bEven) {
1264 xMarginL = xMarginR;
1265 } else {
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);
1272 } else {
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;
1279 } else {
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;
1287 } else {
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);
1295 if (bEven) {
1296 xMoveL = xMoveR;
1297 } else {
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;
1308 } else { // No slop
1309 if (bStrict ||
1310 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1311 // Strict or going out of display
1312 if (bEven) {
1313 // Centre caret
1314 newXY.xOffset += static_cast<int>(pt.x - rcClient.left - halfScreen);
1315 } else {
1316 // Put caret on right
1317 newXY.xOffset += static_cast<int>(pt.x - rcClient.right + 1);
1319 } else {
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
1323 if (bEven) {
1324 newXY.xOffset -= static_cast<int>(rcClient.left - pt.x);
1325 } else {
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);
1351 } else {
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) {
1360 newXY.xOffset = 0;
1364 return newXY;
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());
1381 SetScrollBars();
1384 SetHorizontalScrollPos();
1386 Redraw();
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),
1400 caretPolicies));
1403 void Editor::ShowCaretAtCurrentPosition() {
1404 if (hasFocus) {
1405 caret.active = true;
1406 caret.on = true;
1407 FineTickerCancel(TickReason::caret);
1408 if (caret.period > 0)
1409 FineTickerStart(TickReason::caret, caret.period, caret.period/10);
1410 } else {
1411 caret.active = false;
1412 caret.on = false;
1413 FineTickerCancel(TickReason::caret);
1415 InvalidateCaret();
1418 void Editor::DropCaret() {
1419 caret.active = false;
1420 FineTickerCancel(TickReason::caret);
1421 InvalidateCaret();
1424 void Editor::CaretSetPeriod(int period) {
1425 if (caret.period != period) {
1426 caret.period = period;
1427 caret.on = true;
1428 FineTickerCancel(TickReason::caret);
1429 if ((caret.active) && (caret.period > 0))
1430 FineTickerStart(TickReason::caret, caret.period, caret.period/10);
1431 InvalidateCaret();
1435 void Editor::InvalidateCaret() {
1436 if (posDrag.IsValid()) {
1437 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1438 } else {
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()) {
1463 SetIdle(true);
1467 bool Editor::WrapOneLine(Surface *surface, Sci::Line lineToWrap) {
1468 std::shared_ptr<LineLayout> ll = view.RetrieveLineLayout(lineToWrap, *this);
1469 int linesWrapped = 1;
1470 if (ll) {
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);
1480 namespace {
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)) {
1497 threads = 1;
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);
1528 while (true) {
1529 const size_t i = nextIndex.fetch_add(1, std::memory_order_acq_rel);
1530 if (i >= linesBeingWrapped) {
1531 break;
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);
1541 } else {
1542 ll = llTemporary;
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) {
1553 f.wait();
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);
1573 } else {
1574 ll = llLarge;
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)) {
1594 wrapsDone++;
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;
1612 if (!Wrapping()) {
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),
1647 0x2000, 0x200000);
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))
1652 lines--;
1653 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
1658 return false;
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),
1665 0x200, 0x20000);
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());
1680 RefreshStyleData();
1681 AutoSurface surface(this);
1682 if (surface) {
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();
1698 if (wrapOccurred) {
1699 SetScrollBars();
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())) {
1709 UndoGroup ug(pdoc);
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();
1734 UndoGroup ug(pdoc);
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)
1756 return;
1758 RefreshStyleData();
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()) {
1765 return;
1768 PRectangle rcMargin = GetClientRectangle();
1769 const Point ptOrigin = GetVisibleOriginInMain();
1770 rcMargin.Move(0, -ptOrigin.y);
1771 rcMargin.left = 0;
1772 rcMargin.right = static_cast<XYPOSITION>(vs.fixedColumnWidth);
1774 if (!rc.Intersects(rcMargin))
1775 return;
1777 Surface *surface;
1778 if (view.bufferedDraw) {
1779 surface = marginView.pixmapSelMargin.get();
1780 } else {
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);
1821 RefreshStyleData();
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()) {
1835 RefreshStyleData();
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()) {
1844 return;
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.
1853 return;
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");
1881 if (Wrapping()) {
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();
1890 return;
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();
1905 NotifyPainted();
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) {
1912 if (!lParam)
1913 return 0;
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) {
1922 return 0;
1924 return view.FormatRange(draw, chrg, pfr->rc, surface, surfaceMeasure, *this, vs);
1925 } else {
1926 // FormatRangeFull
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) {
1931 return 0;
1933 return view.FormatRange(draw, pfr->chrg, pfr->rc, surface, surfaceMeasure, *this, vs);
1937 long Editor::TextWidth(uptr_t style, const char *text) {
1938 RefreshStyleData();
1939 AutoSurface surface(this);
1940 if (surface) {
1941 return std::lround(surface->WidthText(vs.styles[style].font.get(), text));
1942 } else {
1943 return 1;
1947 // Empty method is overridden on GTK+ to show / hide scrollbars
1948 void Editor::ReconfigureScrollBars() {}
1950 void Editor::ChangeScrollBars() {
1951 RefreshStyleData();
1953 const Sci::Line nMax = MaxScrollPos();
1954 const Sci::Line nPage = LinesOnScreen();
1955 const bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
1956 if (modified) {
1957 DwellEnd(true);
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();
1965 Redraw();
1967 if (modified) {
1968 if (!AbandonPaint())
1969 Redraw();
1971 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
1974 void Editor::SetScrollBars() {
1975 // Overridden on GTK to defer to idle
1976 ChangeScrollBars();
1979 void Editor::ChangeSize() {
1980 DropGraphics();
1981 SetScrollBars();
1982 if (Wrapping()) {
1983 PRectangle rcTextArea = GetClientRectangle();
1984 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1985 rcTextArea.right -= vs.rightMarginWidth;
1986 if (wrapWidth != rcTextArea.Width()) {
1987 NeedWrapping();
1988 Redraw();
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);
1999 } else {
2000 const std::string spaceText(virtualSpace, ' ');
2001 const Sci::Position lengthInserted = pdoc->InsertString(position, spaceText);
2002 position += lengthInserted;
2005 return position;
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) {
2027 if (sv.empty()) {
2028 return;
2030 FilterSelections();
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();
2055 } else {
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
2075 if (Wrapping()) {
2076 AutoSurface surface(this);
2077 if (surface) {
2078 if (WrapOneLine(surface, pdoc->SciLineFromPosition(positionInsert))) {
2079 wrapOccurred = true;
2086 if (wrapOccurred) {
2087 SetScrollBars();
2088 SetVerticalScrollPos();
2089 Redraw();
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))) {
2098 SetLastXChosen();
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]);
2107 } else {
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.
2113 } else {
2114 unsigned int utf32[1] = { 0 };
2115 UTF32FromUTF8(sv, utf32, std::size(utf32));
2116 ch = utf32[0];
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.
2129 FilterSelections();
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();
2139 } else {
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);
2158 } else {
2159 // MultiPaste::Each
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();
2168 } else {
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);
2195 } else {
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);
2208 } else {
2209 InsertPaste(text, len);
2214 void Editor::ClearSelection(bool retainMultipleSelections) {
2215 if (!sel.IsRectangular() && !retainMultipleSelections)
2216 FilterSelections();
2217 UndoGroup ug(pdoc);
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();
2230 ClaimSelection();
2231 SetHoverIndicatorPosition(sel.MainCaret());
2234 void Editor::ClearAll() {
2236 UndoGroup ug(pdoc);
2237 if (0 != pdoc->Length()) {
2238 pdoc->DeleteChars(0, pdoc->Length());
2240 if (!pdoc->IsReadOnly()) {
2241 pcs->Clear();
2242 pdoc->AnnotationClearAll();
2243 pdoc->EOLAnnotationClearAll();
2244 pdoc->MarginClearAll();
2248 view.ClearAllTabstops();
2250 sel.Clear();
2251 SetTopLine(0);
2252 SetVerticalScrollPos();
2253 InvalidateStyleRedraw();
2256 void Editor::ClearDocumentStyle() {
2257 pdoc->decorations->DeleteLexerDecorations();
2258 pdoc->StartStyling(0);
2259 pdoc->SetStyleFor(pdoc->Length(), 0);
2260 pcs->ShowAll();
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()) {
2274 Copy();
2275 ClearSelection();
2279 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, Sci::Position len) {
2280 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
2281 return;
2283 sel.Clear();
2284 sel.RangeMain() = SelectionRange(pos);
2285 Sci::Line line = pdoc->SciLineFromPosition(sel.MainCaret());
2286 UndoGroup ug(pdoc);
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]))
2291 len--;
2292 for (Sci::Position i = 0; i < len; i++) {
2293 if (IsEOLCharacter(ptr[i])) {
2294 if ((ptr[i] == '\r') || (!prevCr))
2295 line++;
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) {
2304 assert(pdoc);
2305 const Sci::Position lengthInserted = pdoc->InsertString(sel.MainCaret(), " ", 1);
2306 sel.RangeMain().caret.Add(lengthInserted);
2309 prevCr = ptr[i] == '\r';
2310 } else {
2311 const Sci::Position lengthInserted = pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
2312 sel.RangeMain().caret.Add(lengthInserted);
2313 prevCr = false;
2316 SetEmptySelection(pos);
2319 bool Editor::CanPaste() {
2320 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
2323 void Editor::Clear() {
2324 // If multiple selections, don't delete EOLS
2325 if (sel.Empty()) {
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()));
2338 else
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
2345 } else {
2346 sel.Range(r).ClearVirtualSpace();
2349 } else {
2350 ClearSelection();
2352 sel.RemoveDuplicates();
2353 ShowCaretAtCurrentPosition(); // Avoid blinking
2356 void Editor::SelectAll() {
2357 sel.Clear();
2358 SetSelection(0, pdoc->Length());
2359 Redraw();
2362 void Editor::Undo() {
2363 if (pdoc->CanUndo()) {
2364 InvalidateCaret();
2365 const Sci::Position newPos = pdoc->Undo();
2366 if (newPos >= 0)
2367 SetEmptySelection(newPos);
2368 EnsureCaretVisible();
2372 void Editor::Redo() {
2373 if (pdoc->CanRedo()) {
2374 const Sci::Position newPos = pdoc->Redo();
2375 if (newPos >= 0)
2376 SetEmptySelection(newPos);
2377 EnsureCaretVisible();
2381 void Editor::DelCharBack(bool allowLineStartDeletion) {
2382 RefreshStyleData();
2383 if (!sel.IsRectangular())
2384 FilterSelections();
2385 if (sel.IsRectangular())
2386 allowLineStartDeletion = false;
2387 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
2388 if (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());
2394 } else {
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);
2409 } else {
2410 pdoc->DelCharBack(sel.Range(r).caret.Position());
2414 } else {
2415 sel.Range(r).ClearVirtualSpace();
2418 ThinRectangularRange();
2419 } else {
2420 ClearSelection();
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;
2431 NotifyParent(scn);
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;
2442 NotifyParent(scn);
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;
2456 scn.ch = ch;
2457 scn.characterSource = charSource;
2458 NotifyParent(scn);
2461 void Editor::NotifySavePoint(bool isSavePoint) {
2462 NotificationData scn = {};
2463 if (isSavePoint) {
2464 scn.nmhdr.code = Notification::SavePointReached;
2465 if (changeHistoryOption != ChangeHistoryOption::Disabled) {
2466 Redraw();
2468 } else {
2469 scn.nmhdr.code = Notification::SavePointLeft;
2471 NotifyParent(scn);
2474 void Editor::NotifyModifyAttempt() {
2475 NotificationData scn = {};
2476 scn.nmhdr.code = Notification::ModifyAttemptRO;
2477 NotifyParent(scn);
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;
2486 NotifyParent(scn);
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;
2494 NotifyParent(scn);
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;
2502 NotifyParent(scn);
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;
2510 NotifyParent(scn);
2513 bool Editor::NotifyUpdateUI() {
2514 if (needUpdateUI != Update::None) {
2515 NotificationData scn = {};
2516 scn.nmhdr.code = Notification::UpdateUI;
2517 scn.updated = needUpdateUI;
2518 NotifyParent(scn);
2519 needUpdateUI = Update::None;
2520 return true;
2522 return false;
2525 void Editor::NotifyPainted() {
2526 NotificationData scn = {};
2527 scn.nmhdr.code = Notification::Painted;
2528 NotifyParent(scn);
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;
2539 NotifyParent(scn);
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);
2553 } else {
2554 const FoldLevel levelClick = pdoc->GetFoldLevel(lineClick);
2555 if (LevelIsHeader(levelClick)) {
2556 if (shift) {
2557 // Ensure all children visible
2558 FoldExpand(lineClick, FoldAction::Expand, levelClick);
2559 } else if (ctrl) {
2560 FoldExpand(lineClick, FoldAction::Toggle, levelClick);
2561 } else {
2562 // Toggle this line
2563 FoldLine(lineClick, FoldAction::Toggle);
2567 return true;
2569 NotificationData scn = {};
2570 scn.nmhdr.code = Notification::MarginClick;
2571 scn.modifiers = modifiers;
2572 scn.position = position;
2573 scn.margin = marginClicked;
2574 NotifyParent(scn);
2575 return true;
2576 } else {
2577 return false;
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;
2590 NotifyParent(scn);
2591 return true;
2592 } else {
2593 return false;
2597 void Editor::NotifyNeedShown(Sci::Position pos, Sci::Position len) {
2598 NotificationData scn = {};
2599 scn.nmhdr.code = Notification::NeedShown;
2600 scn.position = pos;
2601 scn.length = len;
2602 NotifyParent(scn);
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);
2611 NotifyParent(scn);
2614 void Editor::NotifyZoom() {
2615 NotificationData scn = {};
2616 scn.nmhdr.code = Notification::Zoom;
2617 NotifyParent(scn);
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);
2636 if (Wrapping()) {
2637 NeedWrapping(lineDoc, lineDoc + lines + 1);
2639 RefreshStyleData();
2640 // Fix up annotation heights
2641 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
2645 namespace {
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;
2652 return position;
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;
2662 } else {
2663 return startDeletion;
2665 } else {
2666 return position;
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)));
2682 } else {
2683 // Could check that change is before last visible line.
2684 Redraw();
2687 if (FlagSet(mh.modificationType, ModificationFlags::ChangeTabStops)) {
2688 Redraw();
2690 if (FlagSet(mh.modificationType, ModificationFlags::LexerState)) {
2691 if (paintState == PaintState::painting) {
2692 CheckForChangeOutsidePaint(
2693 Range(mh.position, mh.position + mh.length));
2694 } else {
2695 Redraw();
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
2706 Redraw();
2707 } else {
2708 InvalidateRange(mh.position, mh.position + mh.length);
2711 if (FlagSet(mh.modificationType, ModificationFlags::ChangeStyle)) {
2712 view.llc.Invalidate(LineLayout::ValidLevel::checkTextAndStyle);
2714 } else {
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);
2754 } else {
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))) {
2763 SetScrollBars();
2765 Redraw();
2768 if (FlagSet(mh.modificationType, ModificationFlags::ChangeEOLAnnotation)) {
2769 if (vs.eolAnnotationVisible != EOLAnnotationVisible::Hidden) {
2770 Redraw();
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) {
2779 SetTopLine(newTop);
2780 SetVerticalScrollPos();
2784 if (paintState == PaintState::notPainting && !CanDeferToLastStep(mh)) {
2785 if (SynchronousStylingToVisible()) {
2786 QueueIdleWork(WorkItems::style, pdoc->Length());
2788 Redraw();
2790 } else {
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)) {
2804 SetScrollBars();
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);
2812 } else {
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)) {
2823 SetScrollBars();
2824 Redraw();
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;
2840 scn.text = mh.text;
2841 scn.length = mh.length;
2842 scn.linesAdded = mh.linesAdded;
2843 scn.line = mh.line;
2844 scn.foldLevelNow = mh.foldLevelNow;
2845 scn.foldLevelPrev = mh.foldLevelPrev;
2846 scn.token = static_cast<int>(mh.token);
2847 scn.annotationLinesAdded = mh.annotationLinesAdded;
2848 NotifyParent(scn);
2852 void Editor::NotifyDeleted(Document *, void *) noexcept {
2853 /* Do nothing */
2856 void Editor::NotifyMacroRecord(Message iMessage, uptr_t wParam, sptr_t lParam) {
2858 // Enumerates all macroable messages
2859 switch (iMessage) {
2860 case Message::Cut:
2861 case Message::Copy:
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:
2899 case Message::Home:
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:
2922 case Message::Tab:
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:
2968 break;
2970 // Filter out all others like display changes. Also, newlines are redundant
2971 // with char insert messages.
2972 case Message::NewLine:
2973 default:
2974 // printf("Filtered out %ld of macro recording\n", iMessage);
2975 return;
2978 // Send notification
2979 NotificationData scn = {};
2980 scn.nmhdr.code = Notification::MacroRecord;
2981 scn.message = iMessage;
2982 scn.wParam = wParam;
2983 scn.lParam = lParam;
2984 NotifyParent(scn);
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());
3019 } else {
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();
3034 Redraw();
3035 } else {
3036 MovePositionTo(newPos, selt);
3040 void Editor::ChangeCaseOfSelection(CaseMapping caseMapping) {
3041 UndoGroup ug(pdoc);
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])
3055 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;
3063 pdoc->DeleteChars(
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,
3070 lengthChange);
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);
3076 else
3077 current.caret.Add(diffSizes);
3079 sel.Range(r) = current;
3085 void Editor::LineTranspose() {
3086 const Sci::Line line = pdoc->SciLineFromPosition(sel.MainCaret());
3087 if (line > 0) {
3088 UndoGroup ug(pdoc);
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;
3113 if (lineDiff <= 0)
3114 return;
3115 UndoGroup ug(pdoc);
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) {
3137 if (sel.Empty()) {
3138 forLine = true;
3140 UndoGroup ug(pdoc);
3141 std::string_view eol;
3142 if (forLine) {
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();
3148 if (forLine) {
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;
3155 if (forLine)
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();
3161 if (forLine) {
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;
3168 else
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));
3187 // Clear each range
3188 if (!sel.Empty()) {
3189 ClearSelection();
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);
3201 countInsertions++;
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));
3217 SetLastXChosen();
3218 SetScrollBars();
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);
3226 int skipLines = 0;
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;
3244 if (lastX < 0) {
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)) {
3255 posNew.Add(-1);
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
3261 // over a line.
3262 Point ptNew = LocationFromPosition(posNew.Position());
3263 while ((posNew.Position() > spStart.Position()) && (ptNew.y > newY)) {
3264 posNew.Add(-1);
3265 posNew.SetVirtualSpace(0);
3266 ptNew = LocationFromPosition(posNew.Position());
3269 return posNew;
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;
3280 } else {
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);
3301 } else {
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) {
3321 Sci::Line lineDoc;
3322 const Sci::Position savedPos = sel.MainCaret();
3323 do {
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)));
3331 break;
3334 } while (!pcs->GetVisible(lineDoc));
3337 Range Editor::RangeDisplayLine(Sci::Line lineVisible) {
3338 RefreshStyleData();
3339 AutoSurface surface(this);
3340 return view.RangeDisplayLine(surface, *this, lineVisible, vs);
3343 Sci::Position Editor::StartEndDisplayLine(Sci::Position pos, bool start) {
3344 RefreshStyleData();
3345 AutoSurface surface(this);
3346 const Sci::Position posRet = view.StartEndDisplayLine(surface, *this, pos, start, vs);
3347 if (posRet == Sci::invalidPosition) {
3348 return pos;
3349 } else {
3350 return posRet;
3354 namespace {
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 {
3365 switch (iMessage) {
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 {
3392 switch (iMessage) {
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:
3402 case Message::Home:
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:
3415 return -1;
3417 default:
3418 return 1;
3422 constexpr bool IsRectExtend(Message iMessage, bool isRectMoveExtends) noexcept {
3423 switch (iMessage) {
3424 case Message::CharLeftRectExtend:
3425 case Message::CharRightRectExtend:
3426 case Message::HomeRectExtend:
3427 case Message::VCHomeRectExtend:
3428 case Message::LineEndRectExtend:
3429 return true;
3430 default:
3431 if (isRectMoveExtends) {
3432 // Handle Message::SetSelectionMode(SelectionMode::Rectangle) and subsequent movements.
3433 switch (iMessage) {
3434 case Message::CharLeftExtend:
3435 case Message::CharRightExtend:
3436 case Message::HomeExtend:
3437 case Message::VCHomeExtend:
3438 case Message::LineEndExtend:
3439 return true;
3440 default:
3441 return false;
3444 return false;
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;
3455 else
3456 return homePos;
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;
3464 else
3465 return homePos;
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
3473 return realEndPos;
3474 else
3475 return endPos;
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;
3501 switch (iMessage) {
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);
3509 break;
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);
3514 } else {
3515 spCaret = SelectionPosition(spCaret.Position() + 1);
3517 break;
3518 case Message::HomeRectExtend:
3519 case Message::HomeExtend: // only when sel.IsRectangular() && sel.MoveExtends()
3520 spCaret = SelectionPosition(
3521 pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3522 break;
3523 case Message::VCHomeRectExtend:
3524 case Message::VCHomeExtend: // only when sel.IsRectangular() && sel.MoveExtends()
3525 spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
3526 break;
3527 case Message::LineEndRectExtend:
3528 case Message::LineEndExtend: // only when sel.IsRectangular() && sel.MoveExtends()
3529 spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3530 break;
3531 default:
3532 break;
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;
3542 switch (iMessage) {
3543 case Message::Home:
3544 selAtLimit = SelectionPosition(
3545 pdoc->LineStart(pdoc->LineFromPosition(selAtLimit.Position())));
3546 break;
3547 case Message::VCHome:
3548 selAtLimit = SelectionPosition(pdoc->VCHomePosition(selAtLimit.Position()));
3549 break;
3550 case Message::LineEnd:
3551 selAtLimit = SelectionPosition(pdoc->LineEndPosition(selAtLimit.Position()));
3552 break;
3553 default:
3554 break;
3556 sel.selType = Selection::SelTypes::stream;
3557 sel.SetSelection(SelectionRange(selAtLimit));
3558 } else {
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;
3566 switch (iMessage) {
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);
3574 break;
3575 case Message::CharRight:
3576 case Message::CharRightExtend:
3577 if (FlagSet(virtualSpaceOptions, VirtualSpace::UserAccessible) && pdoc->IsLineEndPosition(spCaret.Position())) {
3578 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3579 } else {
3580 spCaret = SelectionPosition(spCaret.Position() + 1);
3582 break;
3583 case Message::WordLeft:
3584 case Message::WordLeftExtend:
3585 spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), -1));
3586 break;
3587 case Message::WordRight:
3588 case Message::WordRightExtend:
3589 spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), 1));
3590 break;
3591 case Message::WordLeftEnd:
3592 case Message::WordLeftEndExtend:
3593 spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), -1));
3594 break;
3595 case Message::WordRightEnd:
3596 case Message::WordRightEndExtend:
3597 spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), 1));
3598 break;
3599 case Message::WordPartLeft:
3600 case Message::WordPartLeftExtend:
3601 spCaret = SelectionPosition(pdoc->WordPartLeft(spCaret.Position()));
3602 break;
3603 case Message::WordPartRight:
3604 case Message::WordPartRightExtend:
3605 spCaret = SelectionPosition(pdoc->WordPartRight(spCaret.Position()));
3606 break;
3607 case Message::Home:
3608 case Message::HomeExtend:
3609 spCaret = SelectionPosition(
3610 pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3611 break;
3612 case Message::HomeDisplay:
3613 case Message::HomeDisplayExtend:
3614 spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), true));
3615 break;
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())));
3622 break;
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()));
3627 break;
3628 case Message::VCHomeDisplay:
3629 case Message::VCHomeDisplayExtend:
3630 spCaret = SelectionPosition(VCHomeDisplayPosition(spCaret.Position()));
3631 break;
3632 case Message::VCHomeWrap:
3633 case Message::VCHomeWrapExtend:
3634 spCaret = SelectionPosition(VCHomeWrapPosition(spCaret.Position()));
3635 break;
3636 case Message::LineEnd:
3637 case Message::LineEndExtend:
3638 spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3639 break;
3640 case Message::LineEndDisplay:
3641 case Message::LineEndDisplayExtend:
3642 spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), false));
3643 break;
3644 case Message::LineEndWrap:
3645 case Message::LineEndWrapExtend:
3646 spCaret = SelectionPosition(LineEndWrapPosition(spCaret.Position()));
3647 break;
3649 default:
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
3657 switch (iMessage) {
3658 case Message::CharLeft:
3659 case Message::CharRight:
3660 if (sel.Range(r).Empty()) {
3661 sel.Range(r) = SelectionRange(spCaret);
3662 } else {
3663 sel.Range(r) = SelectionRange(
3664 (iMessage == Message::CharLeft) ? sel.Range(r).Start() : sel.Range(r).End());
3666 break;
3668 case Message::WordLeft:
3669 case Message::WordRight:
3670 case Message::WordLeftEnd:
3671 case Message::WordRightEnd:
3672 case Message::WordPartLeft:
3673 case Message::WordPartRight:
3674 case Message::Home:
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);
3684 break;
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;
3707 break;
3709 default:
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();
3722 SetLastXChosen();
3723 // Need the line moving and so forth from MovePositionTo
3724 return 0;
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++) {
3743 if (leftwards) {
3744 // Delete to the left so first clear the virtual space.
3745 sel.Range(r).ClearVirtualSpace();
3746 } else {
3747 // Delete to the right so first realise the virtual space.
3748 sel.Range(r) = SelectionRange(
3749 RealizeVirtualSpace(sel.Range(r).caret));
3752 Range rangeDelete;
3753 switch (iMessage) {
3754 case Message::DelWordLeft:
3755 rangeDelete = Range(
3756 pdoc->NextWordStart(sel.Range(r).caret.Position(), -1),
3757 sel.Range(r).caret.Position());
3758 break;
3759 case Message::DelWordRight:
3760 rangeDelete = Range(
3761 sel.Range(r).caret.Position(),
3762 pdoc->NextWordStart(sel.Range(r).caret.Position(), 1));
3763 break;
3764 case Message::DelWordRightEnd:
3765 rangeDelete = Range(
3766 sel.Range(r).caret.Position(),
3767 pdoc->NextWordEnd(sel.Range(r).caret.Position(), 1));
3768 break;
3769 case Message::DelLineLeft:
3770 rangeDelete = Range(
3771 pdoc->LineStart(pdoc->LineFromPosition(sel.Range(r).caret.Position())),
3772 sel.Range(r).caret.Position());
3773 break;
3774 case Message::DelLineRight:
3775 rangeDelete = Range(
3776 sel.Range(r).caret.Position(),
3777 pdoc->LineEnd(pdoc->LineFromPosition(sel.Range(r).caret.Position())));
3778 break;
3779 default:
3780 break;
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();
3795 SetLastXChosen();
3796 return 0;
3799 int Editor::KeyCommand(Message iMessage) {
3800 switch (iMessage) {
3801 case Message::LineDown:
3802 CursorUpOrDown(1, Selection::SelTypes::none);
3803 break;
3804 case Message::LineDownExtend:
3805 CursorUpOrDown(1, Selection::SelTypes::stream);
3806 break;
3807 case Message::LineDownRectExtend:
3808 CursorUpOrDown(1, Selection::SelTypes::rectangle);
3809 break;
3810 case Message::ParaDown:
3811 ParaUpOrDown(1, Selection::SelTypes::none);
3812 break;
3813 case Message::ParaDownExtend:
3814 ParaUpOrDown(1, Selection::SelTypes::stream);
3815 break;
3816 case Message::LineScrollDown:
3817 ScrollTo(topLine + 1);
3818 MoveCaretInsideView(false);
3819 break;
3820 case Message::LineUp:
3821 CursorUpOrDown(-1, Selection::SelTypes::none);
3822 break;
3823 case Message::LineUpExtend:
3824 CursorUpOrDown(-1, Selection::SelTypes::stream);
3825 break;
3826 case Message::LineUpRectExtend:
3827 CursorUpOrDown(-1, Selection::SelTypes::rectangle);
3828 break;
3829 case Message::ParaUp:
3830 ParaUpOrDown(-1, Selection::SelTypes::none);
3831 break;
3832 case Message::ParaUpExtend:
3833 ParaUpOrDown(-1, Selection::SelTypes::stream);
3834 break;
3835 case Message::LineScrollUp:
3836 ScrollTo(topLine - 1);
3837 MoveCaretInsideView(false);
3838 break;
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:
3858 case Message::Home:
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:
3882 MovePositionTo(0);
3883 SetLastXChosen();
3884 break;
3885 case Message::DocumentStartExtend:
3886 MovePositionTo(0, Selection::SelTypes::stream);
3887 SetLastXChosen();
3888 break;
3889 case Message::DocumentEnd:
3890 MovePositionTo(pdoc->Length());
3891 SetLastXChosen();
3892 break;
3893 case Message::DocumentEndExtend:
3894 MovePositionTo(pdoc->Length(), Selection::SelTypes::stream);
3895 SetLastXChosen();
3896 break;
3897 case Message::StutteredPageUp:
3898 PageMove(-1, Selection::SelTypes::none, true);
3899 break;
3900 case Message::StutteredPageUpExtend:
3901 PageMove(-1, Selection::SelTypes::stream, true);
3902 break;
3903 case Message::StutteredPageDown:
3904 PageMove(1, Selection::SelTypes::none, true);
3905 break;
3906 case Message::StutteredPageDownExtend:
3907 PageMove(1, Selection::SelTypes::stream, true);
3908 break;
3909 case Message::PageUp:
3910 PageMove(-1);
3911 break;
3912 case Message::PageUpExtend:
3913 PageMove(-1, Selection::SelTypes::stream);
3914 break;
3915 case Message::PageUpRectExtend:
3916 PageMove(-1, Selection::SelTypes::rectangle);
3917 break;
3918 case Message::PageDown:
3919 PageMove(1);
3920 break;
3921 case Message::PageDownExtend:
3922 PageMove(1, Selection::SelTypes::stream);
3923 break;
3924 case Message::PageDownRectExtend:
3925 PageMove(1, Selection::SelTypes::rectangle);
3926 break;
3927 case Message::EditToggleOvertype:
3928 inOverstrike = !inOverstrike;
3929 ContainerNeedsUpdate(Update::Selection);
3930 ShowCaretAtCurrentPosition();
3931 SetIdle(true);
3932 break;
3933 case Message::Cancel: // Cancel any modes - handled in subclass
3934 // Also unselect text
3935 CancelModes();
3936 if ((sel.Count() > 1) && !sel.IsRectangular()) {
3937 // Drop additional selections
3938 InvalidateWholeSelection();
3939 sel.DropAdditionalRanges();
3941 break;
3942 case Message::DeleteBack:
3943 DelCharBack(true);
3944 if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {
3945 SetLastXChosen();
3947 EnsureCaretVisible();
3948 break;
3949 case Message::DeleteBackNotLine:
3950 DelCharBack(false);
3951 if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {
3952 SetLastXChosen();
3954 EnsureCaretVisible();
3955 break;
3956 case Message::Tab:
3957 Indent(true);
3958 if (caretSticky == CaretSticky::Off) {
3959 SetLastXChosen();
3961 EnsureCaretVisible();
3962 ShowCaretAtCurrentPosition(); // Avoid blinking
3963 break;
3964 case Message::BackTab:
3965 Indent(false);
3966 if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {
3967 SetLastXChosen();
3969 EnsureCaretVisible();
3970 ShowCaretAtCurrentPosition(); // Avoid blinking
3971 break;
3972 case Message::NewLine:
3973 NewLine();
3974 break;
3975 case Message::FormFeed:
3976 AddChar('\f');
3977 break;
3978 case Message::ZoomIn:
3979 if (vs.zoomLevel < 20) {
3980 vs.zoomLevel++;
3981 InvalidateStyleRedraw();
3982 NotifyZoom();
3984 break;
3985 case Message::ZoomOut:
3986 if (vs.zoomLevel > -10) {
3987 vs.zoomLevel--;
3988 InvalidateStyleRedraw();
3989 NotifyZoom();
3991 break;
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));
4006 break;
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);
4013 Cut();
4014 SetLastXChosen();
4016 break;
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);
4023 break;
4024 case Message::LineTranspose:
4025 LineTranspose();
4026 break;
4027 case Message::LineReverse:
4028 LineReverse();
4029 break;
4030 case Message::LineDuplicate:
4031 Duplicate(true);
4032 break;
4033 case Message::SelectionDuplicate:
4034 Duplicate(false);
4035 break;
4036 case Message::LowerCase:
4037 ChangeCaseOfSelection(CaseMapping::lower);
4038 break;
4039 case Message::UpperCase:
4040 ChangeCaseOfSelection(CaseMapping::upper);
4041 break;
4042 case Message::ScrollToStart:
4043 ScrollTo(0);
4044 break;
4045 case Message::ScrollToEnd:
4046 ScrollTo(MaxScrollPos());
4047 break;
4048 default:
4049 break;
4051 return 0;
4054 int Editor::KeyDefault(Keys, KeyMod) {
4055 return 0;
4058 int Editor::KeyDownWithModifiers(Keys key, KeyMod modifiers, bool *consumed) {
4059 DwellEnd(false);
4060 const Message msg = kmap.Find(key, modifiers);
4061 if (msg != static_cast<Message>(0)) {
4062 if (consumed)
4063 *consumed = true;
4064 return static_cast<int>(WndProc(msg, 0, 0));
4065 } else {
4066 if (consumed)
4067 *consumed = false;
4068 return KeyDefault(key, modifiers);
4072 void Editor::Indent(bool forwards) {
4073 UndoGroup ug(pdoc);
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) {
4080 if (forwards) {
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)) &&
4084 pdoc->tabIndents) {
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);
4090 } else {
4091 if (pdoc->useTabs) {
4092 const Sci::Position lengthInserted = pdoc->InsertString(caretPosition, "\t", 1);
4093 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
4094 } else {
4095 int numSpaces = (pdoc->tabInChars) -
4096 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
4097 if (numSpaces < 1)
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);
4104 } else {
4105 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
4106 pdoc->tabIndents) {
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);
4111 } else {
4112 Sci::Position newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
4113 pdoc->tabInChars;
4114 if (newColumn < 0)
4115 newColumn = 0;
4116 Sci::Position newPos = caretPosition;
4117 while (pdoc->GetColumn(newPos) > newColumn)
4118 newPos--;
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));
4137 else
4138 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1),
4139 pdoc->LineStart(lineOfAnchor));
4140 } else {
4141 if (anchorPosOnLine == 0)
4142 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos),
4143 pdoc->LineStart(lineOfAnchor));
4144 else
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());
4171 try {
4172 const Sci::Position pos = pdoc->FindText(
4173 static_cast<Sci::Position>(ft->chrg.cpMin),
4174 static_cast<Sci::Position>(ft->chrg.cpMax),
4175 ft->lpstrText,
4176 static_cast<FindOption>(wParam),
4177 &lengthFound);
4178 if (pos != -1) {
4179 ft->chrgText.cpMin = static_cast<Sci_PositionCR>(pos);
4180 ft->chrgText.cpMax = static_cast<Sci_PositionCR>(pos + lengthFound);
4182 return pos;
4183 } catch (RegexError &) {
4184 errorStatus = Status::RegEx;
4185 return -1;
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());
4202 try {
4203 const Sci::Position pos = pdoc->FindText(
4204 ft->chrg.cpMin,
4205 ft->chrg.cpMax,
4206 ft->lpstrText,
4207 static_cast<FindOption>(wParam),
4208 &lengthFound);
4209 if (pos != -1) {
4210 ft->chrgText.cpMin = pos;
4211 ft->chrgText.cpMax = pos + lengthFound;
4213 return pos;
4214 } catch (RegexError &) {
4215 errorStatus = Status::RegEx;
4216 return -1;
4221 * Relocatable search support : Searches relative to current selection
4222 * point and sets the selection to the found text range with
4223 * each search.
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());
4251 try {
4252 if (iMessage == Message::SearchNext) {
4253 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
4254 static_cast<FindOption>(wParam),
4255 &lengthFound);
4256 } else {
4257 pos = pdoc->FindText(searchAnchor, 0, txt,
4258 static_cast<FindOption>(wParam),
4259 &lengthFound);
4261 } catch (RegexError &) {
4262 errorStatus = Status::RegEx;
4263 return Sci::invalidPosition;
4265 if (pos != Sci::invalidPosition) {
4266 SetSelection(pos, pos + lengthFound);
4269 return pos;
4272 std::string Editor::CaseMapString(const std::string &s, CaseMapping caseMapping) {
4273 std::string ret(s);
4274 for (char &ch : ret) {
4275 switch (caseMapping) {
4276 case CaseMapping::upper:
4277 ch = MakeUpperCase(ch);
4278 break;
4279 case CaseMapping::lower:
4280 ch = MakeLowerCase(ch);
4281 break;
4282 default: // no action
4283 break;
4286 return ret;
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());
4298 try {
4299 const Sci::Position pos = pdoc->FindText(targetRange.start.Position(), targetRange.end.Position(), text,
4300 searchFlags,
4301 &lengthFound);
4302 if (pos != -1) {
4303 targetRange.start.SetPosition(pos);
4304 targetRange.end.SetPosition(pos + lengthFound);
4306 return pos;
4307 } catch (RegexError &) {
4308 errorStatus = Status::RegEx;
4309 return -1;
4313 void Editor::GoToLine(Sci::Line lineNo) {
4314 if (lineNo > pdoc->LinesTotal())
4315 lineNo = pdoc->LinesTotal();
4316 if (lineNo < 0)
4317 lineNo = 0;
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)
4326 return false;
4327 if (std::abs(ptDifference.y) > threshold.y)
4328 return false;
4329 return true;
4332 std::string Editor::RangeText(Sci::Position start, Sci::Position end) const {
4333 if (start < end) {
4334 const Sci::Position len = end - start;
4335 std::string ret(len, '\0');
4336 pdoc->GetCharRange(ret.data(), start, len);
4337 return ret;
4339 return std::string();
4342 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
4343 if (sel.Empty()) {
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);
4357 } else {
4358 std::string text;
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 &current : 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);
4396 posDrop = newPos;
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);
4405 caret.on = true;
4406 FineTickerCancel(TickReason::caret);
4407 if ((caret.active) && (caret.period > 0) && (newPos.Position() < 0))
4408 FineTickerStart(TickReason::caret, caret.period, caret.period/10);
4409 InvalidateCaret();
4410 posDrag = newPos;
4411 InvalidateCaret();
4415 void Editor::DisplayCursor(Window::Cursor c) {
4416 if (cursorMode == CursorShape::Normal)
4417 wMain.SetCursor(c);
4418 else
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();
4450 UndoGroup ug(pdoc);
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());
4460 } else {
4461 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
4465 } else {
4466 if (position > selStart) {
4467 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
4470 ClearSelection();
4472 position = positionAfterDeletion;
4474 std::string convertedText = Document::TransformLineEnds(value, lengthValue, pdoc->eolMode);
4476 if (rectangular) {
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);
4480 } else {
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))
4507 return true;
4509 return false;
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)) {
4518 bool hit = true;
4519 if (pos == range.Start()) {
4520 // see if just before selection
4521 if (pt.x < ptPos.x) {
4522 hit = false;
4525 if (pos == range.End()) {
4526 // see if just after selection
4527 if (pt.x > ptPos.x) {
4528 hit = false;
4531 if (hit)
4532 return true;
4535 return false;
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);
4547 } else {
4548 return false;
4552 Window::Cursor Editor::GetMarginCursor(Point pt) const noexcept {
4553 int x = 0;
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);
4557 x += m.width;
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;
4570 if (wholeLine) {
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_);
4583 } else {
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);
4616 } else {
4617 // Select only the anchored word
4618 if (pos >= originalAnchorPos)
4619 TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
4620 else
4621 TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
4625 void Editor::DwellEnd(bool mouseMoved) {
4626 if (mouseMoved)
4627 ticksToDwell = dwellDelay;
4628 else
4629 ticksToDwell = TimeForever;
4630 if (dwelling && (dwellDelay < TimeForever)) {
4631 dwelling = false;
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);
4642 DwellEnd(true);
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);
4654 ptMouseLast = pt;
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))
4666 return;
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) {
4673 SelectAll();
4674 lastClickTime = curTime;
4675 lastClick = pt;
4676 return;
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;
4688 if (inSelMargin) {
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;
4698 } else {
4699 if (selectionUnit == TextUnit::character) {
4700 selectionUnit = TextUnit::word;
4701 doubleClick = true;
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;
4706 } else {
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);
4724 } else {
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);
4730 } else {
4731 // Anchor at start of line; select nothing to begin with.
4732 startWord = charPos;
4733 endWord = 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);
4745 } else {
4746 SetEmptySelection(sel.MainCaret());
4748 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
4749 if (doubleClick) {
4750 NotifyDoubleClick(pt, modifiers);
4751 if (PositionIsHotspot(newCharPos.Position()))
4752 NotifyHotSpotDoubleClicked(newCharPos.Position(), modifiers);
4754 } else { // Single click
4755 if (inSelMargin) {
4756 if (sel.IsRectangular() || (sel.Count() > 1)) {
4757 InvalidateWholeSelection();
4758 sel.Clear();
4760 sel.selType = Selection::SelTypes::stream;
4761 if (!shift) {
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);
4766 } else {
4767 // Single shift+click in margin: select from line anchor to clicked line
4768 if (sel.MainAnchor() > sel.MainCaret())
4769 lineAnchorPos = sel.MainAnchor() - 1;
4770 else
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);
4784 } else {
4785 if (PointIsHotspot(pt)) {
4786 NotifyHotSpotClicked(newCharPos.Position(), modifiers);
4787 hotSpotClickPos = newCharPos.Position();
4789 if (!shift) {
4790 if (PointInSelection(pt) && !SelectionEmpty())
4791 inDragDrop = DragDrop::initial;
4792 else
4793 inDragDrop = DragDrop::none;
4795 SetMouseCapture(true);
4796 FineTickerStart(TickReason::scroll, 100, 10);
4797 if (inDragDrop != DragDrop::initial) {
4798 SetDragPosition(SelectionPosition(Sci::invalidPosition));
4799 if (!shift) {
4800 if (ctrl && multipleSelection) {
4801 const SelectionRange range(newPos);
4802 sel.TentativeSelection(range);
4803 InvalidateSelection(range, true);
4804 } else {
4805 InvalidateSelection(SelectionRange(newPos), true);
4806 if (sel.Count() > 1)
4807 Redraw();
4808 if ((sel.Count() > 1) || (sel.selType != Selection::SelTypes::stream))
4809 sel.Clear();
4810 sel.selType = alt ? Selection::SelTypes::rectangle : Selection::SelTypes::stream;
4811 SetSelection(newPos, newPos);
4814 SelectionPosition anchorCurrent = newPos;
4815 if (shift)
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;
4827 lastClick = pt;
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))
4834 return;
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)
4844 return false;
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)
4852 return;
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) {
4863 Redraw();
4867 void Editor::SetHoverIndicatorPoint(Point pt) {
4868 if (!vs.indicatorsDynamic) {
4869 SetHoverIndicatorPosition(Sci::invalidPosition);
4870 } else {
4871 SetHoverIndicatorPosition(PositionFromLocation(pt, true, true));
4875 void Editor::SetHotSpotRange(const Point *pt) {
4876 if (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.
4882 Range hsNew;
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);
4891 hotspot = hsNew;
4892 InvalidateRange(hotspot.start, hotspot.end);
4894 } else {
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) {
4904 DwellEnd(true);
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);
4917 StartDrag();
4919 return;
4922 ptMouseLast = pt;
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)
4935 return;
4936 autoScrollTimer.ticksToWait = autoScrollDelay;
4938 // Adjust selection
4939 if (posDrag.IsValid()) {
4940 SetDragPosition(movePos);
4941 } else {
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);
4954 } else {
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
4968 // being unmade.
4969 } else {
4970 wordSelectInitialCaretPos = -1;
4971 WordSelection(movePos.Position());
4973 } else {
4974 // Continue selecting by line
4975 LineSelection(movePos.Position(), lineAnchorPos, selectionUnit == TextUnit::wholeLine);
4979 // Autoscroll
4980 const Sci::Line lineMove = DisplayFromPosition(movePos.Position());
4981 if (pt.y >= rcClient.bottom) {
4982 ScrollTo(lineMove - LinesOnScreen() + 1);
4983 Redraw();
4984 } else if (pt.y < rcClient.top) {
4985 ScrollTo(lineMove);
4986 Redraw();
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;
5000 } else {
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);
5013 } else {
5014 SetHoverIndicatorPoint(pt);
5015 if (PointIsHotspot(pt)) {
5016 DisplayCursor(Window::Cursor::hand);
5017 SetHotSpotRange(&pt);
5018 } else {
5019 if (hoverIndicatorPos != Sci::invalidPosition)
5020 DisplayCursor(Window::Cursor::hand);
5021 else
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));
5051 } else {
5052 DisplayCursor(Window::Cursor::text);
5053 SetHotSpotRange(nullptr);
5055 ptMouseLast = pt;
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);
5086 } else {
5087 SetEmptySelection(newPos.Position());
5089 drag.Clear();
5091 selectionUnit = TextUnit::character;
5093 } else {
5094 if (selectionUnit == TextUnit::character) {
5095 if (sel.Count() > 1) {
5096 sel.RangeMain() =
5097 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
5098 InvalidateWholeSelection();
5099 } else {
5100 SetSelection(newPos, sel.RangeMain().anchor);
5103 sel.CommitTentative();
5105 SetRectangularRange();
5106 lastClickTime = curTime;
5107 lastClick = pt;
5108 lastXChosen = static_cast<int>(pt.x) + xOffset;
5109 if (sel.selType == Selection::SelTypes::stream) {
5110 SetLastXChosen();
5112 inDragDrop = DragDrop::none;
5113 EnsureCaretVisible(false);
5117 bool Editor::Idle() {
5118 NotifyUpdateUI();
5120 bool needWrap = Wrapping() && wrapPending.NeedsWrap();
5122 if (needWrap) {
5123 // Wrap lines during idle.
5124 WrapLines(WrapScope::wsIdle);
5125 // No more wrapping
5126 needWrap = wrapPending.NeedsWrap();
5127 } else if (needIdleStyling) {
5128 IdleStyle();
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
5134 // called again.
5136 const bool idleDone = !needWrap && !needIdleStyling; // && thatDone && theOtherThingDone...
5138 return !idleDone;
5141 void Editor::TickFor(TickReason reason) {
5142 switch (reason) {
5143 case TickReason::caret:
5144 caret.on = !caret.on;
5145 if (caret.active) {
5146 InvalidateCaret();
5148 break;
5149 case TickReason::scroll:
5150 // Auto scroll
5151 ButtonMoveWithModifiers(ptMouseLast, 0, KeyMod::Norm);
5152 break;
5153 case TickReason::widen:
5154 SetScrollBars();
5155 FineTickerCancel(TickReason::widen);
5156 break;
5157 case TickReason::dwell:
5158 if ((!HaveMouseCapture()) &&
5159 (ptMouseLast.y >= 0)) {
5160 dwelling = true;
5161 NotifyDwelling(ptMouseLast, dwelling);
5163 FineTickerCancel(TickReason::dwell);
5164 break;
5165 default:
5166 // tickPlatform handled by subclass
5167 break;
5171 // FineTickerStart is be overridden by subclasses that support fine ticking so
5172 // this method should never be called.
5173 bool Editor::FineTickerRunning(TickReason) {
5174 assert(false);
5175 return false;
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) {
5181 assert(false);
5184 // FineTickerCancel is be overridden by subclasses that support fine ticking so
5185 // this method should never be called.
5186 void Editor::FineTickerCancel(TickReason) {
5187 assert(false);
5190 void Editor::SetFocusState(bool focusState) {
5191 const bool changing = hasFocus != focusState;
5192 hasFocus = focusState;
5193 if (changing) {
5194 Redraw();
5196 NotifyFocus(hasFocus);
5197 if (!hasFocus) {
5198 CancelModes();
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);
5214 else
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)
5223 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
5239 return posMax;
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),
5248 0x200, 0x20000);
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) {
5266 SetIdle(true);
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);
5278 } else {
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));
5302 NotifyUpdateUI();
5303 workNeeded.Reset();
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) {
5316 if (rc.Empty()) {
5317 return true;
5318 } else {
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.
5327 return false;
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);
5337 if (!r.Valid())
5338 return;
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)) {
5350 AbandonPaint();
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));
5361 braces[0] = pos0;
5363 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
5364 CheckForChangeOutsidePaint(Range(braces[1]));
5365 CheckForChangeOutsidePaint(Range(pos1));
5366 braces[1] = pos1;
5368 bracesMatchStyle = matchStyle;
5369 if (paintState == PaintState::notPainting) {
5370 Redraw();
5375 void Editor::SetAnnotationHeights(Sci::Line start, Sci::Line end) {
5376 if (vs.annotationVisible != AnnotationVisible::Hidden) {
5377 RefreshStyleData();
5378 bool changedHeight = false;
5379 for (Sci::Line line=start; line<end && line<pdoc->LinesTotal(); line++) {
5380 int linesWrapped = 1;
5381 if (Wrapping()) {
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) {
5393 SetScrollBars();
5394 SetVerticalScrollPos();
5395 Redraw();
5400 void Editor::SetDocPointer(Document *document) {
5401 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
5402 pdoc->RemoveWatcher(this, nullptr);
5403 pdoc->Release();
5404 if (!document) {
5405 pdoc = new Document(DocumentOption::Default);
5406 } else {
5407 pdoc = document;
5409 pdoc->AddRef();
5410 pcs = ContractionStateCreate(pdoc->IsLarge());
5412 // Ensure all positions within document
5413 sel.Clear();
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.
5424 pcs->Clear();
5425 pcs->InsertLines(0, pdoc->LinesTotal() - 1);
5426 SetAnnotationHeights(0, pdoc->LinesTotal());
5427 view.llc.Deallocate();
5428 NeedWrapping();
5430 hotspot = Range(Sci::invalidPosition);
5431 hoverIndicatorPos = Sci::invalidPosition;
5433 view.ClearAllTabstops();
5435 pdoc->AddWatcher(this, nullptr);
5436 SetScrollBars();
5437 Redraw();
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);
5452 SetScrollBars();
5454 Redraw();
5458 void Editor::SetEOLAnnotationVisible(EOLAnnotationVisible visible) {
5459 if (vs.eolAnnotationVisible != visible) {
5460 vs.eolAnnotationVisible = visible;
5461 Redraw();
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);
5470 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);
5478 } else {
5479 line = pdoc->GetLastChild(line);
5481 lineStart = line + 1;
5483 line++;
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)) {
5493 RedrawSelMargin();
5497 void Editor::FoldLine(Sci::Line line, FoldAction action) {
5498 if (line >= 0) {
5499 if (action == FoldAction::Toggle) {
5500 if (!LevelIsHeader(pdoc->GetFoldLevel(line))) {
5501 line = pdoc->GetFoldParent(line);
5502 if (line < 0)
5503 return;
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();
5522 } else {
5523 if (!(pcs->GetVisible(line))) {
5524 EnsureLineVisible(line, false);
5525 GoToLine(line);
5527 pcs->SetExpanded(line, true);
5528 ExpandLine(line);
5531 SetScrollBars();
5532 Redraw();
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))
5546 // Nothing to do
5547 return;
5548 const Sci::Line lineMaxSubord = pdoc->GetLastChild(line, LevelNumberPart(level));
5549 line++;
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);
5556 line++;
5558 SetScrollBars();
5559 Redraw();
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)))
5565 return line;
5566 line = pcs->ContractedNext(line+1);
5567 if (line < 0)
5568 return -1;
5571 return -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)) {
5583 Redraw();
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);
5607 SetScrollBars();
5608 Redraw();
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();
5616 Redraw();
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();
5621 Redraw();
5623 } else {
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();
5627 Redraw();
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;
5638 if (!expanding) {
5639 pdoc->EnsureStyledTo(pdoc->Length());
5641 Sci::Line line = 0;
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);
5647 break;
5651 if (expanding) {
5652 pcs->SetVisible(0, maxLine-1, true);
5653 pcs->ExpandAll();
5654 } else {
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);
5663 if (!contractAll) {
5664 line = lineMaxSubord;
5667 } else if (contractAll) {
5668 SetFoldExpanded(line, false);
5673 SetScrollBars();
5674 Redraw();
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)) {
5682 RedrawSelMargin();
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)) {
5698 RedrawSelMargin();
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);
5711 SetScrollBars();
5712 Redraw();
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);
5734 } else {
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');
5745 length = 2;
5746 text = pdoc->SubstituteByPosition(name, &length);
5748 if (tagValue) {
5749 if (text)
5750 memcpy(tagValue, text, length + 1);
5751 else
5752 *tagValue = '\0';
5754 return length;
5757 Sci::Position Editor::ReplaceTarget(ReplaceType replaceType, std::string_view text) {
5758 UndoGroup ug(pdoc);
5759 if (replaceType == ReplaceType::patterns) {
5760 Sci::Position length = text.length();
5761 const char *p = pdoc->SubstituteByPosition(text.data(), &length);
5762 if (!p) {
5763 return 0;
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 {
5801 if (pdoc)
5802 return pdoc->dbcsCodePage;
5803 else
5804 return 0;
5807 std::unique_ptr<Surface> Editor::CreateMeasurementSurface() const {
5808 if (!wMain.GetID()) {
5809 return {};
5811 std::unique_ptr<Surface> surf = Surface::Allocate(technology);
5812 surf->Init(wMain.GetID());
5813 surf->SetMode(CurrentSurfaceMode());
5814 return surf;
5817 std::unique_ptr<Surface> Editor::CreateDrawingSurface(SurfaceID sid, std::optional<Scintilla::Technology> technologyOpt) const {
5818 if (!wMain.GetID()) {
5819 return {};
5821 std::unique_ptr<Surface> surf = Surface::Allocate(technologyOpt ? *technologyOpt : technology);
5822 surf->Init(sid, wMain.GetID());
5823 surf->SetMode(CurrentSurfaceMode());
5824 return surf;
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);
5833 return ll->lines;
5834 } else {
5835 return 1;
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';
5863 return iPlace;
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
5872 buffer[len] = '\0';
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);
5882 switch (iMessage) {
5883 case Message::StyleSetFore:
5884 vs.styles[wParam].fore = ColourRGBA::FromIpRGB(lParam);
5885 break;
5886 case Message::StyleSetBack:
5887 vs.styles[wParam].back = ColourRGBA::FromIpRGB(lParam);
5888 break;
5889 case Message::StyleSetBold:
5890 vs.styles[wParam].weight = lParam != 0 ? FontWeight::Bold : FontWeight::Normal;
5891 break;
5892 case Message::StyleSetWeight:
5893 vs.styles[wParam].weight = static_cast<FontWeight>(lParam);
5894 break;
5895 case Message::StyleSetItalic:
5896 vs.styles[wParam].italic = lParam != 0;
5897 break;
5898 case Message::StyleSetEOLFilled:
5899 vs.styles[wParam].eolFilled = lParam != 0;
5900 break;
5901 case Message::StyleSetSize:
5902 vs.styles[wParam].size = static_cast<int>(lParam * FontSizeMultiplier);
5903 break;
5904 case Message::StyleSetSizeFractional:
5905 vs.styles[wParam].size = static_cast<int>(lParam);
5906 break;
5907 case Message::StyleSetFont:
5908 if (lParam != 0) {
5909 vs.SetStyleFontName(static_cast<int>(wParam), ConstCharPtrFromSPtr(lParam));
5911 break;
5912 case Message::StyleSetUnderline:
5913 vs.styles[wParam].underline = lParam != 0;
5914 break;
5915 case Message::StyleSetCase:
5916 vs.styles[wParam].caseForce = static_cast<Style::CaseForce>(lParam);
5917 break;
5918 case Message::StyleSetCharacterSet:
5919 vs.styles[wParam].characterSet = static_cast<CharacterSet>(lParam);
5920 pdoc->SetCaseFolder(nullptr);
5921 break;
5922 case Message::StyleSetVisible:
5923 vs.styles[wParam].visible = lParam != 0;
5924 break;
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)) {
5930 // valid UTF-8
5931 const int len = classified & UTF8MaskWidth;
5932 for (int i=0; i<len && i<UTF8MaxBytes; i++)
5933 *rep++ = *utf8++;
5935 *rep = 0;
5936 break;
5938 case Message::StyleSetChangeable:
5939 vs.styles[wParam].changeable = lParam != 0;
5940 break;
5941 case Message::StyleSetHotSpot:
5942 vs.styles[wParam].hotspot = lParam != 0;
5943 break;
5944 case Message::StyleSetCheckMonospaced:
5945 vs.styles[wParam].checkMonospaced = lParam != 0;
5946 break;
5947 default:
5948 break;
5950 InvalidateStyleRedraw();
5953 sptr_t Editor::StyleGetMessage(Message iMessage, uptr_t wParam, sptr_t lParam) {
5954 vs.EnsureStyle(wParam);
5955 switch (iMessage) {
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;
5990 default:
5991 break;
5993 return 0;
5996 void Editor::SetSelectionNMessage(Message iMessage, uptr_t wParam, sptr_t lParam) {
5997 if (wParam >= sel.Count()) {
5998 return;
6000 InvalidateRange(sel.Range(wParam).Start().Position(), sel.Range(wParam).End().Position());
6002 switch (iMessage) {
6003 case Message::SetSelectionNCaret:
6004 sel.Range(wParam).caret.SetPosition(lParam);
6005 break;
6007 case Message::SetSelectionNAnchor:
6008 sel.Range(wParam).anchor.SetPosition(lParam);
6009 break;
6011 case Message::SetSelectionNCaretVirtualSpace:
6012 sel.Range(wParam).caret.SetVirtualSpace(lParam);
6013 break;
6015 case Message::SetSelectionNAnchorVirtualSpace:
6016 sel.Range(wParam).anchor.SetVirtualSpace(lParam);
6017 break;
6019 case Message::SetSelectionNStart:
6020 sel.Range(wParam).anchor.SetPosition(lParam);
6021 break;
6023 case Message::SetSelectionNEnd:
6024 sel.Range(wParam).caret.SetPosition(lParam);
6025 break;
6027 default:
6028 break;
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;
6038 if (lParam) {
6039 char *ptr = CharPtrFromSPtr(lParam);
6040 if (val)
6041 memcpy(ptr, val, len+1);
6042 else
6043 *ptr = 0;
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);
6052 if (val)
6053 memcpy(ptr, val, len);
6054 else
6055 *ptr = 0;
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
6064 if (recordingMacro)
6065 NotifyMacroRecord(iMessage, wParam, lParam);
6067 switch (iMessage) {
6069 case Message::GetText: {
6070 if (lParam == 0)
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);
6075 ptr[len] = '\0';
6076 return len;
6079 case Message::SetText: {
6080 if (lParam == 0)
6081 return 0;
6082 UndoGroup ug(pdoc);
6083 pdoc->DeleteChars(0, pdoc->Length());
6084 SetEmptySelection(0);
6085 const char *text = ConstCharPtrFromSPtr(lParam);
6086 pdoc->InsertString(0, text, strlen(text));
6087 return 1;
6090 case Message::GetTextLength:
6091 return pdoc->Length();
6093 case Message::Cut:
6094 Cut();
6095 SetLastXChosen();
6096 break;
6098 case Message::Copy:
6099 Copy();
6100 break;
6102 case Message::CopyAllowLine:
6103 CopyAllowLine();
6104 break;
6106 case Message::VerticalCentreCaret:
6107 VerticalCentreCaret();
6108 break;
6110 case Message::MoveSelectedLinesUp:
6111 MoveSelectedLinesUp();
6112 break;
6114 case Message::MoveSelectedLinesDown:
6115 MoveSelectedLinesDown();
6116 break;
6118 case Message::CopyRange:
6119 CopyRangeToClipboard(PositionFromUPtr(wParam), lParam);
6120 break;
6122 case Message::CopyText:
6123 CopyText(wParam, ConstCharPtrFromSPtr(lParam));
6124 break;
6126 case Message::Paste:
6127 Paste();
6128 if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {
6129 SetLastXChosen();
6131 EnsureCaretVisible();
6132 break;
6134 case Message::ReplaceRectangular: {
6135 UndoGroup ug(pdoc);
6136 if (!sel.Empty()) {
6137 ClearSelection(); // want to replace rectangular selection contents
6139 InsertPasteShape(ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam), PasteShape::rectangular);
6140 break;
6143 case Message::Clear:
6144 Clear();
6145 SetLastXChosen();
6146 EnsureCaretVisible();
6147 break;
6149 case Message::Undo:
6150 Undo();
6151 SetLastXChosen();
6152 break;
6154 case Message::CanUndo:
6155 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
6157 case Message::EmptyUndoBuffer:
6158 pdoc->DeleteUndoHistory();
6159 return 0;
6161 case Message::GetFirstVisibleLine:
6162 return topLine;
6164 case Message::SetFirstVisibleLine:
6165 ScrollTo(LineFromUPtr(wParam));
6166 break;
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;
6175 if (lParam == 0) {
6176 return len;
6178 char *ptr = CharPtrFromSPtr(lParam);
6179 pdoc->GetCharRange(ptr, lineStart, len);
6180 return len;
6183 case Message::GetLineCount:
6184 if (pdoc->LinesTotal() == 0)
6185 return 1;
6186 else
6187 return pdoc->LinesTotal();
6189 case Message::AllocateLines:
6190 pdoc->AllocateLines(wParam);
6191 break;
6193 case Message::GetModify:
6194 return !pdoc->IsSavePoint();
6196 case Message::SetSel: {
6197 Sci::Position nStart = PositionFromUPtr(wParam);
6198 Sci::Position nEnd = lParam;
6199 if (nEnd < 0)
6200 nEnd = pdoc->Length();
6201 if (nStart < 0)
6202 nStart = nEnd; // Remove selection
6203 InvalidateSelection(SelectionRange(nStart, nEnd));
6204 sel.Clear();
6205 sel.selType = Selection::SelTypes::stream;
6206 SetSelection(nEnd, nStart);
6207 EnsureCaretVisible();
6209 break;
6211 case Message::GetSelText: {
6212 SelectionText selectedText;
6213 CopySelectionRange(&selectedText);
6214 if (lParam) {
6215 char *ptr = CharPtrFromSPtr(lParam);
6216 size_t iChar = selectedText.Length();
6217 if (iChar) {
6218 memcpy(ptr, selectedText.Data(), iChar);
6220 ptr[iChar] = '\0';
6222 return selectedText.Length();
6225 case Message::LineFromPosition:
6226 if (PositionFromUPtr(wParam) < 0)
6227 return 0;
6228 return pdoc->LineFromPosition(PositionFromUPtr(wParam));
6230 case Message::PositionFromLine:
6231 if (LineFromUPtr(wParam) < 0)
6232 wParam = pdoc->LineFromPosition(SelectionStart().Position());
6233 if (wParam == 0)
6234 return 0; // Even if there is no text, there is a first line that starts at 0
6235 if (LineFromUPtr(wParam) > pdoc->LinesTotal())
6236 return -1;
6237 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
6238 // return -1;
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())))
6245 return 0;
6246 return pdoc->LineStart(LineFromUPtr(wParam) + 1) - pdoc->LineStart(LineFromUPtr(wParam));
6248 case Message::ReplaceSel: {
6249 if (lParam == 0)
6250 return 0;
6251 UndoGroup ug(pdoc);
6252 ClearSelection();
6253 const char *replacement = ConstCharPtrFromSPtr(lParam);
6254 const Sci::Position lengthInserted = pdoc->InsertString(
6255 sel.MainCaret(), replacement, strlen(replacement));
6256 SetEmptySelection(sel.MainCaret() + lengthInserted);
6257 SetLastXChosen();
6258 EnsureCaretVisible();
6260 break;
6262 case Message::SetTargetStart:
6263 targetRange.start.SetPosition(PositionFromUPtr(wParam));
6264 break;
6266 case Message::GetTargetStart:
6267 return targetRange.start.Position();
6269 case Message::SetTargetStartVirtualSpace:
6270 targetRange.start.SetVirtualSpace(PositionFromUPtr(wParam));
6271 break;
6273 case Message::GetTargetStartVirtualSpace:
6274 return targetRange.start.VirtualSpace();
6276 case Message::SetTargetEnd:
6277 targetRange.end.SetPosition(PositionFromUPtr(wParam));
6278 break;
6280 case Message::GetTargetEnd:
6281 return targetRange.end.Position();
6283 case Message::SetTargetEndVirtualSpace:
6284 targetRange.end.SetVirtualSpace(PositionFromUPtr(wParam));
6285 break;
6287 case Message::GetTargetEndVirtualSpace:
6288 return targetRange.end.VirtualSpace();
6290 case Message::SetTargetRange:
6291 targetRange.start.SetPosition(PositionFromUPtr(wParam));
6292 targetRange.end.SetPosition(lParam);
6293 break;
6295 case Message::TargetWholeDocument:
6296 targetRange.start.SetPosition(0);
6297 targetRange.end.SetPosition(pdoc->Length());
6298 break;
6300 case Message::TargetFromSelection:
6301 targetRange.start = sel.RangeMain().Start();
6302 targetRange.end = sel.RangeMain().End();
6303 break;
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);
6328 break;
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),
6345 0, pdoc->Length());
6347 case Message::PositionRelativeCodeUnits:
6348 return std::clamp<Sci::Position>(pdoc->GetRelativePositionUTF16(
6349 PositionFromUPtr(wParam), lParam),
6350 0, pdoc->Length());
6352 case Message::LineScroll:
6353 ScrollTo(topLine + lParam);
6354 HorizontalScrollTo(xOffset + static_cast<int>(static_cast<int>(wParam) * vs.spaceWidth));
6355 return 1;
6357 case Message::SetXOffset:
6358 xOffset = static_cast<int>(wParam);
6359 ContainerNeedsUpdate(Update::HScroll);
6360 SetHorizontalScrollPos();
6361 Redraw();
6362 break;
6364 case Message::GetXOffset:
6365 return xOffset;
6367 case Message::ChooseCaretX:
6368 SetLastXChosen();
6369 break;
6371 case Message::ScrollCaret:
6372 EnsureCaretVisible();
6373 break;
6375 case Message::SetReadOnly:
6376 pdoc->SetReadOnly(wParam != 0);
6377 return 1;
6379 case Message::GetReadOnly:
6380 return pdoc->IsReadOnly();
6382 case Message::CanPaste:
6383 return CanPaste();
6385 case Message::PointXFromPosition:
6386 if (lParam < 0) {
6387 return 0;
6388 } else {
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:
6395 if (lParam < 0) {
6396 return 0;
6397 } else {
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);
6412 return 0;
6414 case Message::GetTextRangeFull:
6415 if (TextRangeFull *tr = static_cast<TextRangeFull *>(PtrFromSPtr(lParam))) {
6416 return GetTextRange(tr->lpstrText, tr->chrg.cpMin, tr->chrg.cpMax);
6418 return 0;
6420 case Message::HideSelection:
6421 vs.selection.visible = wParam == 0;
6422 Redraw();
6423 break;
6425 case Message::GetSelectionHidden:
6426 return !vs.selection.visible;
6427 break;
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();
6443 break;
6445 case Message::SetMarginRight:
6446 vs.rightMarginWidth = static_cast<int>(lParam);
6447 InvalidateStyleRedraw();
6448 break;
6450 // Control specific messages
6452 case Message::AddText: {
6453 if (lParam == 0)
6454 return 0;
6455 const Sci::Position lengthInserted = pdoc->InsertString(
6456 CurrentPosition(), ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));
6457 SetEmptySelection(sel.MainCaret() + lengthInserted);
6458 return 0;
6461 case Message::AddStyledText:
6462 if (lParam)
6463 AddStyledText(ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));
6464 return 0;
6466 case Message::InsertText: {
6467 if (lParam == 0)
6468 return 0;
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);
6478 return 0;
6481 case Message::ChangeInsertion:
6482 PLATFORM_ASSERT(lParam);
6483 pdoc->ChangeInsertion(ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));
6484 return 0;
6486 case Message::AppendText:
6487 pdoc->InsertString(pdoc->Length(),
6488 ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));
6489 return 0;
6491 case Message::ClearAll:
6492 ClearAll();
6493 return 0;
6495 case Message::DeleteRange:
6496 pdoc->DeleteChars(PositionFromUPtr(wParam), lParam);
6497 return 0;
6499 case Message::ClearDocumentStyle:
6500 ClearDocumentStyle();
6501 return 0;
6503 case Message::SetUndoCollection:
6504 pdoc->SetUndoCollection(wParam != 0);
6505 return 0;
6507 case Message::GetUndoCollection:
6508 return pdoc->IsCollectingUndo();
6510 case Message::BeginUndoAction:
6511 pdoc->BeginUndoAction();
6512 return 0;
6514 case Message::EndUndoAction:
6515 pdoc->EndUndoAction();
6516 return 0;
6518 case Message::GetCaretPeriod:
6519 return caret.period;
6521 case Message::SetCaretPeriod:
6522 CaretSetPeriod(static_cast<int>(wParam));
6523 break;
6525 case Message::GetWordChars:
6526 return pdoc->GetCharsOfClass(CharacterClass::word, UCharPtrFromSPtr(lParam));
6528 case Message::SetWordChars: {
6529 pdoc->SetDefaultCharClasses(false);
6530 if (lParam == 0)
6531 return 0;
6532 pdoc->SetCharClasses(ConstUCharPtrFromSPtr(lParam), CharacterClass::word);
6534 break;
6536 case Message::GetWhitespaceChars:
6537 return pdoc->GetCharsOfClass(CharacterClass::space, UCharPtrFromSPtr(lParam));
6539 case Message::SetWhitespaceChars: {
6540 if (lParam == 0)
6541 return 0;
6542 pdoc->SetCharClasses(ConstUCharPtrFromSPtr(lParam), CharacterClass::space);
6544 break;
6546 case Message::GetPunctuationChars:
6547 return pdoc->GetCharsOfClass(CharacterClass::punctuation, UCharPtrFromSPtr(lParam));
6549 case Message::SetPunctuationChars: {
6550 if (lParam == 0)
6551 return 0;
6552 pdoc->SetCharClasses(ConstUCharPtrFromSPtr(lParam), CharacterClass::punctuation);
6554 break;
6556 case Message::SetCharsDefault:
6557 pdoc->SetDefaultCharClasses(true);
6558 break;
6560 case Message::SetCharacterCategoryOptimization:
6561 pdoc->SetCharacterCategoryOptimization(static_cast<int>(wParam));
6562 break;
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));
6572 break;
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();
6581 Redraw();
6582 } else {
6583 SetSelection(PositionFromUPtr(wParam), sel.MainAnchor());
6585 break;
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();
6594 Redraw();
6595 } else {
6596 SetSelection(sel.MainCaret(), PositionFromUPtr(wParam));
6598 break;
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));
6605 break;
6607 case Message::GetSelectionStart:
6608 return sel.LimitsForRectangularElseMain().start.Position();
6610 case Message::SetSelectionEnd:
6611 SetSelection(PositionFromUPtr(wParam), std::min(sel.MainAnchor(), PositionFromUPtr(wParam)));
6612 break;
6614 case Message::GetSelectionEnd:
6615 return sel.LimitsForRectangularElseMain().end.Position();
6617 case Message::SetEmptySelection:
6618 SetEmptySelection(PositionFromUPtr(wParam));
6619 break;
6621 case Message::SetPrintMagnification:
6622 view.printParameters.magnification = static_cast<int>(wParam);
6623 break;
6625 case Message::GetPrintMagnification:
6626 return view.printParameters.magnification;
6628 case Message::SetPrintColourMode:
6629 view.printParameters.colourMode = static_cast<PrintOption>(wParam);
6630 break;
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;
6637 break;
6639 case Message::GetPrintWrapMode:
6640 return static_cast<sptr_t>(view.printParameters.wrapState);
6642 case Message::GetStyleAt:
6643 if (PositionFromUPtr(wParam) >= pdoc->Length())
6644 return 0;
6645 else
6646 return pdoc->StyleAt(PositionFromUPtr(wParam));
6648 case Message::GetStyleIndexAt:
6649 if (PositionFromUPtr(wParam) >= pdoc->Length())
6650 return 0;
6651 else
6652 return pdoc->StyleIndexAt(PositionFromUPtr(wParam));
6654 case Message::Redo:
6655 Redo();
6656 break;
6658 case Message::SelectAll:
6659 SelectAll();
6660 break;
6662 case Message::SetSavePoint:
6663 pdoc->SetSavePoint();
6664 break;
6666 case Message::GetStyledText:
6667 if (TextRange *tr = static_cast<TextRange *>(PtrFromSPtr(lParam))) {
6668 return GetStyledText(tr->lpstrText, tr->chrg.cpMin, tr->chrg.cpMax);
6670 return 0;
6672 case Message::GetStyledTextFull:
6673 if (TextRangeFull *tr = static_cast<TextRangeFull *>(PtrFromSPtr(lParam))) {
6674 return GetStyledText(tr->lpstrText, tr->chrg.cpMin, tr->chrg.cpMax);
6676 return 0;
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));
6686 break;
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);
6699 Redraw();
6700 break;
6702 case Message::GetTabDrawMode:
6703 return static_cast<sptr_t>(vs.tabDrawMode);
6705 case Message::SetTabDrawMode:
6706 vs.tabDrawMode = static_cast<TabDrawMode>(wParam);
6707 Redraw();
6708 break;
6710 case Message::GetWhitespaceSize:
6711 return vs.whitespaceSize;
6713 case Message::SetWhitespaceSize:
6714 vs.whitespaceSize = static_cast<int>(wParam);
6715 Redraw();
6716 break;
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));
6732 break;
6734 case Message::GotoPos:
6735 SetEmptySelection(PositionFromUPtr(wParam));
6736 EnsureCaretVisible();
6737 break;
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);
6743 if (lParam == 0) {
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);
6749 ptr[len] = '\0';
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);
6761 break;
6763 case Message::SetLineEndTypesAllowed:
6764 if (pdoc->SetLineEndTypesAllowed(static_cast<LineEndType>(wParam))) {
6765 pcs->Clear();
6766 pcs->InsertLines(0, pdoc->LinesTotal() - 1);
6767 SetAnnotationHeights(0, pdoc->LinesTotal());
6768 InvalidateStyleRedraw();
6770 break;
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));
6780 break;
6782 case Message::SetStyling:
6783 if (PositionFromUPtr(wParam) < 0)
6784 errorStatus = Status::Failure;
6785 else
6786 pdoc->SetStyleFor(PositionFromUPtr(wParam), static_cast<char>(lParam));
6787 break;
6789 case Message::SetStylingEx: // Specify a complete styling buffer
6790 if (lParam == 0)
6791 return 0;
6792 pdoc->SetStyles(PositionFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));
6793 break;
6795 case Message::SetBufferedDraw:
6796 view.bufferedDraw = wParam != 0;
6797 break;
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();
6809 break;
6810 #endif
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();
6818 break;
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();
6825 break;
6827 case Message::GetFontQuality:
6828 return static_cast<int>(vs.extraFontFlag) & static_cast<int>(FontQuality::QualityMask);
6830 case Message::SetTabWidth:
6831 if (wParam > 0) {
6832 pdoc->tabInChars = static_cast<int>(wParam);
6833 if (pdoc->indentInChars == 0)
6834 pdoc->actualIndentInChars = pdoc->tabInChars;
6836 InvalidateStyleRedraw();
6837 break;
6839 case Message::GetTabWidth:
6840 return pdoc->tabInChars;
6842 case Message::SetTabMinimumWidth:
6843 SetAppearance(view.tabWidthMinimumPixels, static_cast<int>(wParam));
6844 break;
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);
6854 break;
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);
6861 break;
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;
6870 else
6871 pdoc->actualIndentInChars = pdoc->tabInChars;
6872 InvalidateStyleRedraw();
6873 break;
6875 case Message::GetIndent:
6876 return pdoc->indentInChars;
6878 case Message::SetUseTabs:
6879 pdoc->useTabs = wParam != 0;
6880 InvalidateStyleRedraw();
6881 break;
6883 case Message::GetUseTabs:
6884 return pdoc->useTabs;
6886 case Message::SetLineIndentation:
6887 pdoc->SetLineIndentation(LineFromUPtr(wParam), lParam);
6888 break;
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;
6898 break;
6900 case Message::GetTabIndents:
6901 return pdoc->tabIndents;
6903 case Message::SetBackSpaceUnIndents:
6904 pdoc->backspaceUnindents = wParam != 0;
6905 break;
6907 case Message::GetBackSpaceUnIndents:
6908 return pdoc->backspaceUnindents;
6910 case Message::SetMouseDwellTime:
6911 dwellDelay = static_cast<int>(wParam);
6912 ticksToDwell = dwellDelay;
6913 break;
6915 case Message::GetMouseDwellTime:
6916 return dwellDelay;
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);
6929 break;
6931 case Message::GetIdleStyling:
6932 return static_cast<sptr_t>(idleStyling);
6934 case Message::SetWrapMode:
6935 if (vs.SetWrapState(static_cast<Wrap>(wParam))) {
6936 xOffset = 0;
6937 ContainerNeedsUpdate(Update::HScroll);
6938 InvalidateStyleRedraw();
6939 ReconfigureScrollBars();
6941 break;
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();
6951 break;
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();
6960 break;
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();
6970 break;
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();
6980 break;
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));
6989 break;
6991 case Message::GetLayoutCache:
6992 return static_cast<sptr_t>(view.llc.GetLevel());
6994 case Message::SetPositionCache:
6995 view.posCache->SetSize(wParam);
6996 break;
6998 case Message::GetPositionCache:
6999 return view.posCache->GetSize();
7001 case Message::SetLayoutThreads:
7002 view.SetLayoutThreads(static_cast<unsigned int>(wParam));
7003 break;
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);
7013 SetScrollBars();
7015 break;
7017 case Message::GetScrollWidth:
7018 return scrollWidth;
7020 case Message::SetScrollWidthTracking:
7021 trackLineWidth = wParam != 0;
7022 break;
7024 case Message::GetScrollWidthTracking:
7025 return trackLineWidth;
7027 case Message::LinesJoin:
7028 LinesJoin();
7029 break;
7031 case Message::LinesSplit:
7032 LinesSplit(static_cast<int>(wParam));
7033 break;
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:
7041 RefreshStyleData();
7042 return vs.lineHeight;
7044 case Message::SetEndAtLastLine:
7045 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
7046 if (endAtLastLine != (wParam != 0)) {
7047 endAtLastLine = wParam != 0;
7048 SetScrollBars();
7050 break;
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);
7060 break;
7062 case Message::GetCaretSticky:
7063 return static_cast<sptr_t>(caretSticky);
7065 case Message::ToggleCaretSticky:
7066 caretSticky = (caretSticky == CaretSticky::Off) ? CaretSticky::On : CaretSticky::Off;
7067 break;
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;
7078 SetScrollBars();
7079 ReconfigureScrollBars();
7081 break;
7083 case Message::GetHScrollBar:
7084 return horizontalScrollBarVisible;
7086 case Message::SetVScrollBar:
7087 if (verticalScrollBarVisible != (wParam != 0)) {
7088 verticalScrollBarVisible = wParam != 0;
7089 SetScrollBars();
7090 ReconfigureScrollBars();
7091 if (verticalScrollBarVisible)
7092 SetVerticalScrollPos();
7094 break;
7096 case Message::GetVScrollBar:
7097 return verticalScrollBarVisible;
7099 case Message::SetIndentationGuides:
7100 vs.viewIndentationGuides = static_cast<IndentView>(wParam);
7101 Redraw();
7102 break;
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);
7110 Redraw();
7112 break;
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))) {
7123 pcs->Clear();
7124 pcs->InsertLines(0, pdoc->LinesTotal() - 1);
7125 SetAnnotationHeights(0, pdoc->LinesTotal());
7126 InvalidateStyleRedraw();
7127 SetRepresentations();
7130 break;
7132 case Message::GetCodePage:
7133 return pdoc->dbcsCodePage;
7135 case Message::SetIMEInteraction:
7136 imeInteraction = static_cast<IMEInteraction>(wParam);
7137 break;
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.
7144 break;
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));
7154 break;
7156 case Message::ReleaseLineCharacterIndex:
7157 pdoc->ReleaseLineCharacterIndex(static_cast<LineCharacterIndexType>(wParam));
7158 break;
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();
7173 RedrawSelMargin();
7174 break;
7176 case Message::MarkerSymbolDefined:
7177 if (wParam <= MarkerMax)
7178 return static_cast<sptr_t>(vs.markers[wParam].markType);
7179 else
7180 return 0;
7182 case Message::MarkerSetFore:
7183 if (wParam <= MarkerMax)
7184 vs.markers[wParam].fore = ColourRGBA::FromIpRGB(lParam);
7185 InvalidateStyleData();
7186 RedrawSelMargin();
7187 break;
7188 case Message::MarkerSetBack:
7189 if (wParam <= MarkerMax)
7190 vs.markers[wParam].back = ColourRGBA::FromIpRGB(lParam);
7191 InvalidateStyleData();
7192 RedrawSelMargin();
7193 break;
7194 case Message::MarkerSetBackSelected:
7195 if (wParam <= MarkerMax)
7196 vs.markers[wParam].backSelected = ColourRGBA::FromIpRGB(lParam);
7197 InvalidateStyleData();
7198 RedrawSelMargin();
7199 break;
7200 case Message::MarkerSetForeTranslucent:
7201 if (wParam <= MarkerMax)
7202 vs.markers[wParam].fore = ColourRGBA(static_cast<int>(lParam));
7203 InvalidateStyleData();
7204 RedrawSelMargin();
7205 break;
7206 case Message::MarkerSetBackTranslucent:
7207 if (wParam <= MarkerMax)
7208 vs.markers[wParam].back = ColourRGBA(static_cast<int>(lParam));
7209 InvalidateStyleData();
7210 RedrawSelMargin();
7211 break;
7212 case Message::MarkerSetBackSelectedTranslucent:
7213 if (wParam <= MarkerMax)
7214 vs.markers[wParam].backSelected = ColourRGBA(static_cast<int>(lParam));
7215 InvalidateStyleData();
7216 RedrawSelMargin();
7217 break;
7218 case Message::MarkerSetStrokeWidth:
7219 if (wParam <= MarkerMax)
7220 vs.markers[wParam].strokeWidth = lParam / 100.0f;
7221 InvalidateStyleData();
7222 RedrawSelMargin();
7223 break;
7224 case Message::MarkerEnableHighlight:
7225 marginView.highlightDelimiter.isEnabled = wParam == 1;
7226 RedrawSelMargin();
7227 break;
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);
7233 } else {
7234 SetAppearance(vs.markers[wParam].alpha, static_cast<Alpha>(lParam));
7235 SetAppearance(vs.markers[wParam].layer, Layer::OverText);
7238 break;
7239 case Message::MarkerSetLayer:
7240 if (wParam <= MarkerMax) {
7241 SetAppearance(vs.markers[wParam].layer, static_cast<Layer>(lParam));
7243 break;
7244 case Message::MarkerGetLayer:
7245 if (wParam <= MarkerMax) {
7246 return static_cast<sptr_t>(vs.markers[wParam].layer);
7248 return 0;
7249 case Message::MarkerAdd: {
7250 const int markerID = pdoc->AddMark(LineFromUPtr(wParam), static_cast<int>(lParam));
7251 return markerID;
7253 case Message::MarkerAddSet:
7254 if (lParam != 0)
7255 pdoc->AddMarkSet(LineFromUPtr(wParam), static_cast<int>(lParam));
7256 break;
7258 case Message::MarkerDelete:
7259 pdoc->DeleteMark(LineFromUPtr(wParam), static_cast<int>(lParam));
7260 break;
7262 case Message::MarkerDeleteAll:
7263 pdoc->DeleteAllMarks(static_cast<int>(wParam));
7264 break;
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)
7275 return iLine;
7278 return -1;
7280 case Message::MarkerDefinePixmap:
7281 if (wParam <= MarkerMax) {
7282 vs.markers[wParam].SetXPM(ConstCharPtrFromSPtr(lParam));
7283 vs.CalcLargestMarkerHeight();
7285 InvalidateStyleData();
7286 RedrawSelMargin();
7287 break;
7289 case Message::RGBAImageSetWidth:
7290 sizeRGBAImage.x = static_cast<XYPOSITION>(wParam);
7291 break;
7293 case Message::RGBAImageSetHeight:
7294 sizeRGBAImage.y = static_cast<XYPOSITION>(wParam);
7295 break;
7297 case Message::RGBAImageSetScale:
7298 scaleRGBAImage = static_cast<float>(wParam);
7299 break;
7301 case Message::MarkerDefineRGBAImage:
7302 if (wParam <= MarkerMax) {
7303 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0f, ConstUCharPtrFromSPtr(lParam));
7304 vs.CalcLargestMarkerHeight();
7306 InvalidateStyleData();
7307 RedrawSelMargin();
7308 break;
7310 case Message::SetMarginTypeN:
7311 if (ValidMargin(wParam)) {
7312 vs.ms[wParam].style = static_cast<MarginType>(lParam);
7313 InvalidateStyleRedraw();
7315 break;
7317 case Message::GetMarginTypeN:
7318 if (ValidMargin(wParam))
7319 return static_cast<sptr_t>(vs.ms[wParam].style);
7320 else
7321 return 0;
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();
7332 break;
7334 case Message::GetMarginWidthN:
7335 if (ValidMargin(wParam))
7336 return vs.ms[wParam].width;
7337 else
7338 return 0;
7340 case Message::SetMarginMaskN:
7341 if (ValidMargin(wParam)) {
7342 vs.ms[wParam].mask = static_cast<int>(lParam);
7343 InvalidateStyleRedraw();
7345 break;
7347 case Message::GetMarginMaskN:
7348 if (ValidMargin(wParam))
7349 return vs.ms[wParam].mask;
7350 else
7351 return 0;
7353 case Message::SetMarginSensitiveN:
7354 if (ValidMargin(wParam)) {
7355 vs.ms[wParam].sensitive = lParam != 0;
7356 InvalidateStyleRedraw();
7358 break;
7360 case Message::GetMarginSensitiveN:
7361 if (ValidMargin(wParam))
7362 return vs.ms[wParam].sensitive ? 1 : 0;
7363 else
7364 return 0;
7366 case Message::SetMarginCursorN:
7367 if (ValidMargin(wParam))
7368 vs.ms[wParam].cursor = static_cast<CursorShape>(lParam);
7369 break;
7371 case Message::GetMarginCursorN:
7372 if (ValidMargin(wParam))
7373 return static_cast<sptr_t>(vs.ms[wParam].cursor);
7374 else
7375 return 0;
7377 case Message::SetMarginBackN:
7378 if (ValidMargin(wParam)) {
7379 vs.ms[wParam].back = ColourRGBA::FromIpRGB(lParam);
7380 InvalidateStyleRedraw();
7382 break;
7384 case Message::GetMarginBackN:
7385 if (ValidMargin(wParam))
7386 return vs.ms[wParam].back.OpaqueRGB();
7387 else
7388 return 0;
7390 case Message::SetMargins:
7391 if (wParam < 1000)
7392 vs.ms.resize(wParam);
7393 break;
7395 case Message::GetMargins:
7396 return vs.ms.size();
7398 case Message::StyleClearAll:
7399 vs.ClearStyles();
7400 InvalidateStyleRedraw();
7401 break;
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);
7421 break;
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();
7445 break;
7447 case Message::SetElementColour:
7448 if (vs.SetElementColour(static_cast<Element>(wParam), ColourRGBA(static_cast<int>(lParam)))) {
7449 InvalidateStyleRedraw();
7451 break;
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();
7460 break;
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:
7472 if (lParam) {
7473 vs.SetFontLocaleName(ConstCharPtrFromSPtr(lParam));
7474 InvalidateStyleRedraw();
7476 break;
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);
7484 break;
7486 case SCI_GETSTYLEBITS:
7487 return 8;
7488 #endif
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:
7502 if (wParam) {
7503 if (!vs.elementColours.count(Element::CaretLineBack)) {
7504 vs.elementColours[Element::CaretLineBack] = ColourRGBA(0xFF, 0xFF, 0);
7505 InvalidateStyleRedraw();
7507 } else {
7508 if (vs.ResetElement(Element::CaretLineBack)) {
7509 InvalidateStyleRedraw();
7512 break;
7513 case Message::GetCaretLineVisibleAlways:
7514 return vs.caretLine.alwaysShow;
7515 case Message::SetCaretLineVisibleAlways:
7516 vs.caretLine.alwaysShow = wParam != 0;
7517 InvalidateStyleRedraw();
7518 break;
7520 case Message::GetCaretLineHighlightSubLine:
7521 return vs.caretLine.subLine;
7522 case Message::SetCaretLineHighlightSubLine:
7523 vs.caretLine.subLine = wParam != 0;
7524 InvalidateStyleRedraw();
7525 break;
7527 case Message::GetCaretLineFrame:
7528 return vs.caretLine.frame;
7529 case Message::SetCaretLineFrame:
7530 vs.caretLine.frame = static_cast<int>(wParam);
7531 InvalidateStyleRedraw();
7532 break;
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();
7539 break;
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();
7550 break;
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();
7565 break;
7567 // Folding messages
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))
7581 RedrawSelMargin();
7582 return prev;
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);
7596 SetScrollBars();
7597 Redraw();
7598 break;
7600 case Message::HideLines:
7601 pcs->SetVisible(LineFromUPtr(wParam), lParam, false);
7602 SetScrollBars();
7603 Redraw();
7604 break;
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);
7614 break;
7616 case Message::GetFoldExpanded:
7617 return pcs->GetExpanded(LineFromUPtr(wParam));
7619 case Message::SetAutomaticFold:
7620 foldAutomatic = static_cast<AutomaticFold>(wParam);
7621 break;
7623 case Message::GetAutomaticFold:
7624 return static_cast<sptr_t>(foldAutomatic);
7626 case Message::SetFoldFlags:
7627 foldFlags = static_cast<FoldFlag>(wParam);
7628 Redraw();
7629 break;
7631 case Message::ToggleFoldShowText:
7632 pcs->SetFoldDisplayText(LineFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));
7633 FoldLine(LineFromUPtr(wParam), FoldAction::Toggle);
7634 break;
7636 case Message::FoldDisplayTextSetStyle:
7637 foldDisplayTextStyle = static_cast<FoldDisplayTextStyle>(wParam);
7638 Redraw();
7639 break;
7641 case Message::FoldDisplayTextGetStyle:
7642 return static_cast<sptr_t>(foldDisplayTextStyle);
7644 case Message::SetDefaultFoldDisplayText:
7645 SetDefaultFoldDisplayText(ConstCharPtrFromSPtr(lParam));
7646 Redraw();
7647 break;
7649 case Message::GetDefaultFoldDisplayText:
7650 return StringResult(lParam, GetDefaultFoldDisplayText());
7652 case Message::ToggleFold:
7653 FoldLine(LineFromUPtr(wParam), FoldAction::Toggle);
7654 break;
7656 case Message::FoldLine:
7657 FoldLine(LineFromUPtr(wParam), static_cast<FoldAction>(lParam));
7658 break;
7660 case Message::FoldChildren:
7661 FoldExpand(LineFromUPtr(wParam), static_cast<FoldAction>(lParam), pdoc->GetFoldLevel(LineFromUPtr(wParam)));
7662 break;
7664 case Message::FoldAll:
7665 FoldAll(static_cast<FoldAction>(wParam));
7666 break;
7668 case Message::ExpandChildren:
7669 FoldExpand(LineFromUPtr(wParam), FoldAction::Expand, static_cast<FoldLevel>(lParam));
7670 break;
7672 case Message::ContractedFoldNext:
7673 return ContractedFoldNext(LineFromUPtr(wParam));
7675 case Message::EnsureVisible:
7676 EnsureLineVisible(LineFromUPtr(wParam), false);
7677 break;
7679 case Message::EnsureVisibleEnforcePolicy:
7680 EnsureLineVisible(LineFromUPtr(wParam), true);
7681 break;
7683 case Message::ScrollRange:
7684 ScrollRange(SelectionRange(PositionFromUPtr(wParam), lParam));
7685 break;
7687 case Message::SearchAnchor:
7688 SearchAnchor();
7689 break;
7691 case Message::SearchNext:
7692 case Message::SearchPrev:
7693 return SearchText(iMessage, wParam, lParam);
7695 case Message::SetXCaretPolicy:
7696 caretPolicies.x = CaretPolicySlop(wParam, lParam);
7697 break;
7699 case Message::SetYCaretPolicy:
7700 caretPolicies.y = CaretPolicySlop(wParam, lParam);
7701 break;
7703 case Message::SetVisiblePolicy:
7704 visiblePolicy = VisiblePolicySlop(wParam, lParam);
7705 break;
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();
7714 break;
7716 case Message::SetSelBack:
7717 if (wParam) {
7718 vs.SetElementRGB(Element::SelectionBack, static_cast<int>(lParam));
7719 vs.SetElementRGB(Element::SelectionAdditionalBack, static_cast<int>(lParam));
7720 } else {
7721 vs.ResetElement(Element::SelectionBack);
7722 vs.ResetElement(Element::SelectionAdditionalBack);
7724 InvalidateStyleRedraw();
7725 break;
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();
7740 break;
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();
7753 break;
7755 case Message::SetWhitespaceFore:
7756 if (vs.SetElementColourOptional(Element::WhiteSpace, wParam, lParam)) {
7757 InvalidateStyleRedraw();
7759 break;
7761 case Message::SetWhitespaceBack:
7762 if (vs.SetElementColourOptional(Element::WhiteSpaceBack, wParam, lParam)) {
7763 InvalidateStyleRedraw();
7765 break;
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();
7773 break;
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();
7781 break;
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);
7789 else
7790 /* Default to the line caret */
7791 vs.caret.style = CaretStyle::Line;
7792 InvalidateStyleRedraw();
7793 break;
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();
7801 break;
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));
7809 break;
7811 case Message::ClearCmdKey:
7812 kmap.AssignCmdKey(static_cast<Keys>(LowShortFromWParam(wParam)),
7813 static_cast<KeyMod>(HighShortFromWParam(wParam)), Message::Null);
7814 break;
7816 case Message::ClearAllCmdKeys:
7817 kmap.Clear();
7818 break;
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();
7826 break;
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();
7838 break;
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();
7849 break;
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();
7860 break;
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();
7871 break;
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();
7882 break;
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();
7893 break;
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();
7904 break;
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();
7914 break;
7916 case Message::IndicGetStrokeWidth:
7917 if (wParam <= IndicatorMax) {
7918 return std::lround(vs.indicators[wParam].strokeWidth * 100);
7920 break;
7922 case Message::SetIndicatorCurrent:
7923 pdoc->DecorationSetCurrentIndicator(static_cast<int>(wParam));
7924 break;
7925 case Message::GetIndicatorCurrent:
7926 return pdoc->decorations->GetCurrentIndicator();
7927 case Message::SetIndicatorValue:
7928 pdoc->decorations->SetCurrentValue(static_cast<int>(wParam));
7929 break;
7930 case Message::GetIndicatorValue:
7931 return pdoc->decorations->GetCurrentValue();
7933 case Message::IndicatorFillRange:
7934 pdoc->DecorationFillRange(PositionFromUPtr(wParam),
7935 pdoc->decorations->GetCurrentValue(), lParam);
7936 break;
7938 case Message::IndicatorClearRange:
7939 pdoc->DecorationFillRange(PositionFromUPtr(wParam), 0,
7940 lParam);
7941 break;
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:
7975 case Message::Home:
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:
8002 case Message::Tab:
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);
8052 break;
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);
8059 break;
8061 case Message::BraceBadLight:
8062 SetBraceHighlight(PositionFromUPtr(wParam), -1, StyleBraceBad);
8063 break;
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);
8070 break;
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:
8081 return vs.viewEOL;
8083 case Message::SetViewEOL:
8084 vs.viewEOL = wParam != 0;
8085 InvalidateStyleRedraw();
8086 break;
8088 case Message::SetZoom: {
8089 const int zoomLevel = static_cast<int>(wParam);
8090 if (zoomLevel != vs.zoomLevel) {
8091 vs.zoomLevel = zoomLevel;
8092 InvalidateStyleRedraw();
8093 NotifyZoom();
8095 break;
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();
8107 break;
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();
8115 break;
8117 case Message::GetEdgeColour:
8118 return vs.theEdge.colour.OpaqueRGB();
8120 case Message::SetEdgeColour:
8121 vs.theEdge.colour = ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam));
8122 InvalidateStyleRedraw();
8123 break;
8125 case Message::MultiEdgeAddLine:
8126 vs.AddMultiEdge(static_cast<int>(wParam), ColourRGBA::FromIpRGB(lParam));
8127 InvalidateStyleRedraw();
8128 break;
8130 case Message::MultiEdgeClearAll:
8131 std::vector<EdgeProperties>().swap(vs.theMultiEdge); // Free vector and memory, C++03 compatible
8132 InvalidateStyleRedraw();
8133 break;
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()) {
8139 return -1;
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.
8149 break;
8151 case Message::GetDocPointer:
8152 return reinterpret_cast<sptr_t>(pdoc);
8154 case Message::SetDocPointer:
8155 CancelModes();
8156 SetDocPointer(static_cast<Document *>(PtrFromSPtr(lParam)));
8157 return 0;
8159 case Message::CreateDocument: {
8160 Document *doc = new Document(static_cast<DocumentOption>(lParam));
8161 doc->AddRef();
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();
8169 break;
8171 case Message::ReleaseDocument:
8172 (static_cast<Document *>(PtrFromSPtr(lParam)))->Release();
8173 break;
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));
8180 doc->AddRef();
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);
8189 return 0;
8191 case Message::GetModEventMask:
8192 return static_cast<sptr_t>(modEventMask);
8194 case Message::SetCommandEvents:
8195 commandEvents = static_cast<bool>(wParam);
8196 return 0;
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
8204 return 0;
8206 case Message::SetLengthForEncode:
8207 lengthForEncode = PositionFromUPtr(wParam);
8208 return 0;
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;
8218 break;
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
8223 break;
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
8228 break;
8229 case SelectionMode::Thin:
8230 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::SelTypes::thin));
8231 sel.selType = Selection::SelTypes::thin;
8232 break;
8233 default:
8234 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::SelTypes::stream));
8235 sel.selType = Selection::SelTypes::stream;
8237 InvalidateWholeSelection();
8238 break;
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);
8250 default: // ?!
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();
8274 SetIdle(true);
8276 break;
8278 case Message::GetOvertype:
8279 return inOverstrike ? 1 : 0;
8281 case Message::SetFocus:
8282 SetFocusState(wParam != 0);
8283 break;
8285 case Message::GetFocus:
8286 return hasFocus;
8288 case Message::SetStatus:
8289 errorStatus = static_cast<Status>(wParam);
8290 break;
8292 case Message::GetStatus:
8293 return static_cast<sptr_t>(errorStatus);
8295 case Message::SetMouseDownCaptures:
8296 mouseDownCaptures = wParam != 0;
8297 break;
8299 case Message::GetMouseDownCaptures:
8300 return mouseDownCaptures;
8302 case Message::SetMouseWheelCaptures:
8303 mouseWheelCaptures = wParam != 0;
8304 break;
8306 case Message::GetMouseWheelCaptures:
8307 return mouseWheelCaptures;
8309 case Message::SetCursor:
8310 cursorMode = static_cast<CursorShape>(wParam);
8311 DisplayCursor(Window::Cursor::text);
8312 break;
8314 case Message::GetCursor:
8315 return static_cast<sptr_t>(cursorMode);
8317 case Message::SetControlCharSymbol:
8318 vs.controlCharSymbol = static_cast<int>(wParam);
8319 InvalidateStyleRedraw();
8320 break;
8322 case Message::GetControlCharSymbol:
8323 return vs.controlCharSymbol;
8325 case Message::SetRepresentation:
8326 reprs.SetRepresentation(ConstCharPtrFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));
8327 break;
8329 case Message::GetRepresentation: {
8330 const Representation *repr = reprs.RepresentationFromCharacter(
8331 ConstCharPtrFromUPtr(wParam));
8332 if (repr) {
8333 return StringResult(lParam, repr->stringRep.c_str());
8335 return 0;
8338 case Message::ClearRepresentation:
8339 reprs.ClearRepresentation(ConstCharPtrFromUPtr(wParam));
8340 break;
8342 case Message::ClearAllRepresentations:
8343 SetRepresentations();
8344 break;
8346 case Message::SetRepresentationAppearance:
8347 reprs.SetRepresentationAppearance(ConstCharPtrFromUPtr(wParam), static_cast<RepresentationAppearance>(lParam));
8348 break;
8350 case Message::GetRepresentationAppearance: {
8351 const Representation *repr = reprs.RepresentationFromCharacter(
8352 ConstCharPtrFromUPtr(wParam));
8353 if (repr) {
8354 return static_cast<sptr_t>(repr->appearance);
8356 return 0;
8358 case Message::SetRepresentationColour:
8359 reprs.SetRepresentationColour(ConstCharPtrFromUPtr(wParam), ColourRGBA(static_cast<int>(lParam)));
8360 break;
8362 case Message::GetRepresentationColour: {
8363 const Representation *repr = reprs.RepresentationFromCharacter(
8364 ConstCharPtrFromUPtr(wParam));
8365 if (repr) {
8366 return repr->colour.AsInteger();
8368 return 0;
8371 case Message::StartRecord:
8372 recordingMacro = true;
8373 return 0;
8375 case Message::StopRecord:
8376 recordingMacro = false;
8377 return 0;
8379 case Message::MoveCaretInsideView:
8380 MoveCaretInsideView();
8381 break;
8383 case Message::SetFoldMarginColour:
8384 vs.foldmarginColour = OptionalColour(wParam, lParam);
8385 InvalidateStyleRedraw();
8386 break;
8388 case Message::SetFoldMarginHiColour:
8389 vs.foldmarginHighlightColour = OptionalColour(wParam, lParam);
8390 InvalidateStyleRedraw();
8391 break;
8393 case Message::SetHotspotActiveFore:
8394 if (vs.SetElementColourOptional(Element::HotSpotActive, wParam, lParam)) {
8395 InvalidateStyleRedraw();
8397 break;
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();
8406 break;
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();
8414 break;
8416 case Message::GetHotspotActiveUnderline:
8417 return vs.hotspotUnderline ? 1 : 0;
8419 case Message::SetHotspotSingleLine:
8420 hotspotSingleLine = wParam != 0;
8421 InvalidateStyleRedraw();
8422 break;
8424 case Message::GetHotspotSingleLine:
8425 return hotspotSingleLine ? 1 : 0;
8427 case Message::SetPasteConvertEndings:
8428 convertPastes = wParam != 0;
8429 break;
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);
8447 break;
8449 case Message::GetChangeHistory:
8450 return static_cast<sptr_t>(changeHistoryOption);
8452 case Message::SetExtraAscent:
8453 vs.extraAscent = static_cast<int>(wParam);
8454 InvalidateStyleRedraw();
8455 break;
8457 case Message::GetExtraAscent:
8458 return vs.extraAscent;
8460 case Message::SetExtraDescent:
8461 vs.extraDescent = static_cast<int>(wParam);
8462 InvalidateStyleRedraw();
8463 break;
8465 case Message::GetExtraDescent:
8466 return vs.extraDescent;
8468 case Message::MarginSetStyleOffset:
8469 vs.marginStyleOffset = static_cast<int>(wParam);
8470 InvalidateStyleRedraw();
8471 break;
8473 case Message::MarginGetStyleOffset:
8474 return vs.marginStyleOffset;
8476 case Message::SetMarginOptions:
8477 marginOptions = static_cast<MarginOption>(wParam);
8478 break;
8480 case Message::GetMarginOptions:
8481 return static_cast<sptr_t>(marginOptions);
8483 case Message::MarginSetText:
8484 pdoc->MarginSetText(LineFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));
8485 break;
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));
8494 break;
8496 case Message::MarginGetStyle: {
8497 const StyledText st = pdoc->MarginStyledText(LineFromUPtr(wParam));
8498 return st.style;
8501 case Message::MarginSetStyles:
8502 pdoc->MarginSetStyles(LineFromUPtr(wParam), ConstUCharPtrFromSPtr(lParam));
8503 break;
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();
8512 break;
8514 case Message::AnnotationSetText:
8515 pdoc->AnnotationSetText(LineFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));
8516 break;
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));
8525 return st.style;
8528 case Message::AnnotationSetStyle:
8529 pdoc->AnnotationSetStyle(LineFromUPtr(wParam), static_cast<int>(lParam));
8530 break;
8532 case Message::AnnotationSetStyles:
8533 pdoc->AnnotationSetStyles(LineFromUPtr(wParam), ConstUCharPtrFromSPtr(lParam));
8534 break;
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();
8546 break;
8548 case Message::AnnotationSetVisible:
8549 SetAnnotationVisible(static_cast<AnnotationVisible>(wParam));
8550 break;
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();
8558 break;
8560 case Message::AnnotationGetStyleOffset:
8561 return vs.annotationStyleOffset;
8563 case Message::EOLAnnotationSetText:
8564 pdoc->EOLAnnotationSetText(LineFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));
8565 break;
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));
8574 return st.style;
8577 case Message::EOLAnnotationSetStyle:
8578 pdoc->EOLAnnotationSetStyle(LineFromUPtr(wParam), static_cast<int>(lParam));
8579 break;
8581 case Message::EOLAnnotationClearAll:
8582 pdoc->EOLAnnotationClearAll();
8583 break;
8585 case Message::EOLAnnotationSetVisible:
8586 SetEOLAnnotationVisible(static_cast<EOLAnnotationVisible>(wParam));
8587 break;
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();
8595 break;
8597 case Message::EOLAnnotationGetStyleOffset:
8598 return vs.eolAnnotationStyleOffset;
8600 case Message::ReleaseAllExtendedStyles:
8601 vs.ReleaseAllExtendedStyles();
8602 break;
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));
8613 break;
8615 case Message::SetMouseSelectionRectangularSwitch:
8616 mouseSelectionRectangularSwitch = wParam != 0;
8617 break;
8619 case Message::GetMouseSelectionRectangularSwitch:
8620 return mouseSelectionRectangularSwitch;
8622 case Message::SetMultipleSelection:
8623 multipleSelection = wParam != 0;
8624 InvalidateCaret();
8625 break;
8627 case Message::GetMultipleSelection:
8628 return multipleSelection;
8630 case Message::SetAdditionalSelectionTyping:
8631 additionalSelectionTyping = wParam != 0;
8632 InvalidateCaret();
8633 break;
8635 case Message::GetAdditionalSelectionTyping:
8636 return additionalSelectionTyping;
8638 case Message::SetMultiPaste:
8639 multiPasteMode = static_cast<MultiPaste>(wParam);
8640 break;
8642 case Message::GetMultiPaste:
8643 return static_cast<sptr_t>(multiPasteMode);
8645 case Message::SetAdditionalCaretsBlink:
8646 view.additionalCaretsBlink = wParam != 0;
8647 InvalidateCaret();
8648 break;
8650 case Message::GetAdditionalCaretsBlink:
8651 return view.additionalCaretsBlink;
8653 case Message::SetAdditionalCaretsVisible:
8654 view.additionalCaretsVisible = wParam != 0;
8655 InvalidateCaret();
8656 break;
8658 case Message::GetAdditionalCaretsVisible:
8659 return view.additionalCaretsVisible;
8661 case Message::GetSelections:
8662 return sel.Count();
8664 case Message::GetSelectionEmpty:
8665 return sel.Empty();
8667 case Message::ClearSelections:
8668 sel.Clear();
8669 ContainerNeedsUpdate(Update::Selection);
8670 Redraw();
8671 break;
8673 case Message::SetSelection:
8674 sel.SetSelection(SelectionRange(PositionFromUPtr(wParam), lParam));
8675 Redraw();
8676 break;
8678 case Message::AddSelection:
8679 sel.AddSelection(SelectionRange(PositionFromUPtr(wParam), lParam));
8680 ContainerNeedsUpdate(Update::Selection);
8681 Redraw();
8682 break;
8684 case Message::DropSelectionN:
8685 sel.DropSelection(wParam);
8686 ContainerNeedsUpdate(Update::Selection);
8687 Redraw();
8688 break;
8690 case Message::SetMainSelection:
8691 sel.SetMain(wParam);
8692 ContainerNeedsUpdate(Update::Selection);
8693 Redraw();
8694 break;
8696 case Message::GetMainSelection:
8697 return sel.Main();
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);
8706 break;
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())
8734 sel.Clear();
8735 sel.selType = Selection::SelTypes::rectangle;
8736 sel.Rectangular().caret.SetPosition(PositionFromUPtr(wParam));
8737 SetRectangularRange();
8738 Redraw();
8739 break;
8741 case Message::GetRectangularSelectionCaret:
8742 return sel.Rectangular().caret.Position();
8744 case Message::SetRectangularSelectionAnchor:
8745 if (!sel.IsRectangular())
8746 sel.Clear();
8747 sel.selType = Selection::SelTypes::rectangle;
8748 sel.Rectangular().anchor.SetPosition(PositionFromUPtr(wParam));
8749 SetRectangularRange();
8750 Redraw();
8751 break;
8753 case Message::GetRectangularSelectionAnchor:
8754 return sel.Rectangular().anchor.Position();
8756 case Message::SetRectangularSelectionCaretVirtualSpace:
8757 if (!sel.IsRectangular())
8758 sel.Clear();
8759 sel.selType = Selection::SelTypes::rectangle;
8760 sel.Rectangular().caret.SetVirtualSpace(PositionFromUPtr(wParam));
8761 SetRectangularRange();
8762 Redraw();
8763 break;
8765 case Message::GetRectangularSelectionCaretVirtualSpace:
8766 return sel.Rectangular().caret.VirtualSpace();
8768 case Message::SetRectangularSelectionAnchorVirtualSpace:
8769 if (!sel.IsRectangular())
8770 sel.Clear();
8771 sel.selType = Selection::SelTypes::rectangle;
8772 sel.Rectangular().anchor.SetVirtualSpace(PositionFromUPtr(wParam));
8773 SetRectangularRange();
8774 Redraw();
8775 break;
8777 case Message::GetRectangularSelectionAnchorVirtualSpace:
8778 return sel.Rectangular().anchor.VirtualSpace();
8780 case Message::SetVirtualSpaceOptions:
8781 virtualSpaceOptions = static_cast<VirtualSpace>(wParam);
8782 break;
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();
8790 break;
8792 case Message::SetAdditionalSelBack:
8793 vs.SetElementRGB(Element::SelectionAdditionalBack, static_cast<int>(wParam));
8794 InvalidateStyleRedraw();
8795 break;
8797 case Message::SetAdditionalSelAlpha:
8798 vs.SetElementAlpha(Element::SelectionAdditionalBack, static_cast<int>(wParam));
8799 InvalidateStyleRedraw();
8800 break;
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();
8810 break;
8812 case Message::GetAdditionalCaretFore:
8813 return vs.ElementColourForced(Element::CaretAdditional).OpaqueRGB();
8815 case Message::RotateSelection:
8816 sel.RotateMain();
8817 InvalidateWholeSelection();
8818 break;
8820 case Message::SwapMainAnchorCaret:
8821 InvalidateSelection(sel.RangeMain());
8822 sel.RangeMain().Swap();
8823 break;
8825 case Message::MultipleSelectAddNext:
8826 MultipleSelectAdd(AddNumber::one);
8827 break;
8829 case Message::MultipleSelectAddEach:
8830 MultipleSelectAdd(AddNumber::each);
8831 break;
8833 case Message::ChangeLexerState:
8834 pdoc->ChangeLexerState(PositionFromUPtr(wParam), lParam);
8835 break;
8837 case Message::SetIdentifier:
8838 SetCtrlID(static_cast<int>(wParam));
8839 break;
8841 case Message::GetIdentifier:
8842 return GetCtrlID();
8844 case Message::SetTechnology:
8845 // No action by default
8846 break;
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);
8857 default:
8858 return DefWndProc(iMessage, wParam, lParam);
8860 //Platform::DebugPrintf("end wnd proc\n");
8861 return 0;