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.
7
8#include <cstddef>
9#include <cstdlib>
10#include <cstdint>
11#include <cassert>
12#include <cstring>
13#include <cstdio>
14#include <cmath>
15
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
29#include "ScintillaTypes.h"
30#include "ScintillaMessages.h"
31#include "ScintillaStructures.h"
32#include "ILoader.h"
33#include "ILexer.h"
34
35#include "Debugging.h"
36#include "Geometry.h"
37#include "Platform.h"
38
39#include "CharacterType.h"
40#include "CharacterCategoryMap.h"
41#include "Position.h"
42#include "UniqueString.h"
43#include "SplitVector.h"
44#include "Partitioning.h"
45#include "RunStyles.h"
46#include "ContractionState.h"
47#include "CellBuffer.h"
48#include "PerLine.h"
49#include "KeyMap.h"
50#include "Indicator.h"
51#include "LineMarker.h"
52#include "Style.h"
53#include "ViewStyle.h"
54#include "CharClassify.h"
55#include "Decoration.h"
56#include "CaseFolder.h"
57#include "Document.h"
58#include "UniConversion.h"
59#include "DBCS.h"
60#include "Selection.h"
61#include "PositionCache.h"
62#include "EditModel.h"
63#include "MarginView.h"
64#include "EditView.h"
65#include "Editor.h"
66#include "ElapsedPeriod.h"
67
68using namespace Scintilla;
69using namespace Scintilla::Internal;
70
71namespace {
72
73/*
74 return whether this modification represents an operation that
75 may reasonably be deferred (not done now OR [possibly] at all)
76*/
77constexpr bool CanDeferToLastStep(const DocModification &mh) noexcept {
78 if (FlagSet(mh.modificationType, (ModificationFlags::BeforeInsert | ModificationFlags::BeforeDelete)))
79 return true; // CAN skip
80 if (!FlagSet(mh.modificationType, (ModificationFlags::Undo | ModificationFlags::Redo)))
81 return false; // MUST do
82 if (FlagSet(mh.modificationType, ModificationFlags::MultiStepUndoRedo))
83 return true; // CAN skip
84 return false; // PRESUMABLY must do
85}
86
87constexpr bool CanEliminate(const DocModification &mh) noexcept {
88 return
89 FlagSet(mh.modificationType, (ModificationFlags::BeforeInsert | ModificationFlags::BeforeDelete));
90}
91
92/*
93 return whether this modification represents the FINAL step
94 in a [possibly lengthy] multi-step Undo/Redo sequence
95*/
96constexpr bool IsLastStep(const DocModification &mh) noexcept {
97 return
98 FlagSet(mh.modificationType, (ModificationFlags::Undo | ModificationFlags::Redo))
99 && (FlagSet(mh.modificationType, ModificationFlags::MultiStepUndoRedo))
100 && (FlagSet(mh.modificationType, ModificationFlags::LastStepInUndoRedo))
101 && (FlagSet(mh.modificationType, ModificationFlags::MultilineUndoRedo));
102}
103
104}
105
106Timer::Timer() noexcept :
107 ticking(false), ticksToWait(0), tickerID{} {}
108
109Idler::Idler() noexcept :
110 state(false), idlerID(nullptr) {}
111
112static constexpr bool IsAllSpacesOrTabs(std::string_view sv) noexcept {
113 for (const char ch : sv) {
114 // This is safe because IsSpaceOrTab() will return false for null terminators
115 if (!IsSpaceOrTab(ch))
116 return false;
117 }
118 return true;
119}
120
121Editor::Editor() : durationWrapOneByte(0.000001, 0.00000001, 0.00001) {
122 ctrlID = 0;
123
124 stylesValid = false;
125 technology = Technology::Default;
126 scaleRGBAImage = 100.0f;
127
128 cursorMode = CursorShape::Normal;
129
130 errorStatus = Status::Ok;
131 mouseDownCaptures = true;
132 mouseWheelCaptures = true;
133
134 lastClickTime = 0;
135 doubleClickCloseThreshold = Point(3, 3);
136 dwellDelay = TimeForever;
137 ticksToDwell = TimeForever;
138 dwelling = false;
139 ptMouseLast.x = 0;
140 ptMouseLast.y = 0;
141 inDragDrop = DragDrop::none;
142 dropWentOutside = false;
143 posDrop = SelectionPosition(Sci::invalidPosition);
144 hotSpotClickPos = Sci::invalidPosition;
145 selectionUnit = TextUnit::character;
146
147 lastXChosen = 0;
148 lineAnchorPos = 0;
149 originalAnchorPos = 0;
150 wordSelectAnchorStartPos = 0;
151 wordSelectAnchorEndPos = 0;
152 wordSelectInitialCaretPos = -1;
153
154 caretPolicies.x = { CaretPolicy::Slop | CaretPolicy::Even, 50 };
155 caretPolicies.y = { CaretPolicy::Even, 0 };
156
157 visiblePolicy = { 0, 0 };
158
159 searchAnchor = 0;
160
161 xCaretMargin = 50;
162 horizontalScrollBarVisible = true;
163 scrollWidth = 2000;
164 verticalScrollBarVisible = true;
165 endAtLastLine = true;
166 caretSticky = CaretSticky::Off;
167 marginOptions = MarginOption::None;
168 mouseSelectionRectangularSwitch = false;
169 multipleSelection = false;
170 additionalSelectionTyping = false;
171 multiPasteMode = MultiPaste::Once;
172 virtualSpaceOptions = VirtualSpace::None;
173
174 targetRange = SelectionSegment();
175 searchFlags = FindOption::None;
176
177 topLine = 0;
178 posTopLine = 0;
179
180 lengthForEncode = -1;
181
182 needUpdateUI = Update::None;
183 ContainerNeedsUpdate(Update::Content);
184
185 paintState = PaintState::notPainting;
186 paintAbandonedByStyling = false;
187 paintingAllText = false;
188 willRedrawAll = false;
189 idleStyling = IdleStyling::None;
190 needIdleStyling = false;
191
192 modEventMask = ModificationFlags::EventMaskAll;
193 commandEvents = true;
194
195 pdoc->AddWatcher(this, nullptr);
196
197 recordingMacro = false;
198 foldAutomatic = AutomaticFold::None;
199
200 convertPastes = true;
201
202 SetRepresentations();
203}
204
205Editor::~Editor() {
206 pdoc->RemoveWatcher(this, nullptr);
207 DropGraphics();
208}
209
210void Editor::Finalise() {
211 SetIdle(false);
212 CancelModes();
213}
214
215void Editor::SetRepresentations() {
216 reprs.Clear();
217
218 // C0 control set
219 const char *const reps[] = {
220 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
221 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
222 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
223 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
224 };
225 for (size_t j=0; j < std::size(reps); j++) {
226 const char c[2] = { static_cast<char>(j), 0 };
227 reprs.SetRepresentation(std::string_view(c, 1), reps[j]);
228 }
229 reprs.SetRepresentation("\x7f", "DEL");
230
231 const int dbcsCodePage = pdoc->dbcsCodePage;
232 // C1 control set
233 // As well as Unicode mode, ISO-8859-1 should use these
234 if (CpUtf8 == dbcsCodePage) {
235 const char *const repsC1[] = {
236 "PAD", "HOP", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
237 "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
238 "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
239 "SOS", "SGCI", "SCI", "CSI", "ST", "OSC", "PM", "APC"
240 };
241 for (size_t j=0; j < std::size(repsC1); j++) {
242 const char c1[3] = { '\xc2', static_cast<char>(0x80+j), 0 };
243 reprs.SetRepresentation(c1, repsC1[j]);
244 }
245 reprs.SetRepresentation("\xe2\x80\xa8", "LS");
246 reprs.SetRepresentation("\xe2\x80\xa9", "PS");
247 }
248
249 // Invalid as single bytes in multi-byte encodings
250 if (dbcsCodePage) {
251 for (int k = 0x80; k < 0x100; k++) {
252 if ((CpUtf8 == dbcsCodePage) || !IsDBCSValidSingleByte(dbcsCodePage, k)) {
253 const char hiByte[2] = { static_cast<char>(k), 0 };
254 char hexits[4];
255 Hexits(hexits, k);
256 reprs.SetRepresentation(hiByte, hexits);
257 }
258 }
259 }
260}
261
262void Editor::DropGraphics() noexcept {
263 marginView.DropGraphics();
264 view.DropGraphics();
265}
266
267void Editor::InvalidateStyleData() {
268 stylesValid = false;
269 vs.technology = technology;
270 DropGraphics();
271 view.llc.Invalidate(LineLayout::ValidLevel::invalid);
272 view.posCache.Clear();
273}
274
275void Editor::InvalidateStyleRedraw() {
276 NeedWrapping();
277 InvalidateStyleData();
278 Redraw();
279}
280
281void Editor::RefreshStyleData() {
282 if (!stylesValid) {
283 stylesValid = true;
284 AutoSurface surface(this);
285 if (surface) {
286 vs.Refresh(*surface, pdoc->tabInChars);
287 }
288 SetScrollBars();
289 SetRectangularRange();
290 }
291}
292
293Point Editor::GetVisibleOriginInMain() const {
294 return Point(0, 0);
295}
296
297PointDocument Editor::DocumentPointFromView(Point ptView) const {
298 PointDocument ptDocument(ptView);
299 if (wMargin.GetID()) {
300 const Point ptOrigin = GetVisibleOriginInMain();
301 ptDocument.x += ptOrigin.x;
302 ptDocument.y += ptOrigin.y;
303 } else {
304 ptDocument.x += xOffset;
305 ptDocument.y += topLine * vs.lineHeight;
306 }
307 return ptDocument;
308}
309
310Sci::Line Editor::TopLineOfMain() const {
311 if (wMargin.GetID())
312 return 0;
313 else
314 return topLine;
315}
316
317PRectangle Editor::GetClientRectangle() const {
318 return wMain.GetClientPosition();
319}
320
321PRectangle Editor::GetClientDrawingRectangle() {
322 return GetClientRectangle();
323}
324
325PRectangle Editor::GetTextRectangle() const {
326 PRectangle rc = GetClientRectangle();
327 rc.left += vs.textStart;
328 rc.right -= vs.rightMarginWidth;
329 return rc;
330}
331
332Sci::Line Editor::LinesOnScreen() const {
333 const PRectangle rcClient = GetClientRectangle();
334 const int htClient = static_cast<int>(rcClient.bottom - rcClient.top);
335 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
336 return htClient / vs.lineHeight;
337}
338
339Sci::Line Editor::LinesToScroll() const {
340 const Sci::Line retVal = LinesOnScreen() - 1;
341 if (retVal < 1)
342 return 1;
343 else
344 return retVal;
345}
346
347Sci::Line Editor::MaxScrollPos() const {
348 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
349 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
350 Sci::Line retVal = pcs->LinesDisplayed();
351 if (endAtLastLine) {
352 retVal -= LinesOnScreen();
353 } else {
354 retVal--;
355 }
356 if (retVal < 0) {
357 return 0;
358 } else {
359 return retVal;
360 }
361}
362
363SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
364 if (sp.Position() < 0) {
365 return SelectionPosition(0);
366 } else if (sp.Position() > pdoc->Length()) {
367 return SelectionPosition(pdoc->Length());
368 } else {
369 // If not at end of line then set offset to 0
370 if (!pdoc->IsLineEndPosition(sp.Position()))
371 sp.SetVirtualSpace(0);
372 return sp;
373 }
374}
375
376Point Editor::LocationFromPosition(SelectionPosition pos, PointEnd pe) {
377 const PRectangle rcClient = GetTextRectangle();
378 RefreshStyleData();
379 AutoSurface surface(this);
380 return view.LocationFromPosition(surface, *this, pos, topLine, vs, pe, rcClient);
381}
382
383Point Editor::LocationFromPosition(Sci::Position pos, PointEnd pe) {
384 return LocationFromPosition(SelectionPosition(pos), pe);
385}
386
387int Editor::XFromPosition(SelectionPosition sp) {
388 const Point pt = LocationFromPosition(sp);
389 return static_cast<int>(pt.x) - vs.textStart + xOffset;
390}
391
392SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
393 RefreshStyleData();
394 AutoSurface surface(this);
395
396 PRectangle rcClient = GetTextRectangle();
397 // May be in scroll view coordinates so translate back to main view
398 const Point ptOrigin = GetVisibleOriginInMain();
399 rcClient.Move(-ptOrigin.x, -ptOrigin.y);
400
401 if (canReturnInvalid) {
402 if (!rcClient.Contains(pt))
403 return SelectionPosition(Sci::invalidPosition);
404 if (pt.x < vs.textStart)
405 return SelectionPosition(Sci::invalidPosition);
406 if (pt.y < 0)
407 return SelectionPosition(Sci::invalidPosition);
408 }
409 const PointDocument ptdoc = DocumentPointFromView(pt);
410 return view.SPositionFromLocation(surface, *this, ptdoc, canReturnInvalid,
411 charPosition, virtualSpace, vs, rcClient);
412}
413
414Sci::Position Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
415 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
416}
417
418/**
419* Find the document position corresponding to an x coordinate on a particular document line.
420* Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
421* This method is used for rectangular selections and does not work on wrapped lines.
422*/
423SelectionPosition Editor::SPositionFromLineX(Sci::Line lineDoc, int x) {
424 RefreshStyleData();
425 if (lineDoc >= pdoc->LinesTotal())
426 return SelectionPosition(pdoc->Length());
427 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
428 AutoSurface surface(this);
429 return view.SPositionFromLineX(surface, *this, lineDoc, x, vs);
430}
431
432Sci::Position Editor::PositionFromLineX(Sci::Line lineDoc, int x) {
433 return SPositionFromLineX(lineDoc, x).Position();
434}
435
436Sci::Line Editor::LineFromLocation(Point pt) const {
437 return pcs->DocFromDisplay(static_cast<int>(pt.y) / vs.lineHeight + topLine);
438}
439
440void Editor::SetTopLine(Sci::Line topLineNew) {
441 if ((topLine != topLineNew) && (topLineNew >= 0)) {
442 topLine = topLineNew;
443 ContainerNeedsUpdate(Update::VScroll);
444 }
445 posTopLine = pdoc->LineStart(pcs->DocFromDisplay(topLine));
446}
447
448/**
449 * If painting then abandon the painting because a wider redraw is needed.
450 * @return true if calling code should stop drawing.
451 */
452bool Editor::AbandonPaint() {
453 if ((paintState == PaintState::painting) && !paintingAllText) {
454 paintState = PaintState::abandoned;
455 }
456 return paintState == PaintState::abandoned;
457}
458
459void Editor::RedrawRect(PRectangle rc) {
460 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
461
462 // Clip the redraw rectangle into the client area
463 const PRectangle rcClient = GetClientRectangle();
464 if (rc.top < rcClient.top)
465 rc.top = rcClient.top;
466 if (rc.bottom > rcClient.bottom)
467 rc.bottom = rcClient.bottom;
468 if (rc.left < rcClient.left)
469 rc.left = rcClient.left;
470 if (rc.right > rcClient.right)
471 rc.right = rcClient.right;
472
473 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
474 wMain.InvalidateRectangle(rc);
475 }
476}
477
478void Editor::DiscardOverdraw() {
479 // Overridden on platforms that may draw outside visible area.
480}
481
482void Editor::Redraw() {
483 if (redrawPendingText) {
484 return;
485 }
486 //Platform::DebugPrintf("Redraw all\n");
487 const PRectangle rcClient = GetClientRectangle();
488 wMain.InvalidateRectangle(rcClient);
489 if (wMargin.GetID()) {
490 wMargin.InvalidateAll();
491 } else if (paintState == PaintState::notPainting) {
492 redrawPendingText = true;
493 }
494}
495
496void Editor::RedrawSelMargin(Sci::Line line, bool allAfter) {
497 const bool markersInText = vs.maskInLine || vs.maskDrawInText;
498 if (!wMargin.GetID() || markersInText) { // May affect text area so may need to abandon and retry
499 if (AbandonPaint()) {
500 return;
501 }
502 }
503 if (wMargin.GetID() && markersInText) {
504 Redraw();
505 return;
506 }
507 if (redrawPendingMargin) {
508 return;
509 }
510 PRectangle rcMarkers = GetClientRectangle();
511 if (!markersInText) {
512 // Normal case: just draw the margin
513 rcMarkers.right = rcMarkers.left + vs.fixedColumnWidth;
514 }
515 const PRectangle rcMarkersFull = rcMarkers;
516 if (line != -1) {
517 PRectangle rcLine = RectangleFromRange(Range(pdoc->LineStart(line)), 0);
518
519 // Inflate line rectangle if there are image markers with height larger than line height
520 if (vs.largestMarkerHeight > vs.lineHeight) {
521 const int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
522 rcLine.top -= delta;
523 rcLine.bottom += delta;
524 if (rcLine.top < rcMarkers.top)
525 rcLine.top = rcMarkers.top;
526 if (rcLine.bottom > rcMarkers.bottom)
527 rcLine.bottom = rcMarkers.bottom;
528 }
529
530 rcMarkers.top = rcLine.top;
531 if (!allAfter)
532 rcMarkers.bottom = rcLine.bottom;
533 if (rcMarkers.Empty())
534 return;
535 }
536 if (wMargin.GetID()) {
537 const Point ptOrigin = GetVisibleOriginInMain();
538 rcMarkers.Move(-ptOrigin.x, -ptOrigin.y);
539 wMargin.InvalidateRectangle(rcMarkers);
540 } else {
541 wMain.InvalidateRectangle(rcMarkers);
542 if (rcMarkers == rcMarkersFull) {
543 redrawPendingMargin = true;
544 }
545 }
546}
547
548PRectangle Editor::RectangleFromRange(Range r, int overlap) {
549 const Sci::Line minLine = pcs->DisplayFromDoc(
550 pdoc->SciLineFromPosition(r.First()));
551 const Sci::Line maxLine = pcs->DisplayLastFromDoc(
552 pdoc->SciLineFromPosition(r.Last()));
553 const PRectangle rcClientDrawing = GetClientDrawingRectangle();
554 PRectangle rc;
555 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
556 rc.left = static_cast<XYPOSITION>(vs.textStart - leftTextOverlap);
557 rc.top = static_cast<XYPOSITION>((minLine - TopLineOfMain()) * vs.lineHeight - overlap);
558 if (rc.top < rcClientDrawing.top)
559 rc.top = rcClientDrawing.top;
560 // Extend to right of prepared area if any to prevent artifacts from caret line highlight
561 rc.right = rcClientDrawing.right;
562 rc.bottom = static_cast<XYPOSITION>((maxLine - TopLineOfMain() + 1) * vs.lineHeight + overlap);
563
564 return rc;
565}
566
567void Editor::InvalidateRange(Sci::Position start, Sci::Position end) {
568 if (redrawPendingText) {
569 return;
570 }
571 RedrawRect(RectangleFromRange(Range(start, end), view.LinesOverlap() ? vs.lineOverlap : 0));
572}
573
574Sci::Position Editor::CurrentPosition() const {
575 return sel.MainCaret();
576}
577
578bool Editor::SelectionEmpty() const noexcept {
579 return sel.Empty();
580}
581
582SelectionPosition Editor::SelectionStart() {
583 return sel.RangeMain().Start();
584}
585
586SelectionPosition Editor::SelectionEnd() {
587 return sel.RangeMain().End();
588}
589
590void Editor::SetRectangularRange() {
591 if (sel.IsRectangular()) {
592 const int xAnchor = XFromPosition(sel.Rectangular().anchor);
593 int xCaret = XFromPosition(sel.Rectangular().caret);
594 if (sel.selType == Selection::SelTypes::thin) {
595 xCaret = xAnchor;
596 }
597 const Sci::Line lineAnchorRect =
598 pdoc->SciLineFromPosition(sel.Rectangular().anchor.Position());
599 const Sci::Line lineCaret =
600 pdoc->SciLineFromPosition(sel.Rectangular().caret.Position());
601 const int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
602 AutoSurface surface(this);
603 for (Sci::Line line=lineAnchorRect; line != lineCaret+increment; line += increment) {
604 SelectionRange range(
605 view.SPositionFromLineX(surface, *this, line, xCaret, vs),
606 view.SPositionFromLineX(surface, *this, line, xAnchor, vs));
607 if (!FlagSet(virtualSpaceOptions, VirtualSpace::RectangularSelection))
608 range.ClearVirtualSpace();
609 if (line == lineAnchorRect)
610 sel.SetSelection(range);
611 else
612 sel.AddSelectionWithoutTrim(range);
613 }
614 }
615}
616
617void Editor::ThinRectangularRange() {
618 if (sel.IsRectangular()) {
619 sel.selType = Selection::SelTypes::thin;
620 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
621 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
622 } else {
623 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
624 }
625 SetRectangularRange();
626 }
627}
628
629void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
630 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
631 invalidateWholeSelection = true;
632 }
633 Sci::Position firstAffected = std::min(sel.RangeMain().Start().Position(), newMain.Start().Position());
634 // +1 for lastAffected ensures caret repainted
635 Sci::Position lastAffected = std::max(newMain.caret.Position()+1, newMain.anchor.Position());
636 lastAffected = std::max(lastAffected, sel.RangeMain().End().Position());
637 if (invalidateWholeSelection) {
638 for (size_t r=0; r<sel.Count(); r++) {
639 firstAffected = std::min(firstAffected, sel.Range(r).caret.Position());
640 firstAffected = std::min(firstAffected, sel.Range(r).anchor.Position());
641 lastAffected = std::max(lastAffected, sel.Range(r).caret.Position()+1);
642 lastAffected = std::max(lastAffected, sel.Range(r).anchor.Position());
643 }
644 }
645 ContainerNeedsUpdate(Update::Selection);
646 InvalidateRange(firstAffected, lastAffected);
647}
648
649void Editor::InvalidateWholeSelection() {
650 InvalidateSelection(sel.RangeMain(), true);
651}
652
653/* For Line selection - the anchor and caret are always
654 at the beginning and end of the region lines. */
655SelectionRange Editor::LineSelectionRange(SelectionPosition currentPos_, SelectionPosition anchor_) const {
656 if (currentPos_ > anchor_) {
657 anchor_ = SelectionPosition(
658 pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
659 currentPos_ = SelectionPosition(
660 pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
661 } else {
662 currentPos_ = SelectionPosition(
663 pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
664 anchor_ = SelectionPosition(
665 pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
666 }
667 return SelectionRange(currentPos_, anchor_);
668}
669
670void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
671 currentPos_ = ClampPositionIntoDocument(currentPos_);
672 anchor_ = ClampPositionIntoDocument(anchor_);
673 const Sci::Line currentLine = pdoc->SciLineFromPosition(currentPos_.Position());
674 SelectionRange rangeNew(currentPos_, anchor_);
675 if (sel.selType == Selection::SelTypes::lines) {
676 rangeNew = LineSelectionRange(currentPos_, anchor_);
677 }
678 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
679 InvalidateSelection(rangeNew);
680 }
681 sel.RangeMain() = rangeNew;
682 SetRectangularRange();
683 ClaimSelection();
684 SetHoverIndicatorPosition(sel.MainCaret());
685
686 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
687 RedrawSelMargin();
688 }
689 QueueIdleWork(WorkItems::updateUI);
690}
691
692void Editor::SetSelection(Sci::Position currentPos_, Sci::Position anchor_) {
693 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
694}
695
696// Just move the caret on the main selection
697void Editor::SetSelection(SelectionPosition currentPos_) {
698 currentPos_ = ClampPositionIntoDocument(currentPos_);
699 const Sci::Line currentLine = pdoc->SciLineFromPosition(currentPos_.Position());
700 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
701 InvalidateSelection(SelectionRange(currentPos_));
702 }
703 if (sel.IsRectangular()) {
704 sel.Rectangular() =
705 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
706 SetRectangularRange();
707 } else if (sel.selType == Selection::SelTypes::lines) {
708 sel.RangeMain() = LineSelectionRange(currentPos_, sel.RangeMain().anchor);
709 } else {
710 sel.RangeMain() =
711 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
712 }
713 ClaimSelection();
714 SetHoverIndicatorPosition(sel.MainCaret());
715
716 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
717 RedrawSelMargin();
718 }
719 QueueIdleWork(WorkItems::updateUI);
720}
721
722void Editor::SetEmptySelection(SelectionPosition currentPos_) {
723 const Sci::Line currentLine = pdoc->SciLineFromPosition(currentPos_.Position());
724 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
725 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
726 InvalidateSelection(rangeNew);
727 }
728 sel.Clear();
729 sel.RangeMain() = rangeNew;
730 SetRectangularRange();
731 ClaimSelection();
732 SetHoverIndicatorPosition(sel.MainCaret());
733
734 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
735 RedrawSelMargin();
736 }
737 QueueIdleWork(WorkItems::updateUI);
738}
739
740void Editor::SetEmptySelection(Sci::Position currentPos_) {
741 SetEmptySelection(SelectionPosition(currentPos_));
742}
743
744void Editor::MultipleSelectAdd(AddNumber addNumber) {
745 if (SelectionEmpty() || !multipleSelection) {
746 // Select word at caret
747 const Sci::Position startWord = pdoc->ExtendWordSelect(sel.MainCaret(), -1, true);
748 const Sci::Position endWord = pdoc->ExtendWordSelect(startWord, 1, true);
749 TrimAndSetSelection(endWord, startWord);
750
751 } else {
752
753 if (!pdoc->HasCaseFolder())
754 pdoc->SetCaseFolder(CaseFolderForEncoding());
755
756 const Range rangeMainSelection(sel.RangeMain().Start().Position(), sel.RangeMain().End().Position());
757 const std::string selectedText = RangeText(rangeMainSelection.start, rangeMainSelection.end);
758
759 const Range rangeTarget(targetRange.start.Position(), targetRange.end.Position());
760 std::vector<Range> searchRanges;
761 // Search should be over the target range excluding the current selection so
762 // may need to search 2 ranges, after the selection then before the selection.
763 if (rangeTarget.Overlaps(rangeMainSelection)) {
764 // Common case is that the selection is completely within the target but
765 // may also have overlap at start or end.
766 if (rangeMainSelection.end < rangeTarget.end)
767 searchRanges.push_back(Range(rangeMainSelection.end, rangeTarget.end));
768 if (rangeTarget.start < rangeMainSelection.start)
769 searchRanges.push_back(Range(rangeTarget.start, rangeMainSelection.start));
770 } else {
771 // No overlap
772 searchRanges.push_back(rangeTarget);
773 }
774
775 for (const Range range : searchRanges) {
776 Sci::Position searchStart = range.start;
777 const Sci::Position searchEnd = range.end;
778 for (;;) {
779 Sci::Position lengthFound = selectedText.length();
780 const Sci::Position pos = pdoc->FindText(searchStart, searchEnd,
781 selectedText.c_str(), searchFlags, &lengthFound);
782 if (pos >= 0) {
783 sel.AddSelection(SelectionRange(pos + lengthFound, pos));
784 ContainerNeedsUpdate(Update::Selection);
785 ScrollRange(sel.RangeMain());
786 Redraw();
787 if (addNumber == AddNumber::one)
788 return;
789 searchStart = pos + lengthFound;
790 } else {
791 break;
792 }
793 }
794 }
795 }
796}
797
798bool Editor::RangeContainsProtected(Sci::Position start, Sci::Position end) const noexcept {
799 if (vs.ProtectionActive()) {
800 if (start > end) {
801 const Sci::Position t = start;
802 start = end;
803 end = t;
804 }
805 for (Sci::Position pos = start; pos < end; pos++) {
806 if (vs.styles[pdoc->StyleIndexAt(pos)].IsProtected())
807 return true;
808 }
809 }
810 return false;
811}
812
813bool Editor::SelectionContainsProtected() const {
814 for (size_t r=0; r<sel.Count(); r++) {
815 if (RangeContainsProtected(sel.Range(r).Start().Position(),
816 sel.Range(r).End().Position())) {
817 return true;
818 }
819 }
820 return false;
821}
822
823/**
824 * Asks document to find a good position and then moves out of any invisible positions.
825 */
826Sci::Position Editor::MovePositionOutsideChar(Sci::Position pos, Sci::Position moveDir, bool checkLineEnd) const {
827 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
828}
829
830SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, Sci::Position moveDir, bool checkLineEnd) const {
831 const Sci::Position posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
832 if (posMoved != pos.Position())
833 pos.SetPosition(posMoved);
834 if (vs.ProtectionActive()) {
835 if (moveDir > 0) {
836 if ((pos.Position() > 0) && vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()) {
837 while ((pos.Position() < pdoc->Length()) &&
838 (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()))
839 pos.Add(1);
840 }
841 } else if (moveDir < 0) {
842 if (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()) {
843 while ((pos.Position() > 0) &&
844 (vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()))
845 pos.Add(-1);
846 }
847 }
848 }
849 return pos;
850}
851
852void Editor::MovedCaret(SelectionPosition newPos, SelectionPosition previousPos,
853 bool ensureVisible, CaretPolicies policies) {
854 const Sci::Line currentLine = pdoc->SciLineFromPosition(newPos.Position());
855 if (ensureVisible) {
856 // In case in need of wrapping to ensure DisplayFromDoc works.
857 if (currentLine >= wrapPending.start) {
858 if (WrapLines(WrapScope::wsAll)) {
859 Redraw();
860 }
861 }
862 const XYScrollPosition newXY = XYScrollToMakeVisible(
863 SelectionRange(posDrag.IsValid() ? posDrag : newPos), XYScrollOptions::all, policies);
864 if (previousPos.IsValid() && (newXY.xOffset == xOffset)) {
865 // simple vertical scroll then invalidate
866 ScrollTo(newXY.topLine);
867 InvalidateSelection(SelectionRange(previousPos), true);
868 } else {
869 SetXYScroll(newXY);
870 }
871 }
872
873 ShowCaretAtCurrentPosition();
874 NotifyCaretMove();
875
876 ClaimSelection();
877 SetHoverIndicatorPosition(sel.MainCaret());
878 QueueIdleWork(WorkItems::updateUI);
879
880 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
881 RedrawSelMargin();
882 }
883}
884
885void Editor::MovePositionTo(SelectionPosition newPos, Selection::SelTypes selt, bool ensureVisible) {
886 const SelectionPosition spCaret = ((sel.Count() == 1) && sel.Empty()) ?
887 sel.Last() : SelectionPosition(Sci::invalidPosition);
888
889 const Sci::Position delta = newPos.Position() - sel.MainCaret();
890 newPos = ClampPositionIntoDocument(newPos);
891 newPos = MovePositionOutsideChar(newPos, delta);
892 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::SelTypes::stream)) {
893 // Can't turn into multiple selection so clear additional selections
894 InvalidateSelection(SelectionRange(newPos), true);
895 sel.DropAdditionalRanges();
896 }
897 if (!sel.IsRectangular() && (selt == Selection::SelTypes::rectangle)) {
898 // Switching to rectangular
899 InvalidateSelection(sel.RangeMain(), false);
900 SelectionRange rangeMain = sel.RangeMain();
901 sel.Clear();
902 sel.Rectangular() = rangeMain;
903 }
904 if (selt != Selection::SelTypes::none) {
905 sel.selType = selt;
906 }
907 if (selt != Selection::SelTypes::none || sel.MoveExtends()) {
908 SetSelection(newPos);
909 } else {
910 SetEmptySelection(newPos);
911 }
912
913 MovedCaret(newPos, spCaret, ensureVisible, caretPolicies);
914}
915
916void Editor::MovePositionTo(Sci::Position newPos, Selection::SelTypes selt, bool ensureVisible) {
917 MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
918}
919
920SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
921 pos = ClampPositionIntoDocument(pos);
922 pos = MovePositionOutsideChar(pos, moveDir);
923 const Sci::Line lineDoc = pdoc->SciLineFromPosition(pos.Position());
924 if (pcs->GetVisible(lineDoc)) {
925 return pos;
926 } else {
927 Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
928 if (moveDir > 0) {
929 // lineDisplay is already line before fold as lines in fold use display line of line after fold
930 lineDisplay = std::clamp<Sci::Line>(lineDisplay, 0, pcs->LinesDisplayed());
931 return SelectionPosition(
932 pdoc->LineStart(pcs->DocFromDisplay(lineDisplay)));
933 } else {
934 lineDisplay = std::clamp<Sci::Line>(lineDisplay - 1, 0, pcs->LinesDisplayed());
935 return SelectionPosition(
936 pdoc->LineEnd(pcs->DocFromDisplay(lineDisplay)));
937 }
938 }
939}
940
941SelectionPosition Editor::MovePositionSoVisible(Sci::Position pos, int moveDir) {
942 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
943}
944
945Point Editor::PointMainCaret() {
946 return LocationFromPosition(sel.Range(sel.Main()).caret);
947}
948
949/**
950 * Choose the x position that the caret will try to stick to
951 * as it moves up and down.
952 */
953void Editor::SetLastXChosen() {
954 const Point pt = PointMainCaret();
955 lastXChosen = static_cast<int>(pt.x) + xOffset;
956}
957
958void Editor::ScrollTo(Sci::Line line, bool moveThumb) {
959 const Sci::Line topLineNew = std::clamp<Sci::Line>(line, 0, MaxScrollPos());
960 if (topLineNew != topLine) {
961 // Try to optimise small scrolls
962#ifndef UNDER_CE
963 const Sci::Line linesToMove = topLine - topLineNew;
964 const bool performBlit = (std::abs(linesToMove) <= 10) && (paintState == PaintState::notPainting);
965 willRedrawAll = !performBlit;
966#endif
967 SetTopLine(topLineNew);
968 // Optimize by styling the view as this will invalidate any needed area
969 // which could abort the initial paint if discovered later.
970 StyleAreaBounded(GetClientRectangle(), true);
971#ifndef UNDER_CE
972 // Perform redraw rather than scroll if many lines would be redrawn anyway.
973 if (performBlit) {
974 ScrollText(linesToMove);
975 } else {
976 Redraw();
977 }
978 willRedrawAll = false;
979#else
980 Redraw();
981#endif
982 if (moveThumb) {
983 SetVerticalScrollPos();
984 }
985 }
986}
987
988void Editor::ScrollText(Sci::Line /* linesToMove */) {
989 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
990 Redraw();
991}
992
993void Editor::HorizontalScrollTo(int xPos) {
994 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
995 if (xPos < 0)
996 xPos = 0;
997 if (!Wrapping() && (xOffset != xPos)) {
998 xOffset = xPos;
999 ContainerNeedsUpdate(Update::HScroll);
1000 SetHorizontalScrollPos();
1001 RedrawRect(GetClientRectangle());
1002 }
1003}
1004
1005void Editor::VerticalCentreCaret() {
1006 const Sci::Line lineDoc =
1007 pdoc->SciLineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
1008 const Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
1009 const Sci::Line newTop = lineDisplay - (LinesOnScreen() / 2);
1010 if (topLine != newTop) {
1011 SetTopLine(newTop > 0 ? newTop : 0);
1012 RedrawRect(GetClientRectangle());
1013 }
1014}
1015
1016void Editor::MoveSelectedLines(int lineDelta) {
1017
1018 if (sel.IsRectangular()) {
1019 return;
1020 }
1021
1022 // if selection doesn't start at the beginning of the line, set the new start
1023 Sci::Position selectionStart = SelectionStart().Position();
1024 const Sci::Line startLine = pdoc->SciLineFromPosition(selectionStart);
1025 const Sci::Position beginningOfStartLine = pdoc->LineStart(startLine);
1026 selectionStart = beginningOfStartLine;
1027
1028 // if selection doesn't end at the beginning of a line greater than that of the start,
1029 // then set it at the beginning of the next one
1030 Sci::Position selectionEnd = SelectionEnd().Position();
1031 const Sci::Line endLine = pdoc->SciLineFromPosition(selectionEnd);
1032 const Sci::Position beginningOfEndLine = pdoc->LineStart(endLine);
1033 bool appendEol = false;
1034 if (selectionEnd > beginningOfEndLine
1035 || selectionStart == selectionEnd) {
1036 selectionEnd = pdoc->LineStart(endLine + 1);
1037 appendEol = (selectionEnd == pdoc->Length() && pdoc->SciLineFromPosition(selectionEnd) == endLine);
1038 }
1039
1040 // if there's nowhere for the selection to move
1041 // (i.e. at the beginning going up or at the end going down),
1042 // stop it right there!
1043 if ((selectionStart == 0 && lineDelta < 0)
1044 || (selectionEnd == pdoc->Length() && lineDelta > 0)
1045 || selectionStart == selectionEnd) {
1046 return;
1047 }
1048
1049 UndoGroup ug(pdoc);
1050
1051 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
1052 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
1053 ClearSelection();
1054 selectionEnd = CurrentPosition();
1055 }
1056 SetSelection(selectionStart, selectionEnd);
1057
1058 SelectionText selectedText;
1059 CopySelectionRange(&selectedText);
1060
1061 const Point currentLocation = LocationFromPosition(CurrentPosition());
1062 const Sci::Line currentLine = LineFromLocation(currentLocation);
1063
1064 if (appendEol)
1065 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
1066 ClearSelection();
1067
1068 const char *eol = StringFromEOLMode(pdoc->eolMode);
1069 if (currentLine + lineDelta >= pdoc->LinesTotal())
1070 pdoc->InsertString(pdoc->Length(), eol, strlen(eol));
1071 GoToLine(currentLine + lineDelta);
1072
1073 Sci::Position selectionLength = pdoc->InsertString(CurrentPosition(), selectedText.Data(), selectedText.Length());
1074 if (appendEol) {
1075 const Sci::Position lengthInserted = pdoc->InsertString(CurrentPosition() + selectionLength, eol, strlen(eol));
1076 selectionLength += lengthInserted;
1077 }
1078 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1079}
1080
1081void Editor::MoveSelectedLinesUp() {
1082 MoveSelectedLines(-1);
1083}
1084
1085void Editor::MoveSelectedLinesDown() {
1086 MoveSelectedLines(1);
1087}
1088
1089void Editor::MoveCaretInsideView(bool ensureVisible) {
1090 const PRectangle rcClient = GetTextRectangle();
1091 const Point pt = PointMainCaret();
1092 if (pt.y < rcClient.top) {
1093 MovePositionTo(SPositionFromLocation(
1094 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top)),
1095 false, false, UserVirtualSpace()),
1096 Selection::SelTypes::none, ensureVisible);
1097 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1098 const ptrdiff_t yOfLastLineFullyDisplayed = static_cast<ptrdiff_t>(rcClient.top) + (LinesOnScreen() - 1) * vs.lineHeight;
1099 MovePositionTo(SPositionFromLocation(
1100 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top + yOfLastLineFullyDisplayed)),
1101 false, false, UserVirtualSpace()),
1102 Selection::SelTypes::none, ensureVisible);
1103 }
1104}
1105
1106Sci::Line Editor::DisplayFromPosition(Sci::Position pos) {
1107 AutoSurface surface(this);
1108 return view.DisplayFromPosition(surface, *this, pos, vs);
1109}
1110
1111/**
1112 * Ensure the caret is reasonably visible in context.
1113 *
1114Caret policy in Scintilla
1115
1116If slop is set, we can define a slop value.
1117This value defines an unwanted zone (UZ) where the caret is... unwanted.
1118This zone is defined as a number of pixels near the vertical margins,
1119and as a number of lines near the horizontal margins.
1120By keeping the caret away from the edges, it is seen within its context,
1121so it is likely that the identifier that the caret is on can be completely seen,
1122and that the current line is seen with some of the lines following it which are
1123often dependent on that line.
1124
1125If strict is set, the policy is enforced... strictly.
1126The caret is centred on the display if slop is not set,
1127and cannot go in the UZ if slop is set.
1128
1129If jumps is set, the display is moved more energetically
1130so the caret can move in the same direction longer before the policy is applied again.
1131'3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1132
1133If even is not set, instead of having symmetrical UZs,
1134the left and bottom UZs are extended up to right and top UZs respectively.
1135This way, we favour the displaying of useful information: the beginning of lines,
1136where most code reside, and the lines after the caret, eg. the body of a function.
1137
1138 | | | | |
1139slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1140 | | | | | visibility or going into the UZ) display is...
1141-----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1142 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1143 0 | 0 | 0 | 1 | Yes | moved by one position
1144 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1145 0 | 0 | 1 | 1 | Yes | centred on the caret
1146 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1147 0 | 1 | - | 1 | No, caret is always centred | -
1148 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1149 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1150 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1151 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1152 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1153 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1154 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1155*/
1156
1157Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange &range,
1158 const XYScrollOptions options, CaretPolicies policies) {
1159 const PRectangle rcClient = GetTextRectangle();
1160 const Point ptOrigin = GetVisibleOriginInMain();
1161 const Point pt = LocationFromPosition(range.caret) + ptOrigin;
1162 const Point ptAnchor = LocationFromPosition(range.anchor) + ptOrigin;
1163 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1164
1165 XYScrollPosition newXY(xOffset, topLine);
1166 if (rcClient.Empty()) {
1167 return newXY;
1168 }
1169
1170 // Vertical positioning
1171 if (FlagSet(options, XYScrollOptions::vertical) &&
1172 (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || FlagSet(policies.y.policy, CaretPolicy::Strict))) {
1173 const Sci::Line lineCaret = DisplayFromPosition(range.caret.Position());
1174 const Sci::Line linesOnScreen = LinesOnScreen();
1175 const Sci::Line halfScreen = std::max(linesOnScreen - 1, static_cast<Sci::Line>(2)) / 2;
1176 const bool bSlop = FlagSet(policies.y.policy, CaretPolicy::Slop);
1177 const bool bStrict = FlagSet(policies.y.policy, CaretPolicy::Strict);
1178 const bool bJump = FlagSet(policies.y.policy, CaretPolicy::Jumps);
1179 const bool bEven = FlagSet(policies.y.policy, CaretPolicy::Even);
1180
1181 // It should be possible to scroll the window to show the caret,
1182 // but this fails to remove the caret on GTK+
1183 if (bSlop) { // A margin is defined
1184 Sci::Line yMoveT, yMoveB;
1185 if (bStrict) {
1186 Sci::Line yMarginT, yMarginB;
1187 if (!FlagSet(options, XYScrollOptions::useMargin)) {
1188 // In drag mode, avoid moves
1189 // otherwise, a double click will select several lines.
1190 yMarginT = yMarginB = 0;
1191 } else {
1192 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1193 // a maximum of slightly less than half the height of the text area.
1194 yMarginT = std::clamp<Sci::Line>(policies.y.slop, 1, halfScreen);
1195 if (bEven) {
1196 yMarginB = yMarginT;
1197 } else {
1198 yMarginB = linesOnScreen - yMarginT - 1;
1199 }
1200 }
1201 yMoveT = yMarginT;
1202 if (bEven) {
1203 if (bJump) {
1204 yMoveT = std::clamp<Sci::Line>(policies.y.slop * 3, 1, halfScreen);
1205 }
1206 yMoveB = yMoveT;
1207 } else {
1208 yMoveB = linesOnScreen - yMoveT - 1;
1209 }
1210 if (lineCaret < topLine + yMarginT) {
1211 // Caret goes too high
1212 newXY.topLine = lineCaret - yMoveT;
1213 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1214 // Caret goes too low
1215 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1216 }
1217 } else { // Not strict
1218 yMoveT = bJump ? policies.y.slop * 3 : policies.y.slop;
1219 yMoveT = std::clamp<Sci::Line>(yMoveT, 1, halfScreen);
1220 if (bEven) {
1221 yMoveB = yMoveT;
1222 } else {
1223 yMoveB = linesOnScreen - yMoveT - 1;
1224 }
1225 if (lineCaret < topLine) {
1226 // Caret goes too high
1227 newXY.topLine = lineCaret - yMoveT;
1228 } else if (lineCaret > topLine + linesOnScreen - 1) {
1229 // Caret goes too low
1230 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1231 }
1232 }
1233 } else { // No slop
1234 if (!bStrict && !bJump) {
1235 // Minimal move
1236 if (lineCaret < topLine) {
1237 // Caret goes too high
1238 newXY.topLine = lineCaret;
1239 } else if (lineCaret > topLine + linesOnScreen - 1) {
1240 // Caret goes too low
1241 if (bEven) {
1242 newXY.topLine = lineCaret - linesOnScreen + 1;
1243 } else {
1244 newXY.topLine = lineCaret;
1245 }
1246 }
1247 } else { // Strict or going out of display
1248 if (bEven) {
1249 // Always centre caret
1250 newXY.topLine = lineCaret - halfScreen;
1251 } else {
1252 // Always put caret on top of display
1253 newXY.topLine = lineCaret;
1254 }
1255 }
1256 }
1257 if (!(range.caret == range.anchor)) {
1258 const Sci::Line lineAnchor = DisplayFromPosition(range.anchor.Position());
1259 if (lineAnchor < lineCaret) {
1260 // Shift up to show anchor or as much of range as possible
1261 newXY.topLine = std::min(newXY.topLine, lineAnchor);
1262 newXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());
1263 } else {
1264 // Shift down to show anchor or as much of range as possible
1265 newXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());
1266 newXY.topLine = std::min(newXY.topLine, lineCaret);
1267 }
1268 }
1269 newXY.topLine = std::clamp<Sci::Line>(newXY.topLine, 0, MaxScrollPos());
1270 }
1271
1272 // Horizontal positioning
1273 if (FlagSet(options, XYScrollOptions::horizontal) && !Wrapping()) {
1274 const int halfScreen = std::max(static_cast<int>(rcClient.Width()) - 4, 4) / 2;
1275 const bool bSlop = FlagSet(policies.x.policy, CaretPolicy::Slop);
1276 const bool bStrict = FlagSet(policies.x.policy, CaretPolicy::Strict);
1277 const bool bJump = FlagSet(policies.x.policy, CaretPolicy::Jumps);
1278 const bool bEven = FlagSet(policies.x.policy, CaretPolicy::Even);
1279
1280 if (bSlop) { // A margin is defined
1281 int xMoveL, xMoveR;
1282 if (bStrict) {
1283 int xMarginL, xMarginR;
1284 if (!FlagSet(options, XYScrollOptions::useMargin)) {
1285 // In drag mode, avoid moves unless very near of the margin
1286 // otherwise, a simple click will select text.
1287 xMarginL = xMarginR = 2;
1288 } else {
1289 // xMargin must equal to caretXSlop, with a minimum of 2 and
1290 // a maximum of slightly less than half the width of the text area.
1291 xMarginR = std::clamp(policies.x.slop, 2, halfScreen);
1292 if (bEven) {
1293 xMarginL = xMarginR;
1294 } else {
1295 xMarginL = static_cast<int>(rcClient.Width()) - xMarginR - 4;
1296 }
1297 }
1298 if (bJump && bEven) {
1299 // Jump is used only in even mode
1300 xMoveL = xMoveR = std::clamp(policies.x.slop * 3, 1, halfScreen);
1301 } else {
1302 xMoveL = xMoveR = 0; // Not used, avoid a warning
1303 }
1304 if (pt.x < rcClient.left + xMarginL) {
1305 // Caret is on the left of the display
1306 if (bJump && bEven) {
1307 newXY.xOffset -= xMoveL;
1308 } else {
1309 // Move just enough to allow to display the caret
1310 newXY.xOffset -= static_cast<int>((rcClient.left + xMarginL) - pt.x);
1311 }
1312 } else if (pt.x >= rcClient.right - xMarginR) {
1313 // Caret is on the right of the display
1314 if (bJump && bEven) {
1315 newXY.xOffset += xMoveR;
1316 } else {
1317 // Move just enough to allow to display the caret
1318 newXY.xOffset += static_cast<int>(pt.x - (rcClient.right - xMarginR) + 1);
1319 }
1320 }
1321 } else { // Not strict
1322 xMoveR = bJump ? policies.x.slop * 3 : policies.x.slop;
1323 xMoveR = std::clamp(xMoveR, 1, halfScreen);
1324 if (bEven) {
1325 xMoveL = xMoveR;
1326 } else {
1327 xMoveL = static_cast<int>(rcClient.Width()) - xMoveR - 4;
1328 }
1329 if (pt.x < rcClient.left) {
1330 // Caret is on the left of the display
1331 newXY.xOffset -= xMoveL;
1332 } else if (pt.x >= rcClient.right) {
1333 // Caret is on the right of the display
1334 newXY.xOffset += xMoveR;
1335 }
1336 }
1337 } else { // No slop
1338 if (bStrict ||
1339 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1340 // Strict or going out of display
1341 if (bEven) {
1342 // Centre caret
1343 newXY.xOffset += static_cast<int>(pt.x - rcClient.left - halfScreen);
1344 } else {
1345 // Put caret on right
1346 newXY.xOffset += static_cast<int>(pt.x - rcClient.right + 1);
1347 }
1348 } else {
1349 // Move just enough to allow to display the caret
1350 if (pt.x < rcClient.left) {
1351 // Caret is on the left of the display
1352 if (bEven) {
1353 newXY.xOffset -= static_cast<int>(rcClient.left - pt.x);
1354 } else {
1355 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1356 }
1357 } else if (pt.x >= rcClient.right) {
1358 // Caret is on the right of the display
1359 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1360 }
1361 }
1362 }
1363 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1364 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1365 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 2;
1366 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1367 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 2;
1368 if (vs.IsBlockCaretStyle() || view.imeCaretBlockOverride) {
1369 // Ensure we can see a good portion of the block caret
1370 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1371 }
1372 }
1373 if (!(range.caret == range.anchor)) {
1374 if (ptAnchor.x < pt.x) {
1375 // Shift to left to show anchor or as much of range as possible
1376 const int maxOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.left) - 1;
1377 const int minOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 1;
1378 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1379 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1380 } else {
1381 // Shift to right to show anchor or as much of range as possible
1382 const int minOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.right) + 1;
1383 const int maxOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 1;
1384 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1385 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1386 }
1387 }
1388 if (newXY.xOffset < 0) {
1389 newXY.xOffset = 0;
1390 }
1391 }
1392
1393 return newXY;
1394}
1395
1396void Editor::SetXYScroll(XYScrollPosition newXY) {
1397 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1398 if (newXY.topLine != topLine) {
1399 SetTopLine(newXY.topLine);
1400 SetVerticalScrollPos();
1401 }
1402 if (newXY.xOffset != xOffset) {
1403 xOffset = newXY.xOffset;
1404 ContainerNeedsUpdate(Update::HScroll);
1405 if (newXY.xOffset > 0) {
1406 const PRectangle rcText = GetTextRectangle();
1407 if (horizontalScrollBarVisible &&
1408 rcText.Width() + xOffset > scrollWidth) {
1409 scrollWidth = xOffset + static_cast<int>(rcText.Width());
1410 SetScrollBars();
1411 }
1412 }
1413 SetHorizontalScrollPos();
1414 }
1415 Redraw();
1416 UpdateSystemCaret();
1417 }
1418}
1419
1420void Editor::ScrollRange(SelectionRange range) {
1421 SetXYScroll(XYScrollToMakeVisible(range, XYScrollOptions::all, caretPolicies));
1422}
1423
1424void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1425 SetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret),
1426 (useMargin?XYScrollOptions::useMargin:XYScrollOptions::none)|
1427 (vert?XYScrollOptions::vertical:XYScrollOptions::none)|
1428 (horiz?XYScrollOptions::horizontal:XYScrollOptions::none),
1429 caretPolicies));
1430}
1431
1432void Editor::ShowCaretAtCurrentPosition() {
1433 if (hasFocus) {
1434 caret.active = true;
1435 caret.on = true;
1436 FineTickerCancel(TickReason::caret);
1437 if (caret.period > 0)
1438 FineTickerStart(TickReason::caret, caret.period, caret.period/10);
1439 } else {
1440 caret.active = false;
1441 caret.on = false;
1442 FineTickerCancel(TickReason::caret);
1443 }
1444 InvalidateCaret();
1445}
1446
1447void Editor::DropCaret() {
1448 caret.active = false;
1449 FineTickerCancel(TickReason::caret);
1450 InvalidateCaret();
1451}
1452
1453void Editor::CaretSetPeriod(int period) {
1454 if (caret.period != period) {
1455 caret.period = period;
1456 caret.on = true;
1457 FineTickerCancel(TickReason::caret);
1458 if ((caret.active) && (caret.period > 0))
1459 FineTickerStart(TickReason::caret, caret.period, caret.period/10);
1460 InvalidateCaret();
1461 }
1462}
1463
1464void Editor::InvalidateCaret() {
1465 if (posDrag.IsValid()) {
1466 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1467 } else {
1468 for (size_t r=0; r<sel.Count(); r++) {
1469 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1470 }
1471 }
1472 UpdateSystemCaret();
1473}
1474
1475void Editor::NotifyCaretMove() {
1476}
1477
1478void Editor::UpdateSystemCaret() {
1479}
1480
1481bool Editor::Wrapping() const noexcept {
1482 return vs.wrap.state != Wrap::None;
1483}
1484
1485void Editor::NeedWrapping(Sci::Line docLineStart, Sci::Line docLineEnd) {
1486//Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd);
1487 if (wrapPending.AddRange(docLineStart, docLineEnd)) {
1488 view.llc.Invalidate(LineLayout::ValidLevel::positions);
1489 }
1490 // Wrap lines during idle.
1491 if (Wrapping() && wrapPending.NeedsWrap()) {
1492 SetIdle(true);
1493 }
1494}
1495
1496bool Editor::WrapOneLine(Surface *surface, Sci::Line lineToWrap) {
1497 std::shared_ptr<LineLayout> ll = view.RetrieveLineLayout(lineToWrap, *this);
1498 int linesWrapped = 1;
1499 if (ll) {
1500 view.LayoutLine(*this, surface, vs, ll.get(), wrapWidth);
1501 linesWrapped = ll->lines;
1502 }
1503 return pcs->SetHeight(lineToWrap, linesWrapped +
1504 ((vs.annotationVisible != AnnotationVisible::Hidden) ? pdoc->AnnotationLines(lineToWrap) : 0));
1505}
1506
1507// Perform wrapping for a subset of the lines needing wrapping.
1508// wsAll: wrap all lines which need wrapping in this single call
1509// wsVisible: wrap currently visible lines
1510// wsIdle: wrap one page + 100 lines
1511// Return true if wrapping occurred.
1512bool Editor::WrapLines(WrapScope ws) {
1513 Sci::Line goodTopLine = topLine;
1514 bool wrapOccurred = false;
1515 if (!Wrapping()) {
1516 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1517 wrapWidth = LineLayout::wrapWidthInfinite;
1518 for (Sci::Line lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1519 pcs->SetHeight(lineDoc, 1 +
1520 ((vs.annotationVisible != AnnotationVisible::Hidden) ? pdoc->AnnotationLines(lineDoc) : 0));
1521 }
1522 wrapOccurred = true;
1523 }
1524 wrapPending.Reset();
1525
1526 } else if (wrapPending.NeedsWrap()) {
1527 wrapPending.start = std::min(wrapPending.start, pdoc->LinesTotal());
1528 if (!SetIdle(true)) {
1529 // Idle processing not supported so full wrap required.
1530 ws = WrapScope::wsAll;
1531 }
1532 // Decide where to start wrapping
1533 Sci::Line lineToWrap = wrapPending.start;
1534 Sci::Line lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal());
1535 const Sci::Line lineDocTop = pcs->DocFromDisplay(topLine);
1536 const Sci::Line subLineTop = topLine - pcs->DisplayFromDoc(lineDocTop);
1537 if (ws == WrapScope::wsVisible) {
1538 lineToWrap = std::clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal());
1539 // Priority wrap to just after visible area.
1540 // Since wrapping could reduce display lines, treat each
1541 // as taking only one display line.
1542 lineToWrapEnd = lineDocTop;
1543 Sci::Line lines = LinesOnScreen() + 1;
1544 constexpr double secondsAllowed = 0.1;
1545 const size_t actionsInAllowedTime = std::clamp<Sci::Line>(
1546 durationWrapOneByte.ActionsInAllowedTime(secondsAllowed),
1547 0x2000, 0x200000);
1548 const Sci::Line lineLast = pdoc->LineFromPositionAfter(lineToWrap, actionsInAllowedTime);
1549 const Sci::Line maxLine = std::min(lineLast, pcs->LinesInDoc());
1550 while ((lineToWrapEnd < maxLine) && (lines>0)) {
1551 if (pcs->GetVisible(lineToWrapEnd))
1552 lines--;
1553 lineToWrapEnd++;
1554 }
1555 // .. and if the paint window is outside pending wraps
1556 if ((lineToWrap > wrapPending.end) || (lineToWrapEnd < wrapPending.start)) {
1557 // Currently visible text does not need wrapping
1558 return false;
1559 }
1560 } else if (ws == WrapScope::wsIdle) {
1561 // Try to keep time taken by wrapping reasonable so interaction remains smooth.
1562 constexpr double secondsAllowed = 0.01;
1563 const size_t actionsInAllowedTime = std::clamp<Sci::Line>(
1564 durationWrapOneByte.ActionsInAllowedTime(secondsAllowed),
1565 0x200, 0x20000);
1566 lineToWrapEnd = pdoc->LineFromPositionAfter(lineToWrap, actionsInAllowedTime);
1567 }
1568 const Sci::Line lineEndNeedWrap = std::min(wrapPending.end, pdoc->LinesTotal());
1569 lineToWrapEnd = std::min(lineToWrapEnd, lineEndNeedWrap);
1570
1571 // Ensure all lines being wrapped are styled.
1572 pdoc->EnsureStyledTo(pdoc->LineStart(lineToWrapEnd));
1573
1574 if (lineToWrap < lineToWrapEnd) {
1575
1576 PRectangle rcTextArea = GetClientRectangle();
1577 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1578 rcTextArea.right -= vs.rightMarginWidth;
1579 wrapWidth = static_cast<int>(rcTextArea.Width());
1580 RefreshStyleData();
1581 AutoSurface surface(this);
1582 if (surface) {
1583//Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
1584
1585 const size_t bytesBeingWrapped = pdoc->LineStart(lineToWrapEnd) - pdoc->LineStart(lineToWrap);
1586 ElapsedPeriod epWrapping;
1587 while (lineToWrap < lineToWrapEnd) {
1588 if (WrapOneLine(surface, lineToWrap)) {
1589 wrapOccurred = true;
1590 }
1591 wrapPending.Wrapped(lineToWrap);
1592 lineToWrap++;
1593 }
1594 durationWrapOneByte.AddSample(bytesBeingWrapped, epWrapping.Duration());
1595
1596 goodTopLine = pcs->DisplayFromDoc(lineDocTop) + std::min(
1597 subLineTop, static_cast<Sci::Line>(pcs->GetHeight(lineDocTop)-1));
1598 }
1599 }
1600
1601 // If wrapping is done, bring it to resting position
1602 if (wrapPending.start >= lineEndNeedWrap) {
1603 wrapPending.Reset();
1604 }
1605 }
1606
1607 if (wrapOccurred) {
1608 SetScrollBars();
1609 SetTopLine(std::clamp<Sci::Line>(goodTopLine, 0, MaxScrollPos()));
1610 SetVerticalScrollPos();
1611 }
1612
1613 return wrapOccurred;
1614}
1615
1616void Editor::LinesJoin() {
1617 if (!RangeContainsProtected(targetRange.start.Position(), targetRange.end.Position())) {
1618 UndoGroup ug(pdoc);
1619 bool prevNonWS = true;
1620 for (Sci::Position pos = targetRange.start.Position(); pos < targetRange.end.Position(); pos++) {
1621 if (pdoc->IsPositionInLineEnd(pos)) {
1622 targetRange.end.Add(-pdoc->LenChar(pos));
1623 pdoc->DelChar(pos);
1624 if (prevNonWS) {
1625 // Ensure at least one space separating previous lines
1626 const Sci::Position lengthInserted = pdoc->InsertString(pos, " ", 1);
1627 targetRange.end.Add(lengthInserted);
1628 }
1629 } else {
1630 prevNonWS = pdoc->CharAt(pos) != ' ';
1631 }
1632 }
1633 }
1634}
1635
1636const char *Editor::StringFromEOLMode(EndOfLine eolMode) noexcept {
1637 if (eolMode == EndOfLine::CrLf) {
1638 return "\r\n";
1639 } else if (eolMode == EndOfLine::Cr) {
1640 return "\r";
1641 } else {
1642 return "\n";
1643 }
1644}
1645
1646void Editor::LinesSplit(int pixelWidth) {
1647 if (!RangeContainsProtected(targetRange.start.Position(), targetRange.end.Position())) {
1648 if (pixelWidth == 0) {
1649 const PRectangle rcText = GetTextRectangle();
1650 pixelWidth = static_cast<int>(rcText.Width());
1651 }
1652 const Sci::Line lineStart = pdoc->SciLineFromPosition(targetRange.start.Position());
1653 Sci::Line lineEnd = pdoc->SciLineFromPosition(targetRange.end.Position());
1654 const char *eol = StringFromEOLMode(pdoc->eolMode);
1655 UndoGroup ug(pdoc);
1656 for (Sci::Line line = lineStart; line <= lineEnd; line++) {
1657 AutoSurface surface(this);
1658 std::shared_ptr<LineLayout> ll = view.RetrieveLineLayout(line, *this);
1659 if (surface && ll) {
1660 const Sci::Position posLineStart = pdoc->LineStart(line);
1661 view.LayoutLine(*this, surface, vs, ll.get(), pixelWidth);
1662 Sci::Position lengthInsertedTotal = 0;
1663 for (int subLine = 1; subLine < ll->lines; subLine++) {
1664 const Sci::Position lengthInserted = pdoc->InsertString(
1665 posLineStart + lengthInsertedTotal + ll->LineStart(subLine),
1666 eol, strlen(eol));
1667 targetRange.end.Add(lengthInserted);
1668 lengthInsertedTotal += lengthInserted;
1669 }
1670 }
1671 lineEnd = pdoc->SciLineFromPosition(targetRange.end.Position());
1672 }
1673 }
1674}
1675
1676void Editor::PaintSelMargin(Surface *surfaceWindow, const PRectangle &rc) {
1677 if (vs.fixedColumnWidth == 0)
1678 return;
1679
1680 RefreshStyleData();
1681 RefreshPixMaps(surfaceWindow);
1682
1683 // On GTK+ with Ubuntu overlay scroll bars, the surface may have been finished
1684 // at this point. The Initialised call checks for this case and sets the status
1685 // to be bad which avoids crashes in following calls.
1686 if (!surfaceWindow->Initialised()) {
1687 return;
1688 }
1689
1690 PRectangle rcMargin = GetClientRectangle();
1691 const Point ptOrigin = GetVisibleOriginInMain();
1692 rcMargin.Move(0, -ptOrigin.y);
1693 rcMargin.left = 0;
1694 rcMargin.right = static_cast<XYPOSITION>(vs.fixedColumnWidth);
1695
1696 if (!rc.Intersects(rcMargin))
1697 return;
1698
1699 Surface *surface;
1700 if (view.bufferedDraw) {
1701 surface = marginView.pixmapSelMargin.get();
1702 } else {
1703 surface = surfaceWindow;
1704 }
1705
1706 // Clip vertically to paint area to avoid drawing line numbers
1707 if (rcMargin.bottom > rc.bottom)
1708 rcMargin.bottom = rc.bottom;
1709 if (rcMargin.top < rc.top)
1710 rcMargin.top = rc.top;
1711
1712 marginView.PaintMargin(surface, topLine, rc, rcMargin, *this, vs);
1713
1714 if (view.bufferedDraw) {
1715 marginView.pixmapSelMargin->FlushDrawing();
1716 surfaceWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *marginView.pixmapSelMargin);
1717 }
1718}
1719
1720void Editor::RefreshPixMaps(Surface *surfaceWindow) {
1721 view.RefreshPixMaps(surfaceWindow, vs);
1722 marginView.RefreshPixMaps(surfaceWindow, vs);
1723 if (view.bufferedDraw) {
1724 const PRectangle rcClient = GetClientRectangle();
1725 if (!view.pixmapLine) {
1726 view.pixmapLine = surfaceWindow->AllocatePixMap(static_cast<int>(rcClient.Width()), vs.lineHeight);
1727 }
1728 if (!marginView.pixmapSelMargin) {
1729 marginView.pixmapSelMargin = surfaceWindow->AllocatePixMap(vs.fixedColumnWidth,
1730 static_cast<int>(rcClient.Height()));
1731 }
1732 }
1733}
1734
1735void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
1736 redrawPendingText = false;
1737 redrawPendingMargin = false;
1738
1739 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
1740 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
1741
1742 RefreshStyleData();
1743 if (paintState == PaintState::abandoned)
1744 return; // Scroll bars may have changed so need redraw
1745 RefreshPixMaps(surfaceWindow);
1746
1747 paintAbandonedByStyling = false;
1748
1749 StyleAreaBounded(rcArea, false);
1750
1751 const PRectangle rcClient = GetClientRectangle();
1752 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
1753 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1754
1755 if (NotifyUpdateUI()) {
1756 RefreshStyleData();
1757 RefreshPixMaps(surfaceWindow);
1758 }
1759
1760 // Wrap the visible lines if needed.
1761 if (WrapLines(WrapScope::wsVisible)) {
1762 // The wrapping process has changed the height of some lines so
1763 // abandon this paint for a complete repaint.
1764 if (AbandonPaint()) {
1765 return;
1766 }
1767 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
1768 }
1769
1770 if (!marginView.pixmapSelPattern->Initialised()) {
1771 // When Direct2D is used, pixmap creation may fail with D2DERR_RECREATE_TARGET so
1772 // abandon this paint to avoid further failures.
1773 // Main drawing surface and pixmaps should be recreated by next paint.
1774 return;
1775 }
1776
1777 if (!view.bufferedDraw)
1778 surfaceWindow->SetClip(rcArea);
1779
1780 if (paintState != PaintState::abandoned) {
1781 if (vs.marginInside) {
1782 PaintSelMargin(surfaceWindow, rcArea);
1783 PRectangle rcRightMargin = rcClient;
1784 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
1785 if (rcArea.Intersects(rcRightMargin)) {
1786 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[StyleDefault].back);
1787 }
1788 } else { // Else separate view so separate paint event but leftMargin included to allow overlap
1789 PRectangle rcLeftMargin = rcArea;
1790 rcLeftMargin.left = 0;
1791 rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
1792 if (rcArea.Intersects(rcLeftMargin)) {
1793 surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[StyleDefault].back);
1794 }
1795 }
1796 }
1797
1798 if (paintState == PaintState::abandoned) {
1799 // Either styling or NotifyUpdateUI noticed that painting is needed
1800 // outside the current painting rectangle
1801 //Platform::DebugPrintf("Abandoning paint\n");
1802 if (Wrapping()) {
1803 if (paintAbandonedByStyling) {
1804 // Styling has spilled over a line end, such as occurs by starting a multiline
1805 // comment. The width of subsequent text may have changed, so rewrap.
1806 NeedWrapping(pcs->DocFromDisplay(topLine));
1807 }
1808 }
1809 if (!view.bufferedDraw)
1810 surfaceWindow->PopClip();
1811 return;
1812 }
1813
1814 view.PaintText(surfaceWindow, *this, rcArea, rcClient, vs);
1815
1816 if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) {
1817 scrollWidth = view.lineWidthMaxSeen;
1818 if (!FineTickerRunning(TickReason::widen)) {
1819 FineTickerStart(TickReason::widen, 50, 5);
1820 }
1821 }
1822
1823 if (!view.bufferedDraw)
1824 surfaceWindow->PopClip();
1825
1826 NotifyPainted();
1827}
1828
1829// This is mostly copied from the Paint method but with some things omitted
1830// such as the margin markers, line numbers, selection and caret
1831// Should be merged back into a combined Draw method.
1832Sci::Position Editor::FormatRange(bool draw, const RangeToFormat *pfr) {
1833 if (!pfr)
1834 return 0;
1835
1836 AutoSurface surface(pfr->hdc, this, Technology::Default);
1837 if (!surface)
1838 return 0;
1839 AutoSurface surfaceMeasure(pfr->hdcTarget, this, Technology::Default);
1840 if (!surfaceMeasure) {
1841 return 0;
1842 }
1843 return view.FormatRange(draw, pfr, surface, surfaceMeasure, *this, vs);
1844}
1845
1846long Editor::TextWidth(uptr_t style, const char *text) {
1847 RefreshStyleData();
1848 AutoSurface surface(this);
1849 if (surface) {
1850 return std::lround(surface->WidthText(vs.styles[style].font.get(), text));
1851 } else {
1852 return 1;
1853 }
1854}
1855
1856// Empty method is overridden on GTK+ to show / hide scrollbars
1857void Editor::ReconfigureScrollBars() {}
1858
1859void Editor::SetScrollBars() {
1860 RefreshStyleData();
1861
1862 const Sci::Line nMax = MaxScrollPos();
1863 const Sci::Line nPage = LinesOnScreen();
1864 const bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
1865 if (modified) {
1866 DwellEnd(true);
1867 }
1868
1869 // TODO: ensure always showing as many lines as possible
1870 // May not be, if, for example, window made larger
1871 if (topLine > MaxScrollPos()) {
1872 SetTopLine(std::clamp<Sci::Line>(topLine, 0, MaxScrollPos()));
1873 SetVerticalScrollPos();
1874 Redraw();
1875 }
1876 if (modified) {
1877 if (!AbandonPaint())
1878 Redraw();
1879 }
1880 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
1881}
1882
1883void Editor::ChangeSize() {
1884 DropGraphics();
1885 SetScrollBars();
1886 if (Wrapping()) {
1887 PRectangle rcTextArea = GetClientRectangle();
1888 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1889 rcTextArea.right -= vs.rightMarginWidth;
1890 if (wrapWidth != rcTextArea.Width()) {
1891 NeedWrapping();
1892 Redraw();
1893 }
1894 }
1895}
1896
1897Sci::Position Editor::RealizeVirtualSpace(Sci::Position position, Sci::Position virtualSpace) {
1898 if (virtualSpace > 0) {
1899 const Sci::Line line = pdoc->SciLineFromPosition(position);
1900 const Sci::Position indent = pdoc->GetLineIndentPosition(line);
1901 if (indent == position) {
1902 return pdoc->SetLineIndentation(line, pdoc->GetLineIndentation(line) + virtualSpace);
1903 } else {
1904 std::string spaceText(virtualSpace, ' ');
1905 const Sci::Position lengthInserted = pdoc->InsertString(position, spaceText.c_str(), virtualSpace);
1906 position += lengthInserted;
1907 }
1908 }
1909 return position;
1910}
1911
1912SelectionPosition Editor::RealizeVirtualSpace(const SelectionPosition &position) {
1913 // Return the new position with no virtual space
1914 return SelectionPosition(RealizeVirtualSpace(position.Position(), position.VirtualSpace()));
1915}
1916
1917void Editor::AddChar(char ch) {
1918 char s[2];
1919 s[0] = ch;
1920 s[1] = '\0';
1921 InsertCharacter(std::string_view(s, 1), CharacterSource::DirectInput);
1922}
1923
1924void Editor::FilterSelections() {
1925 if (!additionalSelectionTyping && (sel.Count() > 1)) {
1926 InvalidateWholeSelection();
1927 sel.DropAdditionalRanges();
1928 }
1929}
1930
1931// InsertCharacter inserts a character encoded in document code page.
1932void Editor::InsertCharacter(std::string_view sv, CharacterSource charSource) {
1933 if (sv.empty()) {
1934 return;
1935 }
1936 FilterSelections();
1937 {
1938 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
1939
1940 // Vector elements point into selection in order to change selection.
1941 std::vector<SelectionRange *> selPtrs;
1942 for (size_t r = 0; r < sel.Count(); r++) {
1943 selPtrs.push_back(&sel.Range(r));
1944 }
1945 // Order selections by position in document.
1946 std::sort(selPtrs.begin(), selPtrs.end(),
1947 [](const SelectionRange *a, const SelectionRange *b) noexcept {return *a < *b;});
1948
1949 // Loop in reverse to avoid disturbing positions of selections yet to be processed.
1950 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
1951 rit != selPtrs.rend(); ++rit) {
1952 SelectionRange *currentSel = *rit;
1953 if (!RangeContainsProtected(currentSel->Start().Position(),
1954 currentSel->End().Position())) {
1955 Sci::Position positionInsert = currentSel->Start().Position();
1956 if (!currentSel->Empty()) {
1957 if (currentSel->Length()) {
1958 pdoc->DeleteChars(positionInsert, currentSel->Length());
1959 currentSel->ClearVirtualSpace();
1960 } else {
1961 // Range is all virtual so collapse to start of virtual space
1962 currentSel->MinimizeVirtualSpace();
1963 }
1964 } else if (inOverstrike) {
1965 if (positionInsert < pdoc->Length()) {
1966 if (!pdoc->IsPositionInLineEnd(positionInsert)) {
1967 pdoc->DelChar(positionInsert);
1968 currentSel->ClearVirtualSpace();
1969 }
1970 }
1971 }
1972 positionInsert = RealizeVirtualSpace(positionInsert, currentSel->caret.VirtualSpace());
1973 const Sci::Position lengthInserted = pdoc->InsertString(positionInsert, sv.data(), sv.length());
1974 if (lengthInserted > 0) {
1975 currentSel->caret.SetPosition(positionInsert + lengthInserted);
1976 currentSel->anchor.SetPosition(positionInsert + lengthInserted);
1977 }
1978 currentSel->ClearVirtualSpace();
1979 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1980 if (Wrapping()) {
1981 AutoSurface surface(this);
1982 if (surface) {
1983 if (WrapOneLine(surface, pdoc->SciLineFromPosition(positionInsert))) {
1984 SetScrollBars();
1985 SetVerticalScrollPos();
1986 Redraw();
1987 }
1988 }
1989 }
1990 }
1991 }
1992 }
1993 if (Wrapping()) {
1994 SetScrollBars();
1995 }
1996 ThinRectangularRange();
1997 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1998 EnsureCaretVisible();
1999 // Avoid blinking during rapid typing:
2000 ShowCaretAtCurrentPosition();
2001 if ((caretSticky == CaretSticky::Off) ||
2002 ((caretSticky == CaretSticky::WhiteSpace) && !IsAllSpacesOrTabs(sv))) {
2003 SetLastXChosen();
2004 }
2005
2006 int ch = static_cast<unsigned char>(sv[0]);
2007 if (pdoc->dbcsCodePage != CpUtf8) {
2008 if (sv.length() > 1) {
2009 // DBCS code page or DBCS font character set.
2010 ch = (ch << 8) | static_cast<unsigned char>(sv[1]);
2011 }
2012 } else {
2013 if ((ch < 0xC0) || (1 == sv.length())) {
2014 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
2015 // characters when not in UTF-8 mode.
2016 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
2017 // characters representing themselves.
2018 } else {
2019 unsigned int utf32[1] = { 0 };
2020 UTF32FromUTF8(sv, utf32, std::size(utf32));
2021 ch = utf32[0];
2022 }
2023 }
2024 NotifyChar(ch, charSource);
2025
2026 if (recordingMacro && charSource != CharacterSource::TentativeInput) {
2027 std::string copy(sv); // ensure NUL-terminated
2028 NotifyMacroRecord(Message::ReplaceSel, 0, reinterpret_cast<sptr_t>(copy.data()));
2029 }
2030}
2031
2032void Editor::ClearBeforeTentativeStart() {
2033 // Make positions for the first composition string.
2034 FilterSelections();
2035 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
2036 for (size_t r = 0; r<sel.Count(); r++) {
2037 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
2038 sel.Range(r).End().Position())) {
2039 const Sci::Position positionInsert = sel.Range(r).Start().Position();
2040 if (!sel.Range(r).Empty()) {
2041 if (sel.Range(r).Length()) {
2042 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
2043 sel.Range(r).ClearVirtualSpace();
2044 } else {
2045 // Range is all virtual so collapse to start of virtual space
2046 sel.Range(r).MinimizeVirtualSpace();
2047 }
2048 }
2049 RealizeVirtualSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
2050 sel.Range(r).ClearVirtualSpace();
2051 }
2052 }
2053}
2054
2055void Editor::InsertPaste(const char *text, Sci::Position len) {
2056 if (multiPasteMode == MultiPaste::Once) {
2057 SelectionPosition selStart = sel.Start();
2058 selStart = RealizeVirtualSpace(selStart);
2059 const Sci::Position lengthInserted = pdoc->InsertString(selStart.Position(), text, len);
2060 if (lengthInserted > 0) {
2061 SetEmptySelection(selStart.Position() + lengthInserted);
2062 }
2063 } else {
2064 // MultiPaste::Each
2065 for (size_t r=0; r<sel.Count(); r++) {
2066 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
2067 sel.Range(r).End().Position())) {
2068 Sci::Position positionInsert = sel.Range(r).Start().Position();
2069 if (!sel.Range(r).Empty()) {
2070 if (sel.Range(r).Length()) {
2071 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
2072 sel.Range(r).ClearVirtualSpace();
2073 } else {
2074 // Range is all virtual so collapse to start of virtual space
2075 sel.Range(r).MinimizeVirtualSpace();
2076 }
2077 }
2078 positionInsert = RealizeVirtualSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
2079 const Sci::Position lengthInserted = pdoc->InsertString(positionInsert, text, len);
2080 if (lengthInserted > 0) {
2081 sel.Range(r).caret.SetPosition(positionInsert + lengthInserted);
2082 sel.Range(r).anchor.SetPosition(positionInsert + lengthInserted);
2083 }
2084 sel.Range(r).ClearVirtualSpace();
2085 }
2086 }
2087 }
2088}
2089
2090void Editor::InsertPasteShape(const char *text, Sci::Position len, PasteShape shape) {
2091 std::string convertedText;
2092 if (convertPastes) {
2093 // Convert line endings of the paste into our local line-endings mode
2094 convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
2095 len = convertedText.length();
2096 text = convertedText.c_str();
2097 }
2098 if (shape == PasteShape::rectangular) {
2099 PasteRectangular(sel.Start(), text, len);
2100 } else {
2101 if (shape == PasteShape::line) {
2102 const Sci::Position insertPos =
2103 pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
2104 Sci::Position lengthInserted = pdoc->InsertString(insertPos, text, len);
2105 // add the newline if necessary
2106 if ((len > 0) && (text[len - 1] != '\n' && text[len - 1] != '\r')) {
2107 const char *endline = StringFromEOLMode(pdoc->eolMode);
2108 const Sci::Position length = strlen(endline);
2109 lengthInserted += pdoc->InsertString(insertPos + lengthInserted, endline, length);
2110 }
2111 if (sel.MainCaret() == insertPos) {
2112 SetEmptySelection(sel.MainCaret() + lengthInserted);
2113 }
2114 } else {
2115 InsertPaste(text, len);
2116 }
2117 }
2118}
2119
2120void Editor::ClearSelection(bool retainMultipleSelections) {
2121 if (!sel.IsRectangular() && !retainMultipleSelections)
2122 FilterSelections();
2123 UndoGroup ug(pdoc);
2124 for (size_t r=0; r<sel.Count(); r++) {
2125 if (!sel.Range(r).Empty()) {
2126 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
2127 sel.Range(r).End().Position())) {
2128 pdoc->DeleteChars(sel.Range(r).Start().Position(),
2129 sel.Range(r).Length());
2130 sel.Range(r) = SelectionRange(sel.Range(r).Start());
2131 }
2132 }
2133 }
2134 ThinRectangularRange();
2135 sel.RemoveDuplicates();
2136 ClaimSelection();
2137 SetHoverIndicatorPosition(sel.MainCaret());
2138}
2139
2140void Editor::ClearAll() {
2141 {
2142 UndoGroup ug(pdoc);
2143 if (0 != pdoc->Length()) {
2144 pdoc->DeleteChars(0, pdoc->Length());
2145 }
2146 if (!pdoc->IsReadOnly()) {
2147 pcs->Clear();
2148 pdoc->AnnotationClearAll();
2149 pdoc->EOLAnnotationClearAll();
2150 pdoc->MarginClearAll();
2151 }
2152 }
2153
2154 view.ClearAllTabstops();
2155
2156 sel.Clear();
2157 SetTopLine(0);
2158 SetVerticalScrollPos();
2159 InvalidateStyleRedraw();
2160}
2161
2162void Editor::ClearDocumentStyle() {
2163 pdoc->decorations->DeleteLexerDecorations();
2164 pdoc->StartStyling(0);
2165 pdoc->SetStyleFor(pdoc->Length(), 0);
2166 pcs->ShowAll();
2167 SetAnnotationHeights(0, pdoc->LinesTotal());
2168 pdoc->ClearLevels();
2169}
2170
2171void Editor::CopyAllowLine() {
2172 SelectionText selectedText;
2173 CopySelectionRange(&selectedText, true);
2174 CopyToClipboard(selectedText);
2175}
2176
2177void Editor::Cut() {
2178 pdoc->CheckReadOnly();
2179 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
2180 Copy();
2181 ClearSelection();
2182 }
2183}
2184
2185void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, Sci::Position len) {
2186 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
2187 return;
2188 }
2189 sel.Clear();
2190 sel.RangeMain() = SelectionRange(pos);
2191 Sci::Line line = pdoc->SciLineFromPosition(sel.MainCaret());
2192 UndoGroup ug(pdoc);
2193 sel.RangeMain().caret = RealizeVirtualSpace(sel.RangeMain().caret);
2194 const int xInsert = XFromPosition(sel.RangeMain().caret);
2195 bool prevCr = false;
2196 while ((len > 0) && IsEOLCharacter(ptr[len-1]))
2197 len--;
2198 for (Sci::Position i = 0; i < len; i++) {
2199 if (IsEOLCharacter(ptr[i])) {
2200 if ((ptr[i] == '\r') || (!prevCr))
2201 line++;
2202 if (line >= pdoc->LinesTotal()) {
2203 if (pdoc->eolMode != EndOfLine::Lf)
2204 pdoc->InsertString(pdoc->Length(), "\r", 1);
2205 if (pdoc->eolMode != EndOfLine::Cr)
2206 pdoc->InsertString(pdoc->Length(), "\n", 1);
2207 }
2208 // Pad the end of lines with spaces if required
2209 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
2210 if ((XFromPosition(sel.RangeMain().caret) < xInsert) && (i + 1 < len)) {
2211 while (XFromPosition(sel.RangeMain().caret) < xInsert) {
2212 assert(pdoc);
2213 const Sci::Position lengthInserted = pdoc->InsertString(sel.MainCaret(), " ", 1);
2214 sel.RangeMain().caret.Add(lengthInserted);
2215 }
2216 }
2217 prevCr = ptr[i] == '\r';
2218 } else {
2219 const Sci::Position lengthInserted = pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
2220 sel.RangeMain().caret.Add(lengthInserted);
2221 prevCr = false;
2222 }
2223 }
2224 SetEmptySelection(pos);
2225}
2226
2227bool Editor::CanPaste() {
2228 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
2229}
2230
2231void Editor::Clear() {
2232 // If multiple selections, don't delete EOLS
2233 if (sel.Empty()) {
2234 bool singleVirtual = false;
2235 if ((sel.Count() == 1) &&
2236 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
2237 sel.RangeMain().Start().VirtualSpace()) {
2238 singleVirtual = true;
2239 }
2240 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
2241 for (size_t r=0; r<sel.Count(); r++) {
2242 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
2243 if (sel.Range(r).Start().VirtualSpace()) {
2244 if (sel.Range(r).anchor < sel.Range(r).caret)
2245 sel.Range(r) = SelectionRange(RealizeVirtualSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
2246 else
2247 sel.Range(r) = SelectionRange(RealizeVirtualSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
2248 }
2249 if ((sel.Count() == 1) || !pdoc->IsPositionInLineEnd(sel.Range(r).caret.Position())) {
2250 pdoc->DelChar(sel.Range(r).caret.Position());
2251 sel.Range(r).ClearVirtualSpace();
2252 } // else multiple selection so don't eat line ends
2253 } else {
2254 sel.Range(r).ClearVirtualSpace();
2255 }
2256 }
2257 } else {
2258 ClearSelection();
2259 }
2260 sel.RemoveDuplicates();
2261 ShowCaretAtCurrentPosition(); // Avoid blinking
2262}
2263
2264void Editor::SelectAll() {
2265 sel.Clear();
2266 SetSelection(0, pdoc->Length());
2267 Redraw();
2268}
2269
2270void Editor::Undo() {
2271 if (pdoc->CanUndo()) {
2272 InvalidateCaret();
2273 const Sci::Position newPos = pdoc->Undo();
2274 if (newPos >= 0)
2275 SetEmptySelection(newPos);
2276 EnsureCaretVisible();
2277 }
2278}
2279
2280void Editor::Redo() {
2281 if (pdoc->CanRedo()) {
2282 const Sci::Position newPos = pdoc->Redo();
2283 if (newPos >= 0)
2284 SetEmptySelection(newPos);
2285 EnsureCaretVisible();
2286 }
2287}
2288
2289void Editor::DelCharBack(bool allowLineStartDeletion) {
2290 RefreshStyleData();
2291 if (!sel.IsRectangular())
2292 FilterSelections();
2293 if (sel.IsRectangular())
2294 allowLineStartDeletion = false;
2295 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
2296 if (sel.Empty()) {
2297 for (size_t r=0; r<sel.Count(); r++) {
2298 if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
2299 if (sel.Range(r).caret.VirtualSpace()) {
2300 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
2301 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
2302 } else {
2303 const Sci::Line lineCurrentPos =
2304 pdoc->SciLineFromPosition(sel.Range(r).caret.Position());
2305 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
2306 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
2307 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
2308 UndoGroup ugInner(pdoc, !ug.Needed());
2309 const int indentation = pdoc->GetLineIndentation(lineCurrentPos);
2310 const int indentationStep = pdoc->IndentSize();
2311 int indentationChange = indentation % indentationStep;
2312 if (indentationChange == 0)
2313 indentationChange = indentationStep;
2314 const Sci::Position posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationChange);
2315 // SetEmptySelection
2316 sel.Range(r) = SelectionRange(posSelect);
2317 } else {
2318 pdoc->DelCharBack(sel.Range(r).caret.Position());
2319 }
2320 }
2321 }
2322 } else {
2323 sel.Range(r).ClearVirtualSpace();
2324 }
2325 }
2326 ThinRectangularRange();
2327 } else {
2328 ClearSelection();
2329 }
2330 sel.RemoveDuplicates();
2331 ContainerNeedsUpdate(Update::Selection);
2332 // Avoid blinking during rapid typing:
2333 ShowCaretAtCurrentPosition();
2334}
2335
2336KeyMod Editor::ModifierFlags(bool shift, bool ctrl, bool alt, bool meta, bool super) noexcept {
2337 return
2338 (shift ? KeyMod::Shift : KeyMod::Norm) |
2339 (ctrl ? KeyMod::Ctrl : KeyMod::Norm) |
2340 (alt ? KeyMod::Alt : KeyMod::Norm) |
2341 (meta ? KeyMod::Meta : KeyMod::Norm) |
2342 (super ? KeyMod::Super : KeyMod::Norm);
2343}
2344
2345void Editor::NotifyFocus(bool focus) {
2346 NotificationData scn = {};
2347 scn.nmhdr.code = focus ? Notification::FocusIn : Notification::FocusOut;
2348 NotifyParent(scn);
2349}
2350
2351void Editor::SetCtrlID(int identifier) {
2352 ctrlID = identifier;
2353}
2354
2355void Editor::NotifyStyleToNeeded(Sci::Position endStyleNeeded) {
2356 NotificationData scn = {};
2357 scn.nmhdr.code = Notification::StyleNeeded;
2358 scn.position = endStyleNeeded;
2359 NotifyParent(scn);
2360}
2361
2362void Editor::NotifyStyleNeeded(Document *, void *, Sci::Position endStyleNeeded) {
2363 NotifyStyleToNeeded(endStyleNeeded);
2364}
2365
2366void Editor::NotifyLexerChanged(Document *, void *) {
2367}
2368
2369void Editor::NotifyErrorOccurred(Document *, void *, Status status) {
2370 errorStatus = status;
2371}
2372
2373void Editor::NotifyChar(int ch, CharacterSource charSource) {
2374 NotificationData scn = {};
2375 scn.nmhdr.code = Notification::CharAdded;
2376 scn.ch = ch;
2377 scn.characterSource = charSource;
2378 NotifyParent(scn);
2379}
2380
2381void Editor::NotifySavePoint(bool isSavePoint) {
2382 NotificationData scn = {};
2383 if (isSavePoint) {
2384 scn.nmhdr.code = Notification::SavePointReached;
2385 } else {
2386 scn.nmhdr.code = Notification::SavePointLeft;
2387 }
2388 NotifyParent(scn);
2389}
2390
2391void Editor::NotifyModifyAttempt() {
2392 NotificationData scn = {};
2393 scn.nmhdr.code = Notification::ModifyAttemptRO;
2394 NotifyParent(scn);
2395}
2396
2397void Editor::NotifyDoubleClick(Point pt, KeyMod modifiers) {
2398 NotificationData scn = {};
2399 scn.nmhdr.code = Notification::DoubleClick;
2400 scn.line = LineFromLocation(pt);
2401 scn.position = PositionFromLocation(pt, true);
2402 scn.modifiers = modifiers;
2403 NotifyParent(scn);
2404}
2405
2406void Editor::NotifyHotSpotDoubleClicked(Sci::Position position, KeyMod modifiers) {
2407 NotificationData scn = {};
2408 scn.nmhdr.code = Notification::HotSpotDoubleClick;
2409 scn.position = position;
2410 scn.modifiers = modifiers;
2411 NotifyParent(scn);
2412}
2413
2414void Editor::NotifyHotSpotClicked(Sci::Position position, KeyMod modifiers) {
2415 NotificationData scn = {};
2416 scn.nmhdr.code = Notification::HotSpotClick;
2417 scn.position = position;
2418 scn.modifiers = modifiers;
2419 NotifyParent(scn);
2420}
2421
2422void Editor::NotifyHotSpotReleaseClick(Sci::Position position, KeyMod modifiers) {
2423 NotificationData scn = {};
2424 scn.nmhdr.code = Notification::HotSpotReleaseClick;
2425 scn.position = position;
2426 scn.modifiers = modifiers;
2427 NotifyParent(scn);
2428}
2429
2430bool Editor::NotifyUpdateUI() {
2431 if (needUpdateUI != Update::None) {
2432 NotificationData scn = {};
2433 scn.nmhdr.code = Notification::UpdateUI;
2434 scn.updated = needUpdateUI;
2435 NotifyParent(scn);
2436 needUpdateUI = Update::None;
2437 return true;
2438 }
2439 return false;
2440}
2441
2442void Editor::NotifyPainted() {
2443 NotificationData scn = {};
2444 scn.nmhdr.code = Notification::Painted;
2445 NotifyParent(scn);
2446}
2447
2448void Editor::NotifyIndicatorClick(bool click, Sci::Position position, KeyMod modifiers) {
2449 const int mask = pdoc->decorations->AllOnFor(position);
2450 if ((click && mask) || pdoc->decorations->ClickNotified()) {
2451 NotificationData scn = {};
2452 pdoc->decorations->SetClickNotified(click);
2453 scn.nmhdr.code = click ? Notification::IndicatorClick : Notification::IndicatorRelease;
2454 scn.modifiers = modifiers;
2455 scn.position = position;
2456 NotifyParent(scn);
2457 }
2458}
2459
2460bool Editor::NotifyMarginClick(Point pt, KeyMod modifiers) {
2461 const int marginClicked = vs.MarginFromLocation(pt);
2462 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
2463 const Sci::Position position = pdoc->LineStart(LineFromLocation(pt));
2464 if ((vs.ms[marginClicked].mask & MaskFolders) && (FlagSet(foldAutomatic, AutomaticFold::Click))) {
2465 const bool ctrl = FlagSet(modifiers, KeyMod::Ctrl);
2466 const bool shift = FlagSet(modifiers, KeyMod::Shift);
2467 const Sci::Line lineClick = pdoc->SciLineFromPosition(position);
2468 if (shift && ctrl) {
2469 FoldAll(FoldAction::Toggle);
2470 } else {
2471 const FoldLevel levelClick = pdoc->GetFoldLevel(lineClick);
2472 if (LevelIsHeader(levelClick)) {
2473 if (shift) {
2474 // Ensure all children visible
2475 FoldExpand(lineClick, FoldAction::Expand, levelClick);
2476 } else if (ctrl) {
2477 FoldExpand(lineClick, FoldAction::Toggle, levelClick);
2478 } else {
2479 // Toggle this line
2480 FoldLine(lineClick, FoldAction::Toggle);
2481 }
2482 }
2483 }
2484 return true;
2485 }
2486 NotificationData scn = {};
2487 scn.nmhdr.code = Notification::MarginClick;
2488 scn.modifiers = modifiers;
2489 scn.position = position;
2490 scn.margin = marginClicked;
2491 NotifyParent(scn);
2492 return true;
2493 } else {
2494 return false;
2495 }
2496}
2497
2498bool Editor::NotifyMarginRightClick(Point pt, KeyMod modifiers) {
2499 const int marginRightClicked = vs.MarginFromLocation(pt);
2500 if ((marginRightClicked >= 0) && vs.ms[marginRightClicked].sensitive) {
2501 const Sci::Position position = pdoc->LineStart(LineFromLocation(pt));
2502 NotificationData scn = {};
2503 scn.nmhdr.code = Notification::MarginRightClick;
2504 scn.modifiers = modifiers;
2505 scn.position = position;
2506 scn.margin = marginRightClicked;
2507 NotifyParent(scn);
2508 return true;
2509 } else {
2510 return false;
2511 }
2512}
2513
2514void Editor::NotifyNeedShown(Sci::Position pos, Sci::Position len) {
2515 NotificationData scn = {};
2516 scn.nmhdr.code = Notification::NeedShown;
2517 scn.position = pos;
2518 scn.length = len;
2519 NotifyParent(scn);
2520}
2521
2522void Editor::NotifyDwelling(Point pt, bool state) {
2523 NotificationData scn = {};
2524 scn.nmhdr.code = state ? Notification::DwellStart : Notification::DwellEnd;
2525 scn.position = PositionFromLocation(pt, true);
2526 scn.x = static_cast<int>(pt.x + vs.ExternalMarginWidth());
2527 scn.y = static_cast<int>(pt.y);
2528 NotifyParent(scn);
2529}
2530
2531void Editor::NotifyZoom() {
2532 NotificationData scn = {};
2533 scn.nmhdr.code = Notification::Zoom;
2534 NotifyParent(scn);
2535}
2536
2537// Notifications from document
2538void Editor::NotifyModifyAttempt(Document *, void *) {
2539 //Platform::DebugPrintf("** Modify Attempt\n");
2540 NotifyModifyAttempt();
2541}
2542
2543void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
2544 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
2545 NotifySavePoint(atSavePoint);
2546}
2547
2548void Editor::CheckModificationForWrap(DocModification mh) {
2549 if (FlagSet(mh.modificationType, ModificationFlags::InsertText | ModificationFlags::DeleteText)) {
2550 view.llc.Invalidate(LineLayout::ValidLevel::checkTextAndStyle);
2551 const Sci::Line lineDoc = pdoc->SciLineFromPosition(mh.position);
2552 const Sci::Line lines = std::max(static_cast<Sci::Line>(0), mh.linesAdded);
2553 if (Wrapping()) {
2554 NeedWrapping(lineDoc, lineDoc + lines + 1);
2555 }
2556 RefreshStyleData();
2557 // Fix up annotation heights
2558 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
2559 }
2560}
2561
2562namespace {
2563
2564// Move a position so it is still after the same character as before the insertion.
2565constexpr Sci::Position MovePositionForInsertion(Sci::Position position, Sci::Position startInsertion, Sci::Position length) noexcept {
2566 if (position > startInsertion) {
2567 return position + length;
2568 }
2569 return position;
2570}
2571
2572// Move a position so it is still after the same character as before the deletion if that
2573// character is still present else after the previous surviving character.
2574constexpr Sci::Position MovePositionForDeletion(Sci::Position position, Sci::Position startDeletion, Sci::Position length) noexcept {
2575 if (position > startDeletion) {
2576 const Sci::Position endDeletion = startDeletion + length;
2577 if (position > endDeletion) {
2578 return position - length;
2579 } else {
2580 return startDeletion;
2581 }
2582 } else {
2583 return position;
2584 }
2585}
2586
2587}
2588
2589void Editor::NotifyModified(Document *, DocModification mh, void *) {
2590 ContainerNeedsUpdate(Update::Content);
2591 if (paintState == PaintState::painting) {
2592 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
2593 }
2594 if (FlagSet(mh.modificationType, ModificationFlags::ChangeLineState)) {
2595 if (paintState == PaintState::painting) {
2596 CheckForChangeOutsidePaint(
2597 Range(pdoc->LineStart(mh.line),
2598 pdoc->LineStart(mh.line + 1)));
2599 } else {
2600 // Could check that change is before last visible line.
2601 Redraw();
2602 }
2603 }
2604 if (FlagSet(mh.modificationType, ModificationFlags::ChangeTabStops)) {
2605 Redraw();
2606 }
2607 if (FlagSet(mh.modificationType, ModificationFlags::LexerState)) {
2608 if (paintState == PaintState::painting) {
2609 CheckForChangeOutsidePaint(
2610 Range(mh.position, mh.position + mh.length));
2611 } else {
2612 Redraw();
2613 }
2614 }
2615 if (FlagSet(mh.modificationType, ModificationFlags::ChangeStyle | ModificationFlags::ChangeIndicator)) {
2616 if (FlagSet(mh.modificationType, ModificationFlags::ChangeStyle)) {
2617 pdoc->IncrementStyleClock();
2618 }
2619 if (paintState == PaintState::notPainting) {
2620 const Sci::Line lineDocTop = pcs->DocFromDisplay(topLine);
2621 if (mh.position < pdoc->LineStart(lineDocTop)) {
2622 // Styling performed before this view
2623 Redraw();
2624 } else {
2625 InvalidateRange(mh.position, mh.position + mh.length);
2626 }
2627 }
2628 if (FlagSet(mh.modificationType, ModificationFlags::ChangeStyle)) {
2629 view.llc.Invalidate(LineLayout::ValidLevel::checkTextAndStyle);
2630 }
2631 } else {
2632 // Move selection and brace highlights
2633 if (FlagSet(mh.modificationType, ModificationFlags::InsertText)) {
2634 sel.MovePositions(true, mh.position, mh.length);
2635 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
2636 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
2637 } else if (FlagSet(mh.modificationType, ModificationFlags::DeleteText)) {
2638 sel.MovePositions(false, mh.position, mh.length);
2639 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
2640 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
2641 }
2642 if (FlagSet(mh.modificationType, ModificationFlags::BeforeInsert | ModificationFlags::BeforeDelete) && pcs->HiddenLines()) {
2643 // Some lines are hidden so may need shown.
2644 const Sci::Line lineOfPos = pdoc->SciLineFromPosition(mh.position);
2645 Sci::Position endNeedShown = mh.position;
2646 if (FlagSet(mh.modificationType, ModificationFlags::BeforeInsert)) {
2647 if (pdoc->ContainsLineEnd(mh.text, mh.length) && (mh.position != pdoc->LineStart(lineOfPos)))
2648 endNeedShown = pdoc->LineStart(lineOfPos+1);
2649 } else if (FlagSet(mh.modificationType, ModificationFlags::BeforeDelete)) {
2650 // If the deletion includes any EOL then we extend the need shown area.
2651 endNeedShown = mh.position + mh.length;
2652 Sci::Line lineLast = pdoc->SciLineFromPosition(mh.position+mh.length);
2653 for (Sci::Line line = lineOfPos + 1; line <= lineLast; line++) {
2654 const Sci::Line lineMaxSubord = pdoc->GetLastChild(line, {}, -1);
2655 if (lineLast < lineMaxSubord) {
2656 lineLast = lineMaxSubord;
2657 endNeedShown = pdoc->LineEnd(lineLast);
2658 }
2659 }
2660 }
2661 NeedShown(mh.position, endNeedShown - mh.position);
2662 }
2663 if (mh.linesAdded != 0) {
2664 // Update contraction state for inserted and removed lines
2665 // lineOfPos should be calculated in context of state before modification, shouldn't it
2666 Sci::Line lineOfPos = pdoc->SciLineFromPosition(mh.position);
2667 if (mh.position > pdoc->LineStart(lineOfPos))
2668 lineOfPos++; // Affecting subsequent lines
2669 if (mh.linesAdded > 0) {
2670 pcs->InsertLines(lineOfPos, mh.linesAdded);
2671 } else {
2672 pcs->DeleteLines(lineOfPos, -mh.linesAdded);
2673 }
2674 view.LinesAddedOrRemoved(lineOfPos, mh.linesAdded);
2675 }
2676 if (FlagSet(mh.modificationType, ModificationFlags::ChangeAnnotation)) {
2677 const Sci::Line lineDoc = pdoc->SciLineFromPosition(mh.position);
2678 if (vs.annotationVisible != AnnotationVisible::Hidden) {
2679 if (pcs->SetHeight(lineDoc, pcs->GetHeight(lineDoc) + static_cast<int>(mh.annotationLinesAdded))) {
2680 SetScrollBars();
2681 }
2682 Redraw();
2683 }
2684 }
2685 if (FlagSet(mh.modificationType, ModificationFlags::ChangeEOLAnnotation)) {
2686 if (vs.eolAnnotationVisible != EOLAnnotationVisible::Hidden) {
2687 Redraw();
2688 }
2689 }
2690 CheckModificationForWrap(mh);
2691 if (mh.linesAdded != 0) {
2692 // Avoid scrolling of display if change before current display
2693 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
2694 const Sci::Line newTop = std::clamp<Sci::Line>(topLine + mh.linesAdded, 0, MaxScrollPos());
2695 if (newTop != topLine) {
2696 SetTopLine(newTop);
2697 SetVerticalScrollPos();
2698 }
2699 }
2700
2701 if (paintState == PaintState::notPainting && !CanDeferToLastStep(mh)) {
2702 if (SynchronousStylingToVisible()) {
2703 QueueIdleWork(WorkItems::style, pdoc->Length());
2704 }
2705 Redraw();
2706 }
2707 } else {
2708 if (paintState == PaintState::notPainting && mh.length && !CanEliminate(mh)) {
2709 if (SynchronousStylingToVisible()) {
2710 QueueIdleWork(WorkItems::style, mh.position + mh.length);
2711 }
2712 InvalidateRange(mh.position, mh.position + mh.length);
2713 }
2714 }
2715 }
2716
2717 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
2718 SetScrollBars();
2719 }
2720
2721 if ((FlagSet(mh.modificationType, ModificationFlags::ChangeMarker)) || (FlagSet(mh.modificationType, ModificationFlags::ChangeMargin))) {
2722 if ((!willRedrawAll) && ((paintState == PaintState::notPainting) || !PaintContainsMargin())) {
2723 if (FlagSet(mh.modificationType, ModificationFlags::ChangeFold)) {
2724 // Fold changes can affect the drawing of following lines so redraw whole margin
2725 RedrawSelMargin(marginView.highlightDelimiter.isEnabled ? -1 : mh.line - 1, true);
2726 } else {
2727 RedrawSelMargin(mh.line);
2728 }
2729 }
2730 }
2731 if ((FlagSet(mh.modificationType, ModificationFlags::ChangeFold)) && (FlagSet(foldAutomatic, AutomaticFold::Change))) {
2732 FoldChanged(mh.line, mh.foldLevelNow, mh.foldLevelPrev);
2733 }
2734
2735 // NOW pay the piper WRT "deferred" visual updates
2736 if (IsLastStep(mh)) {
2737 SetScrollBars();
2738 Redraw();
2739 }
2740
2741 // If client wants to see this modification
2742 if (FlagSet(mh.modificationType, modEventMask)) {
2743 if (commandEvents) {
2744 if ((mh.modificationType & (ModificationFlags::ChangeStyle | ModificationFlags::ChangeIndicator)) == ModificationFlags::None) {
2745 // Real modification made to text of document.
2746 NotifyChange(); // Send EN_CHANGE
2747 }
2748 }
2749
2750 NotificationData scn = {};
2751 scn.nmhdr.code = Notification::Modified;
2752 scn.position = mh.position;
2753 scn.modificationType = mh.modificationType;
2754 scn.text = mh.text;
2755 scn.length = mh.length;
2756 scn.linesAdded = mh.linesAdded;
2757 scn.line = mh.line;
2758 scn.foldLevelNow = mh.foldLevelNow;
2759 scn.foldLevelPrev = mh.foldLevelPrev;
2760 scn.token = static_cast<int>(mh.token);
2761 scn.annotationLinesAdded = mh.annotationLinesAdded;
2762 NotifyParent(scn);
2763 }
2764}
2765
2766void Editor::NotifyDeleted(Document *, void *) noexcept {
2767 /* Do nothing */
2768}
2769
2770void Editor::NotifyMacroRecord(Message iMessage, uptr_t wParam, sptr_t lParam) {
2771
2772 // Enumerates all macroable messages
2773 switch (iMessage) {
2774 case Message::Cut:
2775 case Message::Copy:
2776 case Message::Paste:
2777 case Message::Clear:
2778 case Message::ReplaceSel:
2779 case Message::AddText:
2780 case Message::InsertText:
2781 case Message::AppendText:
2782 case Message::ClearAll:
2783 case Message::SelectAll:
2784 case Message::GotoLine:
2785 case Message::GotoPos:
2786 case Message::SearchAnchor:
2787 case Message::SearchNext:
2788 case Message::SearchPrev:
2789 case Message::LineDown:
2790 case Message::LineDownExtend:
2791 case Message::ParaDown:
2792 case Message::ParaDownExtend:
2793 case Message::LineUp:
2794 case Message::LineUpExtend:
2795 case Message::ParaUp:
2796 case Message::ParaUpExtend:
2797 case Message::CharLeft:
2798 case Message::CharLeftExtend:
2799 case Message::CharRight:
2800 case Message::CharRightExtend:
2801 case Message::WordLeft:
2802 case Message::WordLeftExtend:
2803 case Message::WordRight:
2804 case Message::WordRightExtend:
2805 case Message::WordPartLeft:
2806 case Message::WordPartLeftExtend:
2807 case Message::WordPartRight:
2808 case Message::WordPartRightExtend:
2809 case Message::WordLeftEnd:
2810 case Message::WordLeftEndExtend:
2811 case Message::WordRightEnd:
2812 case Message::WordRightEndExtend:
2813 case Message::Home:
2814 case Message::HomeExtend:
2815 case Message::LineEnd:
2816 case Message::LineEndExtend:
2817 case Message::HomeWrap:
2818 case Message::HomeWrapExtend:
2819 case Message::LineEndWrap:
2820 case Message::LineEndWrapExtend:
2821 case Message::DocumentStart:
2822 case Message::DocumentStartExtend:
2823 case Message::DocumentEnd:
2824 case Message::DocumentEndExtend:
2825 case Message::StutteredPageUp:
2826 case Message::StutteredPageUpExtend:
2827 case Message::StutteredPageDown:
2828 case Message::StutteredPageDownExtend:
2829 case Message::PageUp:
2830 case Message::PageUpExtend:
2831 case Message::PageDown:
2832 case Message::PageDownExtend:
2833 case Message::EditToggleOvertype:
2834 case Message::Cancel:
2835 case Message::DeleteBack:
2836 case Message::Tab:
2837 case Message::BackTab:
2838 case Message::FormFeed:
2839 case Message::VCHome:
2840 case Message::VCHomeExtend:
2841 case Message::VCHomeWrap:
2842 case Message::VCHomeWrapExtend:
2843 case Message::VCHomeDisplay:
2844 case Message::VCHomeDisplayExtend:
2845 case Message::DelWordLeft:
2846 case Message::DelWordRight:
2847 case Message::DelWordRightEnd:
2848 case Message::DelLineLeft:
2849 case Message::DelLineRight:
2850 case Message::LineCopy:
2851 case Message::LineCut:
2852 case Message::LineDelete:
2853 case Message::LineTranspose:
2854 case Message::LineReverse:
2855 case Message::LineDuplicate:
2856 case Message::LowerCase:
2857 case Message::UpperCase:
2858 case Message::LineScrollDown:
2859 case Message::LineScrollUp:
2860 case Message::DeleteBackNotLine:
2861 case Message::HomeDisplay:
2862 case Message::HomeDisplayExtend:
2863 case Message::LineEndDisplay:
2864 case Message::LineEndDisplayExtend:
2865 case Message::SetSelectionMode:
2866 case Message::LineDownRectExtend:
2867 case Message::LineUpRectExtend:
2868 case Message::CharLeftRectExtend:
2869 case Message::CharRightRectExtend:
2870 case Message::HomeRectExtend:
2871 case Message::VCHomeRectExtend:
2872 case Message::LineEndRectExtend:
2873 case Message::PageUpRectExtend:
2874 case Message::PageDownRectExtend:
2875 case Message::SelectionDuplicate:
2876 case Message::CopyAllowLine:
2877 case Message::VerticalCentreCaret:
2878 case Message::MoveSelectedLinesUp:
2879 case Message::MoveSelectedLinesDown:
2880 case Message::ScrollToStart:
2881 case Message::ScrollToEnd:
2882 break;
2883
2884 // Filter out all others like display changes. Also, newlines are redundant
2885 // with char insert messages.
2886 case Message::NewLine:
2887 default:
2888 // printf("Filtered out %ld of macro recording\n", iMessage);
2889 return;
2890 }
2891
2892 // Send notification
2893 NotificationData scn = {};
2894 scn.nmhdr.code = Notification::MacroRecord;
2895 scn.message = iMessage;
2896 scn.wParam = wParam;
2897 scn.lParam = lParam;
2898 NotifyParent(scn);
2899}
2900
2901// Something has changed that the container should know about
2902void Editor::ContainerNeedsUpdate(Update flags) noexcept {
2903 needUpdateUI = needUpdateUI | flags;
2904}
2905
2906/**
2907 * Force scroll and keep position relative to top of window.
2908 *
2909 * If stuttered = true and not already at first/last row, move to first/last row of window.
2910 * If stuttered = true and already at first/last row, scroll as normal.
2911 */
2912void Editor::PageMove(int direction, Selection::SelTypes selt, bool stuttered) {
2913 Sci::Line topLineNew;
2914 SelectionPosition newPos;
2915
2916 const Sci::Line currentLine = pdoc->SciLineFromPosition(sel.MainCaret());
2917 const Sci::Line topStutterLine = topLine + caretPolicies.y.slop;
2918 const Sci::Line bottomStutterLine =
2919 pdoc->SciLineFromPosition(PositionFromLocation(
2920 Point::FromInts(lastXChosen - xOffset, direction * vs.lineHeight * static_cast<int>(LinesToScroll()))))
2921 - caretPolicies.y.slop - 1;
2922
2923 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
2924 topLineNew = topLine;
2925 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * caretPolicies.y.slop),
2926 false, false, UserVirtualSpace());
2927
2928 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
2929 topLineNew = topLine;
2930 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * static_cast<int>(LinesToScroll() - caretPolicies.y.slop)),
2931 false, false, UserVirtualSpace());
2932
2933 } else {
2934 const Point pt = LocationFromPosition(sel.MainCaret());
2935
2936 topLineNew = std::clamp<Sci::Line>(
2937 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
2938 newPos = SPositionFromLocation(
2939 Point::FromInts(lastXChosen - xOffset, static_cast<int>(pt.y) +
2940 direction * (vs.lineHeight * static_cast<int>(LinesToScroll()))),
2941 false, false, UserVirtualSpace());
2942 }
2943
2944 if (topLineNew != topLine) {
2945 SetTopLine(topLineNew);
2946 MovePositionTo(newPos, selt);
2947 Redraw();
2948 SetVerticalScrollPos();
2949 } else {
2950 MovePositionTo(newPos, selt);
2951 }
2952}
2953
2954void Editor::ChangeCaseOfSelection(CaseMapping caseMapping) {
2955 UndoGroup ug(pdoc);
2956 for (size_t r=0; r<sel.Count(); r++) {
2957 SelectionRange current = sel.Range(r);
2958 SelectionRange currentNoVS = current;
2959 currentNoVS.ClearVirtualSpace();
2960 const size_t rangeBytes = currentNoVS.Length();
2961 if (rangeBytes > 0) {
2962 std::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position());
2963
2964 std::string sMapped = CaseMapString(sText, caseMapping);
2965
2966 if (sMapped != sText) {
2967 size_t firstDifference = 0;
2968 while (sMapped[firstDifference] == sText[firstDifference])
2969 firstDifference++;
2970 size_t lastDifferenceText = sText.size() - 1;
2971 size_t lastDifferenceMapped = sMapped.size() - 1;
2972 while (sMapped[lastDifferenceMapped] == sText[lastDifferenceText]) {
2973 lastDifferenceText--;
2974 lastDifferenceMapped--;
2975 }
2976 const size_t endDifferenceText = sText.size() - 1 - lastDifferenceText;
2977 pdoc->DeleteChars(
2978 currentNoVS.Start().Position() + firstDifference,
2979 rangeBytes - firstDifference - endDifferenceText);
2980 const Sci::Position lengthChange = lastDifferenceMapped - firstDifference + 1;
2981 const Sci::Position lengthInserted = pdoc->InsertString(
2982 currentNoVS.Start().Position() + firstDifference,
2983 sMapped.c_str() + firstDifference,
2984 lengthChange);
2985 // Automatic movement changes selection so reset to exactly the same as it was.
2986 const Sci::Position diffSizes = sMapped.size() - sText.size() + lengthInserted - lengthChange;
2987 if (diffSizes != 0) {
2988 if (current.anchor > current.caret)
2989 current.anchor.Add(diffSizes);
2990 else
2991 current.caret.Add(diffSizes);
2992 }
2993 sel.Range(r) = current;
2994 }
2995 }
2996 }
2997}
2998
2999void Editor::LineTranspose() {
3000 const Sci::Line line = pdoc->SciLineFromPosition(sel.MainCaret());
3001 if (line > 0) {
3002 UndoGroup ug(pdoc);
3003
3004 const Sci::Position startPrevious = pdoc->LineStart(line - 1);
3005 const std::string linePrevious = RangeText(startPrevious, pdoc->LineEnd(line - 1));
3006
3007 Sci::Position startCurrent = pdoc->LineStart(line);
3008 const std::string lineCurrent = RangeText(startCurrent, pdoc->LineEnd(line));
3009
3010 pdoc->DeleteChars(startCurrent, lineCurrent.length());
3011 pdoc->DeleteChars(startPrevious, linePrevious.length());
3012 startCurrent -= linePrevious.length();
3013
3014 startCurrent += pdoc->InsertString(startPrevious, lineCurrent.c_str(),
3015 lineCurrent.length());
3016 pdoc->InsertString(startCurrent, linePrevious.c_str(),
3017 linePrevious.length());
3018 // Move caret to start of current line
3019 MovePositionTo(SelectionPosition(startCurrent));
3020 }
3021}
3022
3023void Editor::LineReverse() {
3024 const Sci::Line lineStart =
3025 pdoc->SciLineFromPosition(sel.RangeMain().Start().Position());
3026 const Sci::Line lineEnd =
3027 pdoc->SciLineFromPosition(sel.RangeMain().End().Position()-1);
3028 const Sci::Line lineDiff = lineEnd - lineStart;
3029 if (lineDiff <= 0)
3030 return;
3031 UndoGroup ug(pdoc);
3032 for (Sci::Line i=(lineDiff+1)/2-1; i>=0; --i) {
3033 const Sci::Line lineNum2 = lineEnd - i;
3034 const Sci::Line lineNum1 = lineStart + i;
3035 Sci::Position lineStart2 = pdoc->LineStart(lineNum2);
3036 const Sci::Position lineStart1 = pdoc->LineStart(lineNum1);
3037 const std::string line2 = RangeText(lineStart2, pdoc->LineEnd(lineNum2));
3038 const std::string line1 = RangeText(lineStart1, pdoc->LineEnd(lineNum1));
3039 const Sci::Position lineLen2 = line2.length();
3040 const Sci::Position lineLen1 = line1.length();
3041 pdoc->DeleteChars(lineStart2, lineLen2);
3042 pdoc->DeleteChars(lineStart1, lineLen1);
3043 lineStart2 -= lineLen1;
3044 pdoc->InsertString(lineStart2, line1.c_str(), lineLen1);
3045 pdoc->InsertString(lineStart1, line2.c_str(), lineLen2);
3046 }
3047 // Wholly select all affected lines
3048 sel.RangeMain() = SelectionRange(pdoc->LineStart(lineStart),
3049 pdoc->LineStart(lineEnd+1));
3050}
3051
3052void Editor::Duplicate(bool forLine) {
3053 if (sel.Empty()) {
3054 forLine = true;
3055 }
3056 UndoGroup ug(pdoc);
3057 const char *eol = "";
3058 Sci::Position eolLen = 0;
3059 if (forLine) {
3060 eol = StringFromEOLMode(pdoc->eolMode);
3061 eolLen = strlen(eol);
3062 }
3063 for (size_t r=0; r<sel.Count(); r++) {
3064 SelectionPosition start = sel.Range(r).Start();
3065 SelectionPosition end = sel.Range(r).End();
3066 if (forLine) {
3067 const Sci::Line line = pdoc->SciLineFromPosition(sel.Range(r).caret.Position());
3068 start = SelectionPosition(pdoc->LineStart(line));
3069 end = SelectionPosition(pdoc->LineEnd(line));
3070 }
3071 std::string text = RangeText(start.Position(), end.Position());
3072 Sci::Position lengthInserted = eolLen;
3073 if (forLine)
3074 lengthInserted = pdoc->InsertString(end.Position(), eol, eolLen);
3075 pdoc->InsertString(end.Position() + lengthInserted, text.c_str(), text.length());
3076 }
3077 if (sel.Count() && sel.IsRectangular()) {
3078 SelectionPosition last = sel.Last();
3079 if (forLine) {
3080 const Sci::Line line = pdoc->SciLineFromPosition(last.Position());
3081 last = SelectionPosition(last.Position() +
3082 pdoc->LineStart(line+1) - pdoc->LineStart(line));
3083 }
3084 if (sel.Rectangular().anchor > sel.Rectangular().caret)
3085 sel.Rectangular().anchor = last;
3086 else
3087 sel.Rectangular().caret = last;
3088 SetRectangularRange();
3089 }
3090}
3091
3092void Editor::CancelModes() {
3093 sel.SetMoveExtends(false);
3094}
3095
3096void Editor::NewLine() {
3097 InvalidateWholeSelection();
3098 if (sel.IsRectangular() || !additionalSelectionTyping) {
3099 // Remove non-main ranges
3100 sel.DropAdditionalRanges();
3101 }
3102
3103 UndoGroup ug(pdoc, !sel.Empty() || (sel.Count() > 1));
3104
3105 // Clear each range
3106 if (!sel.Empty()) {
3107 ClearSelection();
3108 }
3109
3110 // Insert each line end
3111 size_t countInsertions = 0;
3112 for (size_t r = 0; r < sel.Count(); r++) {
3113 sel.Range(r).ClearVirtualSpace();
3114 const char *eol = StringFromEOLMode(pdoc->eolMode);
3115 const Sci::Position positionInsert = sel.Range(r).caret.Position();
3116 const Sci::Position insertLength = pdoc->InsertString(positionInsert, eol, strlen(eol));
3117 if (insertLength > 0) {
3118 sel.Range(r) = SelectionRange(positionInsert + insertLength);
3119 countInsertions++;
3120 }
3121 }
3122
3123 // Perform notifications after all the changes as the application may change the
3124 // selections in response to the characters.
3125 for (size_t i = 0; i < countInsertions; i++) {
3126 const char *eol = StringFromEOLMode(pdoc->eolMode);
3127 while (*eol) {
3128 NotifyChar(*eol, CharacterSource::DirectInput);
3129 if (recordingMacro) {
3130 char txt[2];
3131 txt[0] = *eol;
3132 txt[1] = '\0';
3133 NotifyMacroRecord(Message::ReplaceSel, 0, reinterpret_cast<sptr_t>(txt));
3134 }
3135 eol++;
3136 }
3137 }
3138
3139 SetLastXChosen();
3140 SetScrollBars();
3141 EnsureCaretVisible();
3142 // Avoid blinking during rapid typing:
3143 ShowCaretAtCurrentPosition();
3144}
3145
3146SelectionPosition Editor::PositionUpOrDown(SelectionPosition spStart, int direction, int lastX) {
3147 const Point pt = LocationFromPosition(spStart);
3148 int skipLines = 0;
3149
3150 if (vs.annotationVisible != AnnotationVisible::Hidden) {
3151 const Sci::Line lineDoc = pdoc->SciLineFromPosition(spStart.Position());
3152 const Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
3153 const int subLine = static_cast<int>(pt.y - ptStartLine.y) / vs.lineHeight;
3154
3155 if (direction < 0 && subLine == 0) {
3156 const Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
3157 if (lineDisplay > 0) {
3158 skipLines = pdoc->AnnotationLines(pcs->DocFromDisplay(lineDisplay - 1));
3159 }
3160 } else if (direction > 0 && subLine >= (pcs->GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
3161 skipLines = pdoc->AnnotationLines(lineDoc);
3162 }
3163 }
3164
3165 const Sci::Line newY = static_cast<Sci::Line>(pt.y) + (1 + skipLines) * direction * vs.lineHeight;
3166 if (lastX < 0) {
3167 lastX = static_cast<int>(pt.x) + xOffset;
3168 }
3169 SelectionPosition posNew = SPositionFromLocation(
3170 Point::FromInts(lastX - xOffset, static_cast<int>(newY)), false, false, UserVirtualSpace());
3171
3172 if (direction < 0) {
3173 // Line wrapping may lead to a location on the same line, so
3174 // seek back if that is the case.
3175 Point ptNew = LocationFromPosition(posNew.Position());
3176 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
3177 posNew.Add(-1);
3178 posNew.SetVirtualSpace(0);
3179 ptNew = LocationFromPosition(posNew.Position());
3180 }
3181 } else if (direction > 0 && posNew.Position() != pdoc->Length()) {
3182 // There is an equivalent case when moving down which skips
3183 // over a line.
3184 Point ptNew = LocationFromPosition(posNew.Position());
3185 while ((posNew.Position() > spStart.Position()) && (ptNew.y > newY)) {
3186 posNew.Add(-1);
3187 posNew.SetVirtualSpace(0);
3188 ptNew = LocationFromPosition(posNew.Position());
3189 }
3190 }
3191 return posNew;
3192}
3193
3194void Editor::CursorUpOrDown(int direction, Selection::SelTypes selt) {
3195 if ((selt == Selection::SelTypes::none) && sel.MoveExtends()) {
3196 selt = !sel.IsRectangular() ? Selection::SelTypes::stream : Selection::SelTypes::rectangle;
3197 }
3198 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
3199 if (sel.IsRectangular()) {
3200 if (selt == Selection::SelTypes::none) {
3201 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
3202 } else {
3203 caretToUse = sel.Rectangular().caret;
3204 }
3205 }
3206 if (selt == Selection::SelTypes::rectangle) {
3207 const SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();
3208 if (!sel.IsRectangular()) {
3209 InvalidateWholeSelection();
3210 sel.DropAdditionalRanges();
3211 }
3212 const SelectionPosition posNew = MovePositionSoVisible(
3213 PositionUpOrDown(caretToUse, direction, lastXChosen), direction);
3214 sel.selType = Selection::SelTypes::rectangle;
3215 sel.Rectangular() = SelectionRange(posNew, rangeBase.anchor);
3216 SetRectangularRange();
3217 MovedCaret(posNew, caretToUse, true, caretPolicies);
3218 } else if (sel.selType == Selection::SelTypes::lines && sel.MoveExtends()) {
3219 // Calculate new caret position and call SetSelection(), which will ensure whole lines are selected.
3220 const SelectionPosition posNew = MovePositionSoVisible(
3221 PositionUpOrDown(caretToUse, direction, -1), direction);
3222 SetSelection(posNew, sel.Range(sel.Main()).anchor);
3223 } else {
3224 InvalidateWholeSelection();
3225 if (!additionalSelectionTyping || (sel.IsRectangular())) {
3226 sel.DropAdditionalRanges();
3227 }
3228 sel.selType = Selection::SelTypes::stream;
3229 for (size_t r = 0; r < sel.Count(); r++) {
3230 const int lastX = (r == sel.Main()) ? lastXChosen : -1;
3231 const SelectionPosition spCaretNow = sel.Range(r).caret;
3232 const SelectionPosition posNew = MovePositionSoVisible(
3233 PositionUpOrDown(spCaretNow, direction, lastX), direction);
3234 sel.Range(r) = selt == Selection::SelTypes::stream ?
3235 SelectionRange(posNew, sel.Range(r).anchor) : SelectionRange(posNew);
3236 }
3237 sel.RemoveDuplicates();
3238 MovedCaret(sel.RangeMain().caret, caretToUse, true, caretPolicies);
3239 }
3240}
3241
3242void Editor::ParaUpOrDown(int direction, Selection::SelTypes selt) {
3243 Sci::Line lineDoc;
3244 const Sci::Position savedPos = sel.MainCaret();
3245 do {
3246 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
3247 lineDoc = pdoc->SciLineFromPosition(sel.MainCaret());
3248 if (direction > 0) {
3249 if (sel.MainCaret() >= pdoc->Length() && !pcs->GetVisible(lineDoc)) {
3250 if (selt == Selection::SelTypes::none) {
3251 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
3252 }
3253 break;
3254 }
3255 }
3256 } while (!pcs->GetVisible(lineDoc));
3257}
3258
3259Range Editor::RangeDisplayLine(Sci::Line lineVisible) {
3260 RefreshStyleData();
3261 AutoSurface surface(this);
3262 return view.RangeDisplayLine(surface, *this, lineVisible, vs);
3263}
3264
3265Sci::Position Editor::StartEndDisplayLine(Sci::Position pos, bool start) {
3266 RefreshStyleData();
3267 AutoSurface surface(this);
3268 const Sci::Position posRet = view.StartEndDisplayLine(surface, *this, pos, start, vs);
3269 if (posRet == Sci::invalidPosition) {
3270 return pos;
3271 } else {
3272 return posRet;
3273 }
3274}
3275
3276namespace {
3277
3278constexpr short HighShortFromWParam(uptr_t x) {
3279 return static_cast<short>(x >> 16);
3280}
3281
3282constexpr short LowShortFromWParam(uptr_t x) {
3283 return static_cast<short>(x & 0xffff);
3284}
3285
3286constexpr Message WithExtends(Message iMessage) noexcept {
3287 switch (iMessage) {
3288 case Message::CharLeft: return Message::CharLeftExtend;
3289 case Message::CharRight: return Message::CharRightExtend;
3290
3291 case Message::WordLeft: return Message::WordLeftExtend;
3292 case Message::WordRight: return Message::WordRightExtend;
3293 case Message::WordLeftEnd: return Message::WordLeftEndExtend;
3294 case Message::WordRightEnd: return Message::WordRightEndExtend;
3295 case Message::WordPartLeft: return Message::WordPartLeftExtend;
3296 case Message::WordPartRight: return Message::WordPartRightExtend;
3297
3298 case Message::Home: return Message::HomeExtend;
3299 case Message::HomeDisplay: return Message::HomeDisplayExtend;
3300 case Message::HomeWrap: return Message::HomeWrapExtend;
3301 case Message::VCHome: return Message::VCHomeExtend;
3302 case Message::VCHomeDisplay: return Message::VCHomeDisplayExtend;
3303 case Message::VCHomeWrap: return Message::VCHomeWrapExtend;
3304
3305 case Message::LineEnd: return Message::LineEndExtend;
3306 case Message::LineEndDisplay: return Message::LineEndDisplayExtend;
3307 case Message::LineEndWrap: return Message::LineEndWrapExtend;
3308
3309 default: return iMessage;
3310 }
3311}
3312
3313constexpr int NaturalDirection(Message iMessage) noexcept {
3314 switch (iMessage) {
3315 case Message::CharLeft:
3316 case Message::CharLeftExtend:
3317 case Message::CharLeftRectExtend:
3318 case Message::WordLeft:
3319 case Message::WordLeftExtend:
3320 case Message::WordLeftEnd:
3321 case Message::WordLeftEndExtend:
3322 case Message::WordPartLeft:
3323 case Message::WordPartLeftExtend:
3324 case Message::Home:
3325 case Message::HomeExtend:
3326 case Message::HomeDisplay:
3327 case Message::HomeDisplayExtend:
3328 case Message::HomeWrap:
3329 case Message::HomeWrapExtend:
3330 // VC_HOME* mostly goes back
3331 case Message::VCHome:
3332 case Message::VCHomeExtend:
3333 case Message::VCHomeDisplay:
3334 case Message::VCHomeDisplayExtend:
3335 case Message::VCHomeWrap:
3336 case Message::VCHomeWrapExtend:
3337 return -1;
3338
3339 default:
3340 return 1;
3341 }
3342}
3343
3344constexpr bool IsRectExtend(Message iMessage, bool isRectMoveExtends) noexcept {
3345 switch (iMessage) {
3346 case Message::CharLeftRectExtend:
3347 case Message::CharRightRectExtend:
3348 case Message::HomeRectExtend:
3349 case Message::VCHomeRectExtend:
3350 case Message::LineEndRectExtend:
3351 return true;
3352 default:
3353 if (isRectMoveExtends) {
3354 // Handle Message::SetSelectionMode(SelectionMode::Rectangle) and subsequent movements.
3355 switch (iMessage) {
3356 case Message::CharLeftExtend:
3357 case Message::CharRightExtend:
3358 case Message::HomeExtend:
3359 case Message::VCHomeExtend:
3360 case Message::LineEndExtend:
3361 return true;
3362 default:
3363 return false;
3364 }
3365 }
3366 return false;
3367 }
3368}
3369
3370}
3371
3372Sci::Position Editor::VCHomeDisplayPosition(Sci::Position position) {
3373 const Sci::Position homePos = pdoc->VCHomePosition(position);
3374 const Sci::Position viewLineStart = StartEndDisplayLine(position, true);
3375 if (viewLineStart > homePos)
3376 return viewLineStart;
3377 else
3378 return homePos;
3379}
3380
3381Sci::Position Editor::VCHomeWrapPosition(Sci::Position position) {
3382 const Sci::Position homePos = pdoc->VCHomePosition(position);
3383 const Sci::Position viewLineStart = StartEndDisplayLine(position, true);
3384 if ((viewLineStart < position) && (viewLineStart > homePos))
3385 return viewLineStart;
3386 else
3387 return homePos;
3388}
3389
3390Sci::Position Editor::LineEndWrapPosition(Sci::Position position) {
3391 const Sci::Position endPos = StartEndDisplayLine(position, false);
3392 const Sci::Position realEndPos = pdoc->LineEndPosition(position);
3393 if (endPos > realEndPos // if moved past visible EOLs
3394 || position >= endPos) // if at end of display line already
3395 return realEndPos;
3396 else
3397 return endPos;
3398}
3399
3400int Editor::HorizontalMove(Message iMessage) {
3401 if (sel.selType == Selection::SelTypes::lines) {
3402 return 0; // horizontal moves with line selection have no effect
3403 }
3404 if (sel.MoveExtends()) {
3405 iMessage = WithExtends(iMessage);
3406 }
3407
3408 if (!multipleSelection && !sel.IsRectangular()) {
3409 // Simplify selection down to 1
3410 sel.SetSelection(sel.RangeMain());
3411 }
3412
3413 // Invalidate each of the current selections
3414 InvalidateWholeSelection();
3415
3416 if (IsRectExtend(iMessage, sel.IsRectangular() && sel.MoveExtends())) {
3417 const SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();
3418 if (!sel.IsRectangular()) {
3419 sel.DropAdditionalRanges();
3420 }
3421 // Will change to rectangular if not currently rectangular
3422 SelectionPosition spCaret = rangeBase.caret;
3423 switch (iMessage) {
3424 case Message::CharLeftRectExtend:
3425 case Message::CharLeftExtend: // only when sel.IsRectangular() && sel.MoveExtends()
3426 if (pdoc->IsLineEndPosition(spCaret.Position()) && spCaret.VirtualSpace()) {
3427 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3428 } else if (!FlagSet(virtualSpaceOptions, VirtualSpace::NoWrapLineStart) || pdoc->GetColumn(spCaret.Position()) > 0) {
3429 spCaret = SelectionPosition(spCaret.Position() - 1);
3430 }
3431 break;
3432 case Message::CharRightRectExtend:
3433 case Message::CharRightExtend: // only when sel.IsRectangular() && sel.MoveExtends()
3434 if (FlagSet(virtualSpaceOptions, VirtualSpace::RectangularSelection) && pdoc->IsLineEndPosition(sel.MainCaret())) {
3435 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3436 } else {
3437 spCaret = SelectionPosition(spCaret.Position() + 1);
3438 }
3439 break;
3440 case Message::HomeRectExtend:
3441 case Message::HomeExtend: // only when sel.IsRectangular() && sel.MoveExtends()
3442 spCaret = SelectionPosition(
3443 pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3444 break;
3445 case Message::VCHomeRectExtend:
3446 case Message::VCHomeExtend: // only when sel.IsRectangular() && sel.MoveExtends()
3447 spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
3448 break;
3449 case Message::LineEndRectExtend:
3450 case Message::LineEndExtend: // only when sel.IsRectangular() && sel.MoveExtends()
3451 spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3452 break;
3453 default:
3454 break;
3455 }
3456 const int directionMove = (spCaret < rangeBase.caret) ? -1 : 1;
3457 spCaret = MovePositionSoVisible(spCaret, directionMove);
3458 sel.selType = Selection::SelTypes::rectangle;
3459 sel.Rectangular() = SelectionRange(spCaret, rangeBase.anchor);
3460 SetRectangularRange();
3461 } else if (sel.IsRectangular()) {
3462 // Not a rectangular extension so switch to stream.
3463 SelectionPosition selAtLimit = (NaturalDirection(iMessage) > 0) ? sel.Limits().end : sel.Limits().start;
3464 switch (iMessage) {
3465 case Message::Home:
3466 selAtLimit = SelectionPosition(
3467 pdoc->LineStart(pdoc->LineFromPosition(selAtLimit.Position())));
3468 break;
3469 case Message::VCHome:
3470 selAtLimit = SelectionPosition(pdoc->VCHomePosition(selAtLimit.Position()));
3471 break;
3472 case Message::LineEnd:
3473 selAtLimit = SelectionPosition(pdoc->LineEndPosition(selAtLimit.Position()));
3474 break;
3475 default:
3476 break;
3477 }
3478 sel.selType = Selection::SelTypes::stream;
3479 sel.SetSelection(SelectionRange(selAtLimit));
3480 } else {
3481 if (!additionalSelectionTyping) {
3482 InvalidateWholeSelection();
3483 sel.DropAdditionalRanges();
3484 }
3485 for (size_t r = 0; r < sel.Count(); r++) {
3486 const SelectionPosition spCaretNow = sel.Range(r).caret;
3487 SelectionPosition spCaret = spCaretNow;
3488 switch (iMessage) {
3489 case Message::CharLeft:
3490 case Message::CharLeftExtend:
3491 if (spCaret.VirtualSpace()) {
3492 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3493 } else if (!FlagSet(virtualSpaceOptions, VirtualSpace::NoWrapLineStart) || pdoc->GetColumn(spCaret.Position()) > 0) {
3494 spCaret = SelectionPosition(spCaret.Position() - 1);
3495 }
3496 break;
3497 case Message::CharRight:
3498 case Message::CharRightExtend:
3499 if (FlagSet(virtualSpaceOptions, VirtualSpace::UserAccessible) && pdoc->IsLineEndPosition(spCaret.Position())) {
3500 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3501 } else {
3502 spCaret = SelectionPosition(spCaret.Position() + 1);
3503 }
3504 break;
3505 case Message::WordLeft:
3506 case Message::WordLeftExtend:
3507 spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), -1));
3508 break;
3509 case Message::WordRight:
3510 case Message::WordRightExtend:
3511 spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), 1));
3512 break;
3513 case Message::WordLeftEnd:
3514 case Message::WordLeftEndExtend:
3515 spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), -1));
3516 break;
3517 case Message::WordRightEnd:
3518 case Message::WordRightEndExtend:
3519 spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), 1));
3520 break;
3521 case Message::WordPartLeft:
3522 case Message::WordPartLeftExtend:
3523 spCaret = SelectionPosition(pdoc->WordPartLeft(spCaret.Position()));
3524 break;
3525 case Message::WordPartRight:
3526 case Message::WordPartRightExtend:
3527 spCaret = SelectionPosition(pdoc->WordPartRight(spCaret.Position()));
3528 break;
3529 case Message::Home:
3530 case Message::HomeExtend:
3531 spCaret = SelectionPosition(
3532 pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3533 break;
3534 case Message::HomeDisplay:
3535 case Message::HomeDisplayExtend:
3536 spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), true));
3537 break;
3538 case Message::HomeWrap:
3539 case Message::HomeWrapExtend:
3540 spCaret = MovePositionSoVisible(StartEndDisplayLine(spCaret.Position(), true), -1);
3541 if (spCaretNow <= spCaret)
3542 spCaret = SelectionPosition(
3543 pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3544 break;
3545 case Message::VCHome:
3546 case Message::VCHomeExtend:
3547 // VCHome alternates between beginning of line and beginning of text so may move back or forwards
3548 spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
3549 break;
3550 case Message::VCHomeDisplay:
3551 case Message::VCHomeDisplayExtend:
3552 spCaret = SelectionPosition(VCHomeDisplayPosition(spCaret.Position()));
3553 break;
3554 case Message::VCHomeWrap:
3555 case Message::VCHomeWrapExtend:
3556 spCaret = SelectionPosition(VCHomeWrapPosition(spCaret.Position()));
3557 break;
3558 case Message::LineEnd:
3559 case Message::LineEndExtend:
3560 spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3561 break;
3562 case Message::LineEndDisplay:
3563 case Message::LineEndDisplayExtend:
3564 spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), false));
3565 break;
3566 case Message::LineEndWrap:
3567 case Message::LineEndWrapExtend:
3568 spCaret = SelectionPosition(LineEndWrapPosition(spCaret.Position()));
3569 break;
3570
3571 default:
3572 PLATFORM_ASSERT(false);
3573 }
3574
3575 const int directionMove = (spCaret < spCaretNow) ? -1 : 1;
3576 spCaret = MovePositionSoVisible(spCaret, directionMove);
3577
3578 // Handle move versus extend, and special behaviour for non-empty left/right
3579 switch (iMessage) {
3580 case Message::CharLeft:
3581 case Message::CharRight:
3582 if (sel.Range(r).Empty()) {
3583 sel.Range(r) = SelectionRange(spCaret);
3584 } else {
3585 sel.Range(r) = SelectionRange(
3586 (iMessage == Message::CharLeft) ? sel.Range(r).Start() : sel.Range(r).End());
3587 }
3588 break;
3589
3590 case Message::WordLeft:
3591 case Message::WordRight:
3592 case Message::WordLeftEnd:
3593 case Message::WordRightEnd:
3594 case Message::WordPartLeft:
3595 case Message::WordPartRight:
3596 case Message::Home:
3597 case Message::HomeDisplay:
3598 case Message::HomeWrap:
3599 case Message::VCHome:
3600 case Message::VCHomeDisplay:
3601 case Message::VCHomeWrap:
3602 case Message::LineEnd:
3603 case Message::LineEndDisplay:
3604 case Message::LineEndWrap:
3605 sel.Range(r) = SelectionRange(spCaret);
3606 break;
3607
3608 case Message::CharLeftExtend:
3609 case Message::CharRightExtend:
3610 case Message::WordLeftExtend:
3611 case Message::WordRightExtend:
3612 case Message::WordLeftEndExtend:
3613 case Message::WordRightEndExtend:
3614 case Message::WordPartLeftExtend:
3615 case Message::WordPartRightExtend:
3616 case Message::HomeExtend:
3617 case Message::HomeDisplayExtend:
3618 case Message::HomeWrapExtend:
3619 case Message::VCHomeExtend:
3620 case Message::VCHomeDisplayExtend:
3621 case Message::VCHomeWrapExtend:
3622 case Message::LineEndExtend:
3623 case Message::LineEndDisplayExtend:
3624 case Message::LineEndWrapExtend: {
3625 SelectionRange rangeNew = SelectionRange(spCaret, sel.Range(r).anchor);
3626 sel.TrimOtherSelections(r, SelectionRange(rangeNew));
3627 sel.Range(r) = rangeNew;
3628 }
3629 break;
3630
3631 default:
3632 PLATFORM_ASSERT(false);
3633 }
3634 }
3635 }
3636
3637 sel.RemoveDuplicates();
3638
3639 MovedCaret(sel.RangeMain().caret, SelectionPosition(Sci::invalidPosition), true, caretPolicies);
3640
3641 // Invalidate the new state of the selection
3642 InvalidateWholeSelection();
3643
3644 SetLastXChosen();
3645 // Need the line moving and so forth from MovePositionTo
3646 return 0;
3647}
3648
3649int Editor::DelWordOrLine(Message iMessage) {
3650 // Virtual space may be realised for Message::DelWordRight or Message::DelWordRightEnd
3651 // which means 2 actions so wrap in an undo group.
3652
3653 // Rightwards and leftwards deletions differ in treatment of virtual space.
3654 // Clear virtual space for leftwards, realise for rightwards.
3655 const bool leftwards = (iMessage == Message::DelWordLeft) || (iMessage == Message::DelLineLeft);
3656
3657 if (!additionalSelectionTyping) {
3658 InvalidateWholeSelection();
3659 sel.DropAdditionalRanges();
3660 }
3661
3662 UndoGroup ug0(pdoc, (sel.Count() > 1) || !leftwards);
3663
3664 for (size_t r = 0; r < sel.Count(); r++) {
3665 if (leftwards) {
3666 // Delete to the left so first clear the virtual space.
3667 sel.Range(r).ClearVirtualSpace();
3668 } else {
3669 // Delete to the right so first realise the virtual space.
3670 sel.Range(r) = SelectionRange(
3671 RealizeVirtualSpace(sel.Range(r).caret));
3672 }
3673
3674 Range rangeDelete;
3675 switch (iMessage) {
3676 case Message::DelWordLeft:
3677 rangeDelete = Range(
3678 pdoc->NextWordStart(sel.Range(r).caret.Position(), -1),
3679 sel.Range(r).caret.Position());
3680 break;
3681 case Message::DelWordRight:
3682 rangeDelete = Range(
3683 sel.Range(r).caret.Position(),
3684 pdoc->NextWordStart(sel.Range(r).caret.Position(), 1));
3685 break;
3686 case Message::DelWordRightEnd:
3687 rangeDelete = Range(
3688 sel.Range(r).caret.Position(),
3689 pdoc->NextWordEnd(sel.Range(r).caret.Position(), 1));
3690 break;
3691 case Message::DelLineLeft:
3692 rangeDelete = Range(
3693 pdoc->LineStart(pdoc->LineFromPosition(sel.Range(r).caret.Position())),
3694 sel.Range(r).caret.Position());
3695 break;
3696 case Message::DelLineRight:
3697 rangeDelete = Range(
3698 sel.Range(r).caret.Position(),
3699 pdoc->LineEnd(pdoc->LineFromPosition(sel.Range(r).caret.Position())));
3700 break;
3701 default:
3702 break;
3703 }
3704 if (!RangeContainsProtected(rangeDelete.start, rangeDelete.end)) {
3705 pdoc->DeleteChars(rangeDelete.start, rangeDelete.end - rangeDelete.start);
3706 }
3707 }
3708
3709 // May need something stronger here: can selections overlap at this point?
3710 sel.RemoveDuplicates();
3711
3712 MovedCaret(sel.RangeMain().caret, SelectionPosition(Sci::invalidPosition), true, caretPolicies);
3713
3714 // Invalidate the new state of the selection
3715 InvalidateWholeSelection();
3716
3717 SetLastXChosen();
3718 return 0;
3719}
3720
3721int Editor::KeyCommand(Message iMessage) {
3722 switch (iMessage) {
3723 case Message::LineDown:
3724 CursorUpOrDown(1, Selection::SelTypes::none);
3725 break;
3726 case Message::LineDownExtend:
3727 CursorUpOrDown(1, Selection::SelTypes::stream);
3728 break;
3729 case Message::LineDownRectExtend:
3730 CursorUpOrDown(1, Selection::SelTypes::rectangle);
3731 break;
3732 case Message::ParaDown:
3733 ParaUpOrDown(1, Selection::SelTypes::none);
3734 break;
3735 case Message::ParaDownExtend:
3736 ParaUpOrDown(1, Selection::SelTypes::stream);
3737 break;
3738 case Message::LineScrollDown:
3739 ScrollTo(topLine + 1);
3740 MoveCaretInsideView(false);
3741 break;
3742 case Message::LineUp:
3743 CursorUpOrDown(-1, Selection::SelTypes::none);
3744 break;
3745 case Message::LineUpExtend:
3746 CursorUpOrDown(-1, Selection::SelTypes::stream);
3747 break;
3748 case Message::LineUpRectExtend:
3749 CursorUpOrDown(-1, Selection::SelTypes::rectangle);
3750 break;
3751 case Message::ParaUp:
3752 ParaUpOrDown(-1, Selection::SelTypes::none);
3753 break;
3754 case Message::ParaUpExtend:
3755 ParaUpOrDown(-1, Selection::SelTypes::stream);
3756 break;
3757 case Message::LineScrollUp:
3758 ScrollTo(topLine - 1);
3759 MoveCaretInsideView(false);
3760 break;
3761
3762 case Message::CharLeft:
3763 case Message::CharLeftExtend:
3764 case Message::CharLeftRectExtend:
3765 case Message::CharRight:
3766 case Message::CharRightExtend:
3767 case Message::CharRightRectExtend:
3768 case Message::WordLeft:
3769 case Message::WordLeftExtend:
3770 case Message::WordRight:
3771 case Message::WordRightExtend:
3772 case Message::WordLeftEnd:
3773 case Message::WordLeftEndExtend:
3774 case Message::WordRightEnd:
3775 case Message::WordRightEndExtend:
3776 case Message::WordPartLeft:
3777 case Message::WordPartLeftExtend:
3778 case Message::WordPartRight:
3779 case Message::WordPartRightExtend:
3780 case Message::Home:
3781 case Message::HomeExtend:
3782 case Message::HomeRectExtend:
3783 case Message::HomeDisplay:
3784 case Message::HomeDisplayExtend:
3785 case Message::HomeWrap:
3786 case Message::HomeWrapExtend:
3787 case Message::VCHome:
3788 case Message::VCHomeExtend:
3789 case Message::VCHomeRectExtend:
3790 case Message::VCHomeDisplay:
3791 case Message::VCHomeDisplayExtend:
3792 case Message::VCHomeWrap:
3793 case Message::VCHomeWrapExtend:
3794 case Message::LineEnd:
3795 case Message::LineEndExtend:
3796 case Message::LineEndRectExtend:
3797 case Message::LineEndDisplay:
3798 case Message::LineEndDisplayExtend:
3799 case Message::LineEndWrap:
3800 case Message::LineEndWrapExtend:
3801 return HorizontalMove(iMessage);
3802
3803 case Message::DocumentStart:
3804 MovePositionTo(0);
3805 SetLastXChosen();
3806 break;
3807 case Message::DocumentStartExtend:
3808 MovePositionTo(0, Selection::SelTypes::stream);
3809 SetLastXChosen();
3810 break;
3811 case Message::DocumentEnd:
3812 MovePositionTo(pdoc->Length());
3813 SetLastXChosen();
3814 break;
3815 case Message::DocumentEndExtend:
3816 MovePositionTo(pdoc->Length(), Selection::SelTypes::stream);
3817 SetLastXChosen();
3818 break;
3819 case Message::StutteredPageUp:
3820 PageMove(-1, Selection::SelTypes::none, true);
3821 break;
3822 case Message::StutteredPageUpExtend:
3823 PageMove(-1, Selection::SelTypes::stream, true);
3824 break;
3825 case Message::StutteredPageDown:
3826 PageMove(1, Selection::SelTypes::none, true);
3827 break;
3828 case Message::StutteredPageDownExtend:
3829 PageMove(1, Selection::SelTypes::stream, true);
3830 break;
3831 case Message::PageUp:
3832 PageMove(-1);
3833 break;
3834 case Message::PageUpExtend:
3835 PageMove(-1, Selection::SelTypes::stream);
3836 break;
3837 case Message::PageUpRectExtend:
3838 PageMove(-1, Selection::SelTypes::rectangle);
3839 break;
3840 case Message::PageDown:
3841 PageMove(1);
3842 break;
3843 case Message::PageDownExtend:
3844 PageMove(1, Selection::SelTypes::stream);
3845 break;
3846 case Message::PageDownRectExtend:
3847 PageMove(1, Selection::SelTypes::rectangle);
3848 break;
3849 case Message::EditToggleOvertype:
3850 inOverstrike = !inOverstrike;
3851 ContainerNeedsUpdate(Update::Selection);
3852 ShowCaretAtCurrentPosition();
3853 SetIdle(true);
3854 break;
3855 case Message::Cancel: // Cancel any modes - handled in subclass
3856 // Also unselect text
3857 CancelModes();
3858 if ((sel.Count() > 1) && !sel.IsRectangular()) {
3859 // Drop additional selections
3860 InvalidateWholeSelection();
3861 sel.DropAdditionalRanges();
3862 }
3863 break;
3864 case Message::DeleteBack:
3865 DelCharBack(true);
3866 if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {
3867 SetLastXChosen();
3868 }
3869 EnsureCaretVisible();
3870 break;
3871 case Message::DeleteBackNotLine:
3872 DelCharBack(false);
3873 if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {
3874 SetLastXChosen();
3875 }
3876 EnsureCaretVisible();
3877 break;
3878 case Message::Tab:
3879 Indent(true);
3880 if (caretSticky == CaretSticky::Off) {
3881 SetLastXChosen();
3882 }
3883 EnsureCaretVisible();
3884 ShowCaretAtCurrentPosition(); // Avoid blinking
3885 break;
3886 case Message::BackTab:
3887 Indent(false);
3888 if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {
3889 SetLastXChosen();
3890 }
3891 EnsureCaretVisible();
3892 ShowCaretAtCurrentPosition(); // Avoid blinking
3893 break;
3894 case Message::NewLine:
3895 NewLine();
3896 break;
3897 case Message::FormFeed:
3898 AddChar('\f');
3899 break;
3900 case Message::ZoomIn:
3901 if (vs.zoomLevel < 20) {
3902 vs.zoomLevel++;
3903 InvalidateStyleRedraw();
3904 NotifyZoom();
3905 }
3906 break;
3907 case Message::ZoomOut:
3908 if (vs.zoomLevel > -10) {
3909 vs.zoomLevel--;
3910 InvalidateStyleRedraw();
3911 NotifyZoom();
3912 }
3913 break;
3914
3915 case Message::DelWordLeft:
3916 case Message::DelWordRight:
3917 case Message::DelWordRightEnd:
3918 case Message::DelLineLeft:
3919 case Message::DelLineRight:
3920 return DelWordOrLine(iMessage);
3921
3922 case Message::LineCopy: {
3923 const Sci::Line lineStart = pdoc->SciLineFromPosition(SelectionStart().Position());
3924 const Sci::Line lineEnd = pdoc->SciLineFromPosition(SelectionEnd().Position());
3925 CopyRangeToClipboard(pdoc->LineStart(lineStart),
3926 pdoc->LineStart(lineEnd + 1));
3927 }
3928 break;
3929 case Message::LineCut: {
3930 const Sci::Line lineStart = pdoc->SciLineFromPosition(SelectionStart().Position());
3931 const Sci::Line lineEnd = pdoc->SciLineFromPosition(SelectionEnd().Position());
3932 const Sci::Position start = pdoc->LineStart(lineStart);
3933 const Sci::Position end = pdoc->LineStart(lineEnd + 1);
3934 SetSelection(start, end);
3935 Cut();
3936 SetLastXChosen();
3937 }
3938 break;
3939 case Message::LineDelete: {
3940 const Sci::Line line = pdoc->SciLineFromPosition(sel.MainCaret());
3941 const Sci::Position start = pdoc->LineStart(line);
3942 const Sci::Position end = pdoc->LineStart(line + 1);
3943 pdoc->DeleteChars(start, end - start);
3944 }
3945 break;
3946 case Message::LineTranspose:
3947 LineTranspose();
3948 break;
3949 case Message::LineReverse:
3950 LineReverse();
3951 break;
3952 case Message::LineDuplicate:
3953 Duplicate(true);
3954 break;
3955 case Message::SelectionDuplicate:
3956 Duplicate(false);
3957 break;
3958 case Message::LowerCase:
3959 ChangeCaseOfSelection(CaseMapping::lower);
3960 break;
3961 case Message::UpperCase:
3962 ChangeCaseOfSelection(CaseMapping::upper);
3963 break;
3964 case Message::ScrollToStart:
3965 ScrollTo(0);
3966 break;
3967 case Message::ScrollToEnd:
3968 ScrollTo(MaxScrollPos());
3969 break;
3970 default:
3971 break;
3972 }
3973 return 0;
3974}
3975
3976int Editor::KeyDefault(Keys, KeyMod) {
3977 return 0;
3978}
3979
3980int Editor::KeyDownWithModifiers(Keys key, KeyMod modifiers, bool *consumed) {
3981 DwellEnd(false);
3982 const Message msg = kmap.Find(key, modifiers);
3983 if (msg != static_cast<Message>(0)) {
3984 if (consumed)
3985 *consumed = true;
3986 return static_cast<int>(WndProc(msg, 0, 0));
3987 } else {
3988 if (consumed)
3989 *consumed = false;
3990 return KeyDefault(key, modifiers);
3991 }
3992}
3993
3994void Editor::Indent(bool forwards) {
3995 UndoGroup ug(pdoc);
3996 for (size_t r=0; r<sel.Count(); r++) {
3997 const Sci::Line lineOfAnchor =
3998 pdoc->SciLineFromPosition(sel.Range(r).anchor.Position());
3999 Sci::Position caretPosition = sel.Range(r).caret.Position();
4000 const Sci::Line lineCurrentPos = pdoc->SciLineFromPosition(caretPosition);
4001 if (lineOfAnchor == lineCurrentPos) {
4002 if (forwards) {
4003 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
4004 caretPosition = sel.Range(r).caret.Position();
4005 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
4006 pdoc->tabIndents) {
4007 const int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4008 const int indentationStep = pdoc->IndentSize();
4009 const Sci::Position posSelect = pdoc->SetLineIndentation(
4010 lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
4011 sel.Range(r) = SelectionRange(posSelect);
4012 } else {
4013 if (pdoc->useTabs) {
4014 const Sci::Position lengthInserted = pdoc->InsertString(caretPosition, "\t", 1);
4015 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
4016 } else {
4017 int numSpaces = (pdoc->tabInChars) -
4018 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
4019 if (numSpaces < 1)
4020 numSpaces = pdoc->tabInChars;
4021 const std::string spaceText(numSpaces, ' ');
4022 const Sci::Position lengthInserted = pdoc->InsertString(caretPosition, spaceText.c_str(),
4023 spaceText.length());
4024 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
4025 }
4026 }
4027 } else {
4028 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
4029 pdoc->tabIndents) {
4030 const int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4031 const int indentationStep = pdoc->IndentSize();
4032 const Sci::Position posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
4033 sel.Range(r) = SelectionRange(posSelect);
4034 } else {
4035 Sci::Position newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
4036 pdoc->tabInChars;
4037 if (newColumn < 0)
4038 newColumn = 0;
4039 Sci::Position newPos = caretPosition;
4040 while (pdoc->GetColumn(newPos) > newColumn)
4041 newPos--;
4042 sel.Range(r) = SelectionRange(newPos);
4043 }
4044 }
4045 } else { // Multiline
4046 const Sci::Position anchorPosOnLine = sel.Range(r).anchor.Position() -
4047 pdoc->LineStart(lineOfAnchor);
4048 const Sci::Position currentPosPosOnLine = caretPosition -
4049 pdoc->LineStart(lineCurrentPos);
4050 // Multiple lines selected so indent / dedent
4051 const Sci::Line lineTopSel = std::min(lineOfAnchor, lineCurrentPos);
4052 Sci::Line lineBottomSel = std::max(lineOfAnchor, lineCurrentPos);
4053 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
4054 lineBottomSel--; // If not selecting any characters on a line, do not indent
4055 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
4056 if (lineOfAnchor < lineCurrentPos) {
4057 if (currentPosPosOnLine == 0)
4058 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos),
4059 pdoc->LineStart(lineOfAnchor));
4060 else
4061 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1),
4062 pdoc->LineStart(lineOfAnchor));
4063 } else {
4064 if (anchorPosOnLine == 0)
4065 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos),
4066 pdoc->LineStart(lineOfAnchor));
4067 else
4068 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos),
4069 pdoc->LineStart(lineOfAnchor + 1));
4070 }
4071 }
4072 }
4073 ContainerNeedsUpdate(Update::Selection);
4074}
4075
4076class CaseFolderASCII : public CaseFolderTable {
4077public:
4078 CaseFolderASCII() noexcept {
4079 StandardASCII();
4080 }
4081};
4082
4083
4084std::unique_ptr<CaseFolder> Editor::CaseFolderForEncoding() {
4085 // Simple default that only maps ASCII upper case to lower case.
4086 return std::make_unique<CaseFolderASCII>();
4087}
4088
4089/**
4090 * Search of a text in the document, in the given range.
4091 * @return The position of the found text, -1 if not found.
4092 */
4093Sci::Position Editor::FindText(
4094 uptr_t wParam, ///< Search modes : @c FindOption::MatchCase, @c FindOption::WholeWord,
4095 ///< @c FindOption::WordStart, @c FindOption::RegExp or @c FindOption::Posix.
4096 sptr_t lParam) { ///< @c Sci_TextToFind structure: The text to search for in the given range.
4097
4098 TextToFind *ft = static_cast<TextToFind *>(PtrFromSPtr(lParam));
4099 Sci::Position lengthFound = strlen(ft->lpstrText);
4100 if (!pdoc->HasCaseFolder())
4101 pdoc->SetCaseFolder(CaseFolderForEncoding());
4102 try {
4103 const Sci::Position pos = pdoc->FindText(
4104 static_cast<Sci::Position>(ft->chrg.cpMin),
4105 static_cast<Sci::Position>(ft->chrg.cpMax),
4106 ft->lpstrText,
4107 static_cast<FindOption>(wParam),
4108 &lengthFound);
4109 if (pos != -1) {
4110 ft->chrgText.cpMin = static_cast<Sci_PositionCR>(pos);
4111 ft->chrgText.cpMax = static_cast<Sci_PositionCR>(pos + lengthFound);
4112 }
4113 return pos;
4114 } catch (RegexError &) {
4115 errorStatus = Status::RegEx;
4116 return -1;
4117 }
4118}
4119
4120/**
4121 * Relocatable search support : Searches relative to current selection
4122 * point and sets the selection to the found text range with
4123 * each search.
4124 */
4125/**
4126 * Anchor following searches at current selection start: This allows
4127 * multiple incremental interactive searches to be macro recorded
4128 * while still setting the selection to found text so the find/select
4129 * operation is self-contained.
4130 */
4131void Editor::SearchAnchor() {
4132 searchAnchor = SelectionStart().Position();
4133}
4134
4135/**
4136 * Find text from current search anchor: Must call @c SearchAnchor first.
4137 * Used for next text and previous text requests.
4138 * @return The position of the found text, -1 if not found.
4139 */
4140Sci::Position Editor::SearchText(
4141 Message iMessage, ///< Accepts both @c Message::SearchNext and @c Message::SearchPrev.
4142 uptr_t wParam, ///< Search modes : @c FindOption::MatchCase, @c FindOption::WholeWord,
4143 ///< @c FindOption::WordStart, @c FindOption::RegExp or @c FindOption::Posix.
4144 sptr_t lParam) { ///< The text to search for.
4145
4146 const char *txt = ConstCharPtrFromSPtr(lParam);
4147 Sci::Position pos = Sci::invalidPosition;
4148 Sci::Position lengthFound = strlen(txt);
4149 if (!pdoc->HasCaseFolder())
4150 pdoc->SetCaseFolder(CaseFolderForEncoding());
4151 try {
4152 if (iMessage == Message::SearchNext) {
4153 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
4154 static_cast<FindOption>(wParam),
4155 &lengthFound);
4156 } else {
4157 pos = pdoc->FindText(searchAnchor, 0, txt,
4158 static_cast<FindOption>(wParam),
4159 &lengthFound);
4160 }
4161 } catch (RegexError &) {
4162 errorStatus = Status::RegEx;
4163 return Sci::invalidPosition;
4164 }
4165 if (pos != Sci::invalidPosition) {
4166 SetSelection(pos, pos + lengthFound);
4167 }
4168
4169 return pos;
4170}
4171
4172std::string Editor::CaseMapString(const std::string &s, CaseMapping caseMapping) {
4173 std::string ret(s);
4174 for (char &ch : ret) {
4175 switch (caseMapping) {
4176 case CaseMapping::upper:
4177 ch = MakeUpperCase(ch);
4178 break;
4179 case CaseMapping::lower:
4180 ch = MakeLowerCase(ch);
4181 break;
4182 default: // no action
4183 break;
4184 }
4185 }
4186 return ret;
4187}
4188
4189/**
4190 * Search for text in the target range of the document.
4191 * @return The position of the found text, -1 if not found.
4192 */
4193Sci::Position Editor::SearchInTarget(const char *text, Sci::Position length) {
4194 Sci::Position lengthFound = length;
4195
4196 if (!pdoc->HasCaseFolder())
4197 pdoc->SetCaseFolder(CaseFolderForEncoding());
4198 try {
4199 const Sci::Position pos = pdoc->FindText(targetRange.start.Position(), targetRange.end.Position(), text,
4200 searchFlags,
4201 &lengthFound);
4202 if (pos != -1) {
4203 targetRange.start.SetPosition(pos);
4204 targetRange.end.SetPosition(pos + lengthFound);
4205 }
4206 return pos;
4207 } catch (RegexError &) {
4208 errorStatus = Status::RegEx;
4209 return -1;
4210 }
4211}
4212
4213void Editor::GoToLine(Sci::Line lineNo) {
4214 if (lineNo > pdoc->LinesTotal())
4215 lineNo = pdoc->LinesTotal();
4216 if (lineNo < 0)
4217 lineNo = 0;
4218 SetEmptySelection(pdoc->LineStart(lineNo));
4219 ShowCaretAtCurrentPosition();
4220 EnsureCaretVisible();
4221}
4222
4223static bool Close(Point pt1, Point pt2, Point threshold) noexcept {
4224 const Point ptDifference = pt2 - pt1;
4225 if (std::abs(ptDifference.x) > threshold.x)
4226 return false;
4227 if (std::abs(ptDifference.y) > threshold.y)
4228 return false;
4229 return true;
4230}
4231
4232std::string Editor::RangeText(Sci::Position start, Sci::Position end) const {
4233 if (start < end) {
4234 const Sci::Position len = end - start;
4235 std::string ret(len, '\0');
4236 pdoc->GetCharRange(ret.data(), start, len);
4237 return ret;
4238 }
4239 return std::string();
4240}
4241
4242void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
4243 if (sel.Empty()) {
4244 if (allowLineCopy) {
4245 const Sci::Line currentLine = pdoc->SciLineFromPosition(sel.MainCaret());
4246 const Sci::Position start = pdoc->LineStart(currentLine);
4247 const Sci::Position end = pdoc->LineEnd(currentLine);
4248
4249 std::string text = RangeText(start, end);
4250 if (pdoc->eolMode != EndOfLine::Lf)
4251 text.push_back('\r');
4252 if (pdoc->eolMode != EndOfLine::Cr)
4253 text.push_back('\n');
4254 ss->Copy(text, pdoc->dbcsCodePage,
4255 vs.styles[StyleDefault].characterSet, false, true);
4256 }
4257 } else {
4258 std::string text;
4259 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
4260 if (sel.selType == Selection::SelTypes::rectangle)
4261 std::sort(rangesInOrder.begin(), rangesInOrder.end());
4262 for (const SelectionRange &current : rangesInOrder) {
4263 text.append(RangeText(current.Start().Position(), current.End().Position()));
4264 if (sel.selType == Selection::SelTypes::rectangle) {
4265 if (pdoc->eolMode != EndOfLine::Lf)
4266 text.push_back('\r');
4267 if (pdoc->eolMode != EndOfLine::Cr)
4268 text.push_back('\n');
4269 }
4270 }
4271 ss->Copy(text, pdoc->dbcsCodePage,
4272 vs.styles[StyleDefault].characterSet, sel.IsRectangular(), sel.selType == Selection::SelTypes::lines);
4273 }
4274}
4275
4276void Editor::CopyRangeToClipboard(Sci::Position start, Sci::Position end) {
4277 start = pdoc->ClampPositionIntoDocument(start);
4278 end = pdoc->ClampPositionIntoDocument(end);
4279 SelectionText selectedText;
4280 std::string text = RangeText(start, end);
4281 selectedText.Copy(text,
4282 pdoc->dbcsCodePage, vs.styles[StyleDefault].characterSet, false, false);
4283 CopyToClipboard(selectedText);
4284}
4285
4286void Editor::CopyText(size_t length, const char *text) {
4287 SelectionText selectedText;
4288 selectedText.Copy(std::string(text, length),
4289 pdoc->dbcsCodePage, vs.styles[StyleDefault].characterSet, false, false);
4290 CopyToClipboard(selectedText);
4291}
4292
4293void Editor::SetDragPosition(SelectionPosition newPos) {
4294 if (newPos.Position() >= 0) {
4295 newPos = MovePositionOutsideChar(newPos, 1);
4296 posDrop = newPos;
4297 }
4298 if (!(posDrag == newPos)) {
4299 const CaretPolicies dragCaretPolicies = {
4300 CaretPolicySlop(CaretPolicy::Slop | CaretPolicy::Strict | CaretPolicy::Even, 50),
4301 CaretPolicySlop(CaretPolicy::Slop | CaretPolicy::Strict | CaretPolicy::Even, 2)
4302 };
4303 MovedCaret(newPos, posDrag, true, dragCaretPolicies);
4304
4305 caret.on = true;
4306 FineTickerCancel(TickReason::caret);
4307 if ((caret.active) && (caret.period > 0) && (newPos.Position() < 0))
4308 FineTickerStart(TickReason::caret, caret.period, caret.period/10);
4309 InvalidateCaret();
4310 posDrag = newPos;
4311 InvalidateCaret();
4312 }
4313}
4314
4315void Editor::DisplayCursor(Window::Cursor c) {
4316 if (cursorMode == CursorShape::Normal)
4317 wMain.SetCursor(c);
4318 else
4319 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
4320}
4321
4322bool Editor::DragThreshold(Point ptStart, Point ptNow) {
4323 const Point ptDiff = ptStart - ptNow;
4324 const XYPOSITION distanceSquared = ptDiff.x * ptDiff.x + ptDiff.y * ptDiff.y;
4325 return distanceSquared > 16.0f;
4326}
4327
4328void Editor::StartDrag() {
4329 // Always handled by subclasses
4330 //SetMouseCapture(true);
4331 //DisplayCursor(Windows::Cursor::Arrow);
4332}
4333
4334void Editor::DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular) {
4335 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
4336 if (inDragDrop == DragDrop::dragging)
4337 dropWentOutside = false;
4338
4339 const bool positionWasInSelection = PositionInSelection(position.Position());
4340
4341 const bool positionOnEdgeOfSelection =
4342 (position == SelectionStart()) || (position == SelectionEnd());
4343
4344 if ((inDragDrop != DragDrop::dragging) || !(positionWasInSelection) ||
4345 (positionOnEdgeOfSelection && !moving)) {
4346
4347 const SelectionPosition selStart = SelectionStart();
4348 const SelectionPosition selEnd = SelectionEnd();
4349
4350 UndoGroup ug(pdoc);
4351
4352 SelectionPosition positionAfterDeletion = position;
4353 if ((inDragDrop == DragDrop::dragging) && moving) {
4354 // Remove dragged out text
4355 if (rectangular || sel.selType == Selection::SelTypes::lines) {
4356 for (size_t r=0; r<sel.Count(); r++) {
4357 if (position >= sel.Range(r).Start()) {
4358 if (position > sel.Range(r).End()) {
4359 positionAfterDeletion.Add(-sel.Range(r).Length());
4360 } else {
4361 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
4362 }
4363 }
4364 }
4365 } else {
4366 if (position > selStart) {
4367 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
4368 }
4369 }
4370 ClearSelection();
4371 }
4372 position = positionAfterDeletion;
4373
4374 std::string convertedText = Document::TransformLineEnds(value, lengthValue, pdoc->eolMode);
4375
4376 if (rectangular) {
4377 PasteRectangular(position, convertedText.c_str(), convertedText.length());
4378 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
4379 SetEmptySelection(position);
4380 } else {
4381 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
4382 position = RealizeVirtualSpace(position);
4383 const Sci::Position lengthInserted = pdoc->InsertString(
4384 position.Position(), convertedText.c_str(), convertedText.length());
4385 if (lengthInserted > 0) {
4386 SelectionPosition posAfterInsertion = position;
4387 posAfterInsertion.Add(lengthInserted);
4388 SetSelection(posAfterInsertion, position);
4389 }
4390 }
4391 } else if (inDragDrop == DragDrop::dragging) {
4392 SetEmptySelection(position);
4393 }
4394}
4395
4396void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
4397 DropAt(position, value, strlen(value), moving, rectangular);
4398}
4399
4400/**
4401 * @return true if given position is inside the selection,
4402 */
4403bool Editor::PositionInSelection(Sci::Position pos) {
4404 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
4405 for (size_t r=0; r<sel.Count(); r++) {
4406 if (sel.Range(r).Contains(pos))
4407 return true;
4408 }
4409 return false;
4410}
4411
4412bool Editor::PointInSelection(Point pt) {
4413 const SelectionPosition pos = SPositionFromLocation(pt, false, true);
4414 const Point ptPos = LocationFromPosition(pos);
4415 for (size_t r=0; r<sel.Count(); r++) {
4416 const SelectionRange &range = sel.Range(r);
4417 if (range.Contains(pos)) {
4418 bool hit = true;
4419 if (pos == range.Start()) {
4420 // see if just before selection
4421 if (pt.x < ptPos.x) {
4422 hit = false;
4423 }
4424 }
4425 if (pos == range.End()) {
4426 // see if just after selection
4427 if (pt.x > ptPos.x) {
4428 hit = false;
4429 }
4430 }
4431 if (hit)
4432 return true;
4433 }
4434 }
4435 return false;
4436}
4437
4438bool Editor::PointInSelMargin(Point pt) const {
4439 // Really means: "Point in a margin"
4440 if (vs.fixedColumnWidth > 0) { // There is a margin
4441 PRectangle rcSelMargin = GetClientRectangle();
4442 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart - vs.leftMarginWidth);
4443 rcSelMargin.left = static_cast<XYPOSITION>(vs.textStart - vs.fixedColumnWidth);
4444 const Point ptOrigin = GetVisibleOriginInMain();
4445 rcSelMargin.Move(0, -ptOrigin.y);
4446 return rcSelMargin.ContainsWholePixel(pt);
4447 } else {
4448 return false;
4449 }
4450}
4451
4452Window::Cursor Editor::GetMarginCursor(Point pt) const noexcept {
4453 int x = 0;
4454 for (const MarginStyle &m : vs.ms) {
4455 if ((pt.x >= x) && (pt.x < x + m.width))
4456 return static_cast<Window::Cursor>(m.cursor);
4457 x += m.width;
4458 }
4459 return Window::Cursor::reverseArrow;
4460}
4461
4462void Editor::TrimAndSetSelection(Sci::Position currentPos_, Sci::Position anchor_) {
4463 sel.TrimSelection(SelectionRange(currentPos_, anchor_));
4464 SetSelection(currentPos_, anchor_);
4465}
4466
4467void Editor::LineSelection(Sci::Position lineCurrentPos_, Sci::Position lineAnchorPos_, bool wholeLine) {
4468 Sci::Position selCurrentPos;
4469 Sci::Position selAnchorPos;
4470 if (wholeLine) {
4471 const Sci::Line lineCurrent_ = pdoc->SciLineFromPosition(lineCurrentPos_);
4472 const Sci::Line lineAnchor_ = pdoc->SciLineFromPosition(lineAnchorPos_);
4473 if (lineAnchorPos_ < lineCurrentPos_) {
4474 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
4475 selAnchorPos = pdoc->LineStart(lineAnchor_);
4476 } else if (lineAnchorPos_ > lineCurrentPos_) {
4477 selCurrentPos = pdoc->LineStart(lineCurrent_);
4478 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
4479 } else { // Same line, select it
4480 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
4481 selAnchorPos = pdoc->LineStart(lineAnchor_);
4482 }
4483 } else {
4484 if (lineAnchorPos_ < lineCurrentPos_) {
4485 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
4486 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4487 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4488 } else if (lineAnchorPos_ > lineCurrentPos_) {
4489 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
4490 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4491 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
4492 } else { // Same line, select it
4493 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4494 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4495 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4496 }
4497 }
4498 TrimAndSetSelection(selCurrentPos, selAnchorPos);
4499}
4500
4501void Editor::WordSelection(Sci::Position pos) {
4502 if (pos < wordSelectAnchorStartPos) {
4503 // Extend backward to the word containing pos.
4504 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
4505 // This ensures that a series of empty lines isn't counted as a single "word".
4506 if (!pdoc->IsLineEndPosition(pos))
4507 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
4508 TrimAndSetSelection(pos, wordSelectAnchorEndPos);
4509 } else if (pos > wordSelectAnchorEndPos) {
4510 // Extend forward to the word containing the character to the left of pos.
4511 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
4512 // This ensures that a series of empty lines isn't counted as a single "word".
4513 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
4514 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
4515 TrimAndSetSelection(pos, wordSelectAnchorStartPos);
4516 } else {
4517 // Select only the anchored word
4518 if (pos >= originalAnchorPos)
4519 TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
4520 else
4521 TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
4522 }
4523}
4524
4525void Editor::DwellEnd(bool mouseMoved) {
4526 if (mouseMoved)
4527 ticksToDwell = dwellDelay;
4528 else
4529 ticksToDwell = TimeForever;
4530 if (dwelling && (dwellDelay < TimeForever)) {
4531 dwelling = false;
4532 NotifyDwelling(ptMouseLast, dwelling);
4533 }
4534 FineTickerCancel(TickReason::dwell);
4535}
4536
4537void Editor::MouseLeave() {
4538 SetHotSpotRange(nullptr);
4539 SetHoverIndicatorPosition(Sci::invalidPosition);
4540 if (!HaveMouseCapture()) {
4541 ptMouseLast = Point(-1, -1);
4542 DwellEnd(true);
4543 }
4544}
4545
4546static constexpr bool AllowVirtualSpace(VirtualSpace virtualSpaceOptions, bool rectangular) noexcept {
4547 return (!rectangular && (FlagSet(virtualSpaceOptions, VirtualSpace::UserAccessible)))
4548 || (rectangular && (FlagSet(virtualSpaceOptions, VirtualSpace::RectangularSelection)));
4549}
4550
4551void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, KeyMod modifiers) {
4552 SetHoverIndicatorPoint(pt);
4553 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
4554 ptMouseLast = pt;
4555 const bool ctrl = FlagSet(modifiers, KeyMod::Ctrl);
4556 const bool shift = FlagSet(modifiers, KeyMod::Shift);
4557 const bool alt = FlagSet(modifiers, KeyMod::Alt);
4558 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
4559 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4560 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4561 newCharPos = MovePositionOutsideChar(newCharPos, -1);
4562 inDragDrop = DragDrop::none;
4563 sel.SetMoveExtends(false);
4564
4565 if (NotifyMarginClick(pt, modifiers))
4566 return;
4567
4568 NotifyIndicatorClick(true, newPos.Position(), modifiers);
4569
4570 const bool inSelMargin = PointInSelMargin(pt);
4571 // In margin ctrl+(double)click should always select everything
4572 if (ctrl && inSelMargin) {
4573 SelectAll();
4574 lastClickTime = curTime;
4575 lastClick = pt;
4576 return;
4577 }
4578 if (shift && !inSelMargin) {
4579 SetSelection(newPos);
4580 }
4581 if ((curTime < (lastClickTime+Platform::DoubleClickTime())) && Close(pt, lastClick, doubleClickCloseThreshold)) {
4582 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
4583 SetMouseCapture(true);
4584 FineTickerStart(TickReason::scroll, 100, 10);
4585 if (!ctrl || !multipleSelection || (selectionUnit != TextUnit::character && selectionUnit != TextUnit::word))
4586 SetEmptySelection(newPos.Position());
4587 bool doubleClick = false;
4588 if (inSelMargin) {
4589 // Inside margin selection type should be either subLine or wholeLine.
4590 if (selectionUnit == TextUnit::subLine) {
4591 // If it is subLine, we're inside a *double* click and word wrap is enabled,
4592 // so we switch to wholeLine in order to select whole line.
4593 selectionUnit = TextUnit::wholeLine;
4594 } else if (selectionUnit != TextUnit::subLine && selectionUnit != TextUnit::wholeLine) {
4595 // If it is neither, reset selection type to line selection.
4596 selectionUnit = (Wrapping() && (FlagSet(marginOptions, MarginOption::SubLineSelect))) ? TextUnit::subLine : TextUnit::wholeLine;
4597 }
4598 } else {
4599 if (selectionUnit == TextUnit::character) {
4600 selectionUnit = TextUnit::word;
4601 doubleClick = true;
4602 } else if (selectionUnit == TextUnit::word) {
4603 // Since we ended up here, we're inside a *triple* click, which should always select
4604 // whole line regardless of word wrap being enabled or not.
4605 selectionUnit = TextUnit::wholeLine;
4606 } else {
4607 selectionUnit = TextUnit::character;
4608 originalAnchorPos = sel.MainCaret();
4609 }
4610 }
4611
4612 if (selectionUnit == TextUnit::word) {
4613 Sci::Position charPos = originalAnchorPos;
4614 if (sel.MainCaret() == originalAnchorPos) {
4615 charPos = PositionFromLocation(pt, false, true);
4616 charPos = MovePositionOutsideChar(charPos, -1);
4617 }
4618
4619 Sci::Position startWord;
4620 Sci::Position endWord;
4621 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
4622 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
4623 endWord = pdoc->ExtendWordSelect(charPos, 1);
4624 } else {
4625 // Selecting backwards, or anchor beyond last character on line. In these cases,
4626 // we select the word containing the character to the *left* of the anchor.
4627 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
4628 startWord = pdoc->ExtendWordSelect(charPos, -1);
4629 endWord = pdoc->ExtendWordSelect(startWord, 1);
4630 } else {
4631 // Anchor at start of line; select nothing to begin with.
4632 startWord = charPos;
4633 endWord = charPos;
4634 }
4635 }
4636
4637 wordSelectAnchorStartPos = startWord;
4638 wordSelectAnchorEndPos = endWord;
4639 wordSelectInitialCaretPos = sel.MainCaret();
4640 WordSelection(wordSelectInitialCaretPos);
4641 } else if (selectionUnit == TextUnit::subLine || selectionUnit == TextUnit::wholeLine) {
4642 lineAnchorPos = newPos.Position();
4643 LineSelection(lineAnchorPos, lineAnchorPos, selectionUnit == TextUnit::wholeLine);
4644 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
4645 } else {
4646 SetEmptySelection(sel.MainCaret());
4647 }
4648 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
4649 if (doubleClick) {
4650 NotifyDoubleClick(pt, modifiers);
4651 if (PositionIsHotspot(newCharPos.Position()))
4652 NotifyHotSpotDoubleClicked(newCharPos.Position(), modifiers);
4653 }
4654 } else { // Single click
4655 if (inSelMargin) {
4656 if (sel.IsRectangular() || (sel.Count() > 1)) {
4657 InvalidateWholeSelection();
4658 sel.Clear();
4659 }
4660 sel.selType = Selection::SelTypes::stream;
4661 if (!shift) {
4662 // Single click in margin: select wholeLine or only subLine if word wrap is enabled
4663 lineAnchorPos = newPos.Position();
4664 selectionUnit = (Wrapping() && (FlagSet(marginOptions, MarginOption::SubLineSelect))) ? TextUnit::subLine : TextUnit::wholeLine;
4665 LineSelection(lineAnchorPos, lineAnchorPos, selectionUnit == TextUnit::wholeLine);
4666 } else {
4667 // Single shift+click in margin: select from line anchor to clicked line
4668 if (sel.MainAnchor() > sel.MainCaret())
4669 lineAnchorPos = sel.MainAnchor() - 1;
4670 else
4671 lineAnchorPos = sel.MainAnchor();
4672 // Reset selection type if there is an empty selection.
4673 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
4674 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
4675 // This ensures that we continue selecting in the same selection mode.
4676 if (sel.Empty() || (selectionUnit != TextUnit::subLine && selectionUnit != TextUnit::wholeLine))
4677 selectionUnit = (Wrapping() && (FlagSet(marginOptions, MarginOption::SubLineSelect))) ? TextUnit::subLine : TextUnit::wholeLine;
4678 LineSelection(newPos.Position(), lineAnchorPos, selectionUnit == TextUnit::wholeLine);
4679 }
4680
4681 SetDragPosition(SelectionPosition(Sci::invalidPosition));
4682 SetMouseCapture(true);
4683 FineTickerStart(TickReason::scroll, 100, 10);
4684 } else {
4685 if (PointIsHotspot(pt)) {
4686 NotifyHotSpotClicked(newCharPos.Position(), modifiers);
4687 hotSpotClickPos = newCharPos.Position();
4688 }
4689 if (!shift) {
4690 if (PointInSelection(pt) && !SelectionEmpty())
4691 inDragDrop = DragDrop::initial;
4692 else
4693 inDragDrop = DragDrop::none;
4694 }
4695 SetMouseCapture(true);
4696 FineTickerStart(TickReason::scroll, 100, 10);
4697 if (inDragDrop != DragDrop::initial) {
4698 SetDragPosition(SelectionPosition(Sci::invalidPosition));
4699 if (!shift) {
4700 if (ctrl && multipleSelection) {
4701 const SelectionRange range(newPos);
4702 sel.TentativeSelection(range);
4703 InvalidateSelection(range, true);
4704 } else {
4705 InvalidateSelection(SelectionRange(newPos), true);
4706 if (sel.Count() > 1)
4707 Redraw();
4708 if ((sel.Count() > 1) || (sel.selType != Selection::SelTypes::stream))
4709 sel.Clear();
4710 sel.selType = alt ? Selection::SelTypes::rectangle : Selection::SelTypes::stream;
4711 SetSelection(newPos, newPos);
4712 }
4713 }
4714 SelectionPosition anchorCurrent = newPos;
4715 if (shift)
4716 anchorCurrent = sel.IsRectangular() ?
4717 sel.Rectangular().anchor : sel.RangeMain().anchor;
4718 sel.selType = alt ? Selection::SelTypes::rectangle : Selection::SelTypes::stream;
4719 selectionUnit = TextUnit::character;
4720 originalAnchorPos = sel.MainCaret();
4721 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
4722 SetRectangularRange();
4723 }
4724 }
4725 }
4726 lastClickTime = curTime;
4727 lastClick = pt;
4728 lastXChosen = static_cast<int>(pt.x) + xOffset;
4729 ShowCaretAtCurrentPosition();
4730}
4731
4732void Editor::RightButtonDownWithModifiers(Point pt, unsigned int, KeyMod modifiers) {
4733 if (NotifyMarginRightClick(pt, modifiers))
4734 return;
4735}
4736
4737bool Editor::PositionIsHotspot(Sci::Position position) const {
4738 return vs.styles[pdoc->StyleIndexAt(position)].hotspot;
4739}
4740
4741bool Editor::PointIsHotspot(Point pt) {
4742 const Sci::Position pos = PositionFromLocation(pt, true, true);
4743 if (pos == Sci::invalidPosition)
4744 return false;
4745 return PositionIsHotspot(pos);
4746}
4747
4748void Editor::SetHoverIndicatorPosition(Sci::Position position) {
4749 const Sci::Position hoverIndicatorPosPrev = hoverIndicatorPos;
4750 hoverIndicatorPos = Sci::invalidPosition;
4751 if (!vs.indicatorsDynamic)
4752 return;
4753 if (position != Sci::invalidPosition) {
4754 for (const IDecoration *deco : pdoc->decorations->View()) {
4755 if (vs.indicators[deco->Indicator()].IsDynamic()) {
4756 if (pdoc->decorations->ValueAt(deco->Indicator(), position)) {
4757 hoverIndicatorPos = position;
4758 }
4759 }
4760 }
4761 }
4762 if (hoverIndicatorPosPrev != hoverIndicatorPos) {
4763 Redraw();
4764 }
4765}
4766
4767void Editor::SetHoverIndicatorPoint(Point pt) {
4768 if (!vs.indicatorsDynamic) {
4769 SetHoverIndicatorPosition(Sci::invalidPosition);
4770 } else {
4771 SetHoverIndicatorPosition(PositionFromLocation(pt, true, true));
4772 }
4773}
4774
4775void Editor::SetHotSpotRange(const Point *pt) {
4776 if (pt) {
4777 const Sci::Position pos = PositionFromLocation(*pt, false, true);
4778
4779 // If we don't limit this to word characters then the
4780 // range can encompass more than the run range and then
4781 // the underline will not be drawn properly.
4782 Range hsNew;
4783 hsNew.start = pdoc->ExtendStyleRange(pos, -1, hotspotSingleLine);
4784 hsNew.end = pdoc->ExtendStyleRange(pos, 1, hotspotSingleLine);
4785
4786 // Only invalidate the range if the hotspot range has changed...
4787 if (!(hsNew == hotspot)) {
4788 if (hotspot.Valid()) {
4789 InvalidateRange(hotspot.start, hotspot.end);
4790 }
4791 hotspot = hsNew;
4792 InvalidateRange(hotspot.start, hotspot.end);
4793 }
4794 } else {
4795 if (hotspot.Valid()) {
4796 InvalidateRange(hotspot.start, hotspot.end);
4797 }
4798 hotspot = Range(Sci::invalidPosition);
4799 }
4800}
4801
4802void Editor::ButtonMoveWithModifiers(Point pt, unsigned int, KeyMod modifiers) {
4803 if (ptMouseLast != pt) {
4804 DwellEnd(true);
4805 }
4806
4807 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
4808 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4809 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
4810
4811 if (inDragDrop == DragDrop::initial) {
4812 if (DragThreshold(ptMouseLast, pt)) {
4813 SetMouseCapture(false);
4814 FineTickerCancel(TickReason::scroll);
4815 SetDragPosition(movePos);
4816 CopySelectionRange(&drag);
4817 StartDrag();
4818 }
4819 return;
4820 }
4821
4822 ptMouseLast = pt;
4823 PRectangle rcClient = GetClientRectangle();
4824 const Point ptOrigin = GetVisibleOriginInMain();
4825 rcClient.Move(0, -ptOrigin.y);
4826 if ((dwellDelay < TimeForever) && rcClient.Contains(pt)) {
4827 FineTickerStart(TickReason::dwell, dwellDelay, dwellDelay/10);
4828 }
4829 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
4830 if (HaveMouseCapture()) {
4831
4832 // Slow down autoscrolling/selection
4833 autoScrollTimer.ticksToWait -= timer.tickSize;
4834 if (autoScrollTimer.ticksToWait > 0)
4835 return;
4836 autoScrollTimer.ticksToWait = autoScrollDelay;
4837
4838 // Adjust selection
4839 if (posDrag.IsValid()) {
4840 SetDragPosition(movePos);
4841 } else {
4842 if (selectionUnit == TextUnit::character) {
4843 if (sel.selType == Selection::SelTypes::stream && FlagSet(modifiers, KeyMod::Alt) && mouseSelectionRectangularSwitch) {
4844 sel.selType = Selection::SelTypes::rectangle;
4845 }
4846 if (sel.IsRectangular()) {
4847 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
4848 SetSelection(movePos, sel.RangeMain().anchor);
4849 } else if (sel.Count() > 1) {
4850 InvalidateSelection(sel.RangeMain(), false);
4851 const SelectionRange range(movePos, sel.RangeMain().anchor);
4852 sel.TentativeSelection(range);
4853 InvalidateSelection(range, true);
4854 } else {
4855 SetSelection(movePos, sel.RangeMain().anchor);
4856 }
4857 } else if (selectionUnit == TextUnit::word) {
4858 // Continue selecting by word
4859 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
4860 // No need to do anything. Previously this case was lumped
4861 // in with "Moved forward", but that can be harmful in this
4862 // case: a handler for the NotifyDoubleClick re-adjusts
4863 // the selection for a fancier definition of "word" (for
4864 // example, in Perl it is useful to include the leading
4865 // '$', '%' or '@' on variables for word selection). In this
4866 // the ButtonMove() called via TickFor() for auto-scrolling
4867 // could result in the fancier word selection adjustment
4868 // being unmade.
4869 } else {
4870 wordSelectInitialCaretPos = -1;
4871 WordSelection(movePos.Position());
4872 }
4873 } else {
4874 // Continue selecting by line
4875 LineSelection(movePos.Position(), lineAnchorPos, selectionUnit == TextUnit::wholeLine);
4876 }
4877 }
4878
4879 // Autoscroll
4880 const Sci::Line lineMove = DisplayFromPosition(movePos.Position());
4881 if (pt.y >= rcClient.bottom) {
4882 ScrollTo(lineMove - LinesOnScreen() + 1);
4883 Redraw();
4884 } else if (pt.y < rcClient.top) {
4885 ScrollTo(lineMove);
4886 Redraw();
4887 }
4888 EnsureCaretVisible(false, false, true);
4889
4890 if (hotspot.Valid() && !PointIsHotspot(pt))
4891 SetHotSpotRange(nullptr);
4892
4893 if (hotSpotClickPos != Sci::invalidPosition && PositionFromLocation(pt, true, true) != hotSpotClickPos) {
4894 if (inDragDrop == DragDrop::none) {
4895 DisplayCursor(Window::Cursor::text);
4896 }
4897 hotSpotClickPos = Sci::invalidPosition;
4898 }
4899
4900 } else {
4901 if (vs.fixedColumnWidth > 0) { // There is a margin
4902 if (PointInSelMargin(pt)) {
4903 DisplayCursor(GetMarginCursor(pt));
4904 SetHotSpotRange(nullptr);
4905 SetHoverIndicatorPosition(Sci::invalidPosition);
4906 return; // No need to test for selection
4907 }
4908 }
4909 // Display regular (drag) cursor over selection
4910 if (PointInSelection(pt) && !SelectionEmpty()) {
4911 DisplayCursor(Window::Cursor::arrow);
4912 SetHoverIndicatorPosition(Sci::invalidPosition);
4913 } else {
4914 SetHoverIndicatorPoint(pt);
4915 if (PointIsHotspot(pt)) {
4916 DisplayCursor(Window::Cursor::hand);
4917 SetHotSpotRange(&pt);
4918 } else {
4919 if (hoverIndicatorPos != Sci::invalidPosition)
4920 DisplayCursor(Window::Cursor::hand);
4921 else
4922 DisplayCursor(Window::Cursor::text);
4923 SetHotSpotRange(nullptr);
4924 }
4925 }
4926 }
4927}
4928
4929void Editor::ButtonUpWithModifiers(Point pt, unsigned int curTime, KeyMod modifiers) {
4930 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
4931 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
4932 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4933 if (hoverIndicatorPos != Sci::invalidPosition)
4934 InvalidateRange(newPos.Position(), newPos.Position() + 1);
4935 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4936 if (inDragDrop == DragDrop::initial) {
4937 inDragDrop = DragDrop::none;
4938 SetEmptySelection(newPos);
4939 selectionUnit = TextUnit::character;
4940 originalAnchorPos = sel.MainCaret();
4941 }
4942 if (hotSpotClickPos != Sci::invalidPosition && PointIsHotspot(pt)) {
4943 hotSpotClickPos = Sci::invalidPosition;
4944 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4945 newCharPos = MovePositionOutsideChar(newCharPos, -1);
4946 NotifyHotSpotReleaseClick(newCharPos.Position(), modifiers & KeyMod::Ctrl);
4947 }
4948 if (HaveMouseCapture()) {
4949 if (PointInSelMargin(pt)) {
4950 DisplayCursor(GetMarginCursor(pt));
4951 } else {
4952 DisplayCursor(Window::Cursor::text);
4953 SetHotSpotRange(nullptr);
4954 }
4955 ptMouseLast = pt;
4956 SetMouseCapture(false);
4957 FineTickerCancel(TickReason::scroll);
4958 NotifyIndicatorClick(false, newPos.Position(), modifiers);
4959 if (inDragDrop == DragDrop::dragging) {
4960 const SelectionPosition selStart = SelectionStart();
4961 const SelectionPosition selEnd = SelectionEnd();
4962 if (selStart < selEnd) {
4963 if (drag.Length()) {
4964 const Sci::Position length = drag.Length();
4965 if (FlagSet(modifiers, KeyMod::Ctrl)) {
4966 const Sci::Position lengthInserted = pdoc->InsertString(
4967 newPos.Position(), drag.Data(), length);
4968 if (lengthInserted > 0) {
4969 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4970 }
4971 } else if (newPos < selStart) {
4972 pdoc->DeleteChars(selStart.Position(), drag.Length());
4973 const Sci::Position lengthInserted = pdoc->InsertString(
4974 newPos.Position(), drag.Data(), length);
4975 if (lengthInserted > 0) {
4976 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4977 }
4978 } else if (newPos > selEnd) {
4979 pdoc->DeleteChars(selStart.Position(), drag.Length());
4980 newPos.Add(-static_cast<Sci::Position>(drag.Length()));
4981 const Sci::Position lengthInserted = pdoc->InsertString(
4982 newPos.Position(), drag.Data(), length);
4983 if (lengthInserted > 0) {
4984 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4985 }
4986 } else {
4987 SetEmptySelection(newPos.Position());
4988 }
4989 drag.Clear();
4990 }
4991 selectionUnit = TextUnit::character;
4992 }
4993 } else {
4994 if (selectionUnit == TextUnit::character) {
4995 if (sel.Count() > 1) {
4996 sel.RangeMain() =
4997 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
4998 InvalidateWholeSelection();
4999 } else {
5000 SetSelection(newPos, sel.RangeMain().anchor);
5001 }
5002 }
5003 sel.CommitTentative();
5004 }
5005 SetRectangularRange();
5006 lastClickTime = curTime;
5007 lastClick = pt;
5008 lastXChosen = static_cast<int>(pt.x) + xOffset;
5009 if (sel.selType == Selection::SelTypes::stream) {
5010 SetLastXChosen();
5011 }
5012 inDragDrop = DragDrop::none;
5013 EnsureCaretVisible(false);
5014 }
5015}
5016
5017bool Editor::Idle() {
5018 NotifyUpdateUI();
5019
5020 bool needWrap = Wrapping() && wrapPending.NeedsWrap();
5021
5022 if (needWrap) {
5023 // Wrap lines during idle.
5024 WrapLines(WrapScope::wsIdle);
5025 // No more wrapping
5026 needWrap = wrapPending.NeedsWrap();
5027 } else if (needIdleStyling) {
5028 IdleStyle();
5029 }
5030
5031 // Add more idle things to do here, but make sure idleDone is
5032 // set correctly before the function returns. returning
5033 // false will stop calling this idle function until SetIdle() is
5034 // called again.
5035
5036 const bool idleDone = !needWrap && !needIdleStyling; // && thatDone && theOtherThingDone...
5037
5038 return !idleDone;
5039}
5040
5041void Editor::TickFor(TickReason reason) {
5042 switch (reason) {
5043 case TickReason::caret:
5044 caret.on = !caret.on;
5045 if (caret.active) {
5046 InvalidateCaret();
5047 }
5048 break;
5049 case TickReason::scroll:
5050 // Auto scroll
5051 ButtonMoveWithModifiers(ptMouseLast, 0, KeyMod::Norm);
5052 break;
5053 case TickReason::widen:
5054 SetScrollBars();
5055 FineTickerCancel(TickReason::widen);
5056 break;
5057 case TickReason::dwell:
5058 if ((!HaveMouseCapture()) &&
5059 (ptMouseLast.y >= 0)) {
5060 dwelling = true;
5061 NotifyDwelling(ptMouseLast, dwelling);
5062 }
5063 FineTickerCancel(TickReason::dwell);
5064 break;
5065 default:
5066 // tickPlatform handled by subclass
5067 break;
5068 }
5069}
5070
5071// FineTickerStart is be overridden by subclasses that support fine ticking so
5072// this method should never be called.
5073bool Editor::FineTickerRunning(TickReason) {
5074 assert(false);
5075 return false;
5076}
5077
5078// FineTickerStart is be overridden by subclasses that support fine ticking so
5079// this method should never be called.
5080void Editor::FineTickerStart(TickReason, int, int) {
5081 assert(false);
5082}
5083
5084// FineTickerCancel is be overridden by subclasses that support fine ticking so
5085// this method should never be called.
5086void Editor::FineTickerCancel(TickReason) {
5087 assert(false);
5088}
5089
5090void Editor::SetFocusState(bool focusState) {
5091 const bool changing = hasFocus != focusState;
5092 hasFocus = focusState;
5093 if (changing) {
5094 Redraw();
5095 }
5096 NotifyFocus(hasFocus);
5097 if (!hasFocus) {
5098 CancelModes();
5099 }
5100 ShowCaretAtCurrentPosition();
5101}
5102
5103void Editor::UpdateBaseElements() {
5104 // Overridden by subclasses
5105}
5106
5107Sci::Position Editor::PositionAfterArea(PRectangle rcArea) const {
5108 // The start of the document line after the display line after the area
5109 // This often means that the line after a modification is restyled which helps
5110 // detect multiline comment additions and heals single line comments
5111 const Sci::Line lineAfter = TopLineOfMain() + static_cast<Sci::Line>(rcArea.bottom - 1) / vs.lineHeight + 1;
5112 if (lineAfter < pcs->LinesDisplayed())
5113 return pdoc->LineStart(pcs->DocFromDisplay(lineAfter) + 1);
5114 else
5115 return pdoc->Length();
5116}
5117
5118// Style to a position within the view. If this causes a change at end of last line then
5119// affects later lines so style all the viewed text.
5120void Editor::StyleToPositionInView(Sci::Position pos) {
5121 Sci::Position endWindow = PositionAfterArea(GetClientDrawingRectangle());
5122 if (pos > endWindow)
5123 pos = endWindow;
5124 const int styleAtEnd = pdoc->StyleIndexAt(pos-1);
5125 pdoc->EnsureStyledTo(pos);
5126 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleIndexAt(pos-1))) {
5127 // Style at end of line changed so is multi-line change like starting a comment
5128 // so require rest of window to be styled.
5129 DiscardOverdraw(); // Prepared bitmaps may be invalid
5130 // DiscardOverdraw may have truncated client drawing area so recalculate endWindow
5131 endWindow = PositionAfterArea(GetClientDrawingRectangle());
5132 pdoc->EnsureStyledTo(endWindow);
5133 }
5134}
5135
5136Sci::Position Editor::PositionAfterMaxStyling(Sci::Position posMax, bool scrolling) const {
5137 if (SynchronousStylingToVisible()) {
5138 // Both states do not limit styling
5139 return posMax;
5140 }
5141
5142 // Try to keep time taken by styling reasonable so interaction remains smooth.
5143 // When scrolling, allow less time to ensure responsive
5144 const double secondsAllowed = scrolling ? 0.005 : 0.02;
5145
5146 const size_t actionsInAllowedTime = std::clamp<Sci::Line>(
5147 pdoc->durationStyleOneByte.ActionsInAllowedTime(secondsAllowed),
5148 0x200, 0x20000);
5149 const Sci::Line lineLast = pdoc->LineFromPositionAfter(pdoc->SciLineFromPosition(pdoc->GetEndStyled()), actionsInAllowedTime);
5150 const Sci::Line stylingMaxLine = std::min(lineLast, pdoc->LinesTotal());
5151
5152 return std::min(pdoc->LineStart(stylingMaxLine), posMax);
5153}
5154
5155void Editor::StartIdleStyling(bool truncatedLastStyling) {
5156 if ((idleStyling == IdleStyling::All) || (idleStyling == IdleStyling::AfterVisible)) {
5157 if (pdoc->GetEndStyled() < pdoc->Length()) {
5158 // Style remainder of document in idle time
5159 needIdleStyling = true;
5160 }
5161 } else if (truncatedLastStyling) {
5162 needIdleStyling = true;
5163 }
5164
5165 if (needIdleStyling) {
5166 SetIdle(true);
5167 }
5168}
5169
5170// Style for an area but bound the amount of styling to remain responsive
5171void Editor::StyleAreaBounded(PRectangle rcArea, bool scrolling) {
5172 const Sci::Position posAfterArea = PositionAfterArea(rcArea);
5173 const Sci::Position posAfterMax = PositionAfterMaxStyling(posAfterArea, scrolling);
5174 if (posAfterMax < posAfterArea) {
5175 // Idle styling may be performed before current visible area
5176 // Style a bit now then style further in idle time
5177 pdoc->StyleToAdjustingLineDuration(posAfterMax);
5178 } else {
5179 // Can style all wanted now.
5180 StyleToPositionInView(posAfterArea);
5181 }
5182 StartIdleStyling(posAfterMax < posAfterArea);
5183}
5184
5185void Editor::IdleStyle() {
5186 const Sci::Position posAfterArea = PositionAfterArea(GetClientRectangle());
5187 const Sci::Position endGoal = (idleStyling >= IdleStyling::AfterVisible) ?
5188 pdoc->Length() : posAfterArea;
5189 const Sci::Position posAfterMax = PositionAfterMaxStyling(endGoal, false);
5190 pdoc->StyleToAdjustingLineDuration(posAfterMax);
5191 if (pdoc->GetEndStyled() >= endGoal) {
5192 needIdleStyling = false;
5193 }
5194}
5195
5196void Editor::IdleWork() {
5197 // Style the line after the modification as this allows modifications that change just the
5198 // line of the modification to heal instead of propagating to the rest of the window.
5199 if (FlagSet(workNeeded.items, WorkItems::style)) {
5200 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2));
5201 }
5202 NotifyUpdateUI();
5203 workNeeded.Reset();
5204}
5205
5206void Editor::QueueIdleWork(WorkItems items, Sci::Position upTo) {
5207 workNeeded.Need(items, upTo);
5208}
5209
5210int Editor::SupportsFeature(Supports feature) {
5211 AutoSurface surface(this);
5212 return surface->SupportsFeature(feature);
5213}
5214
5215bool Editor::PaintContains(PRectangle rc) {
5216 if (rc.Empty()) {
5217 return true;
5218 } else {
5219 return rcPaint.Contains(rc);
5220 }
5221}
5222
5223bool Editor::PaintContainsMargin() {
5224 if (wMargin.GetID()) {
5225 // With separate margin view, paint of text view
5226 // never contains margin.
5227 return false;
5228 }
5229 PRectangle rcSelMargin = GetClientRectangle();
5230 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart);
5231 return PaintContains(rcSelMargin);
5232}
5233
5234void Editor::CheckForChangeOutsidePaint(Range r) {
5235 if (paintState == PaintState::painting && !paintingAllText) {
5236 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
5237 if (!r.Valid())
5238 return;
5239
5240 PRectangle rcRange = RectangleFromRange(r, 0);
5241 const PRectangle rcText = GetTextRectangle();
5242 if (rcRange.top < rcText.top) {
5243 rcRange.top = rcText.top;
5244 }
5245 if (rcRange.bottom > rcText.bottom) {
5246 rcRange.bottom = rcText.bottom;
5247 }
5248
5249 if (!PaintContains(rcRange)) {
5250 AbandonPaint();
5251 paintAbandonedByStyling = true;
5252 }
5253 }
5254}
5255
5256void Editor::SetBraceHighlight(Sci::Position pos0, Sci::Position pos1, int matchStyle) {
5257 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
5258 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
5259 CheckForChangeOutsidePaint(Range(braces[0]));
5260 CheckForChangeOutsidePaint(Range(pos0));
5261 braces[0] = pos0;
5262 }
5263 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
5264 CheckForChangeOutsidePaint(Range(braces[1]));
5265 CheckForChangeOutsidePaint(Range(pos1));
5266 braces[1] = pos1;
5267 }
5268 bracesMatchStyle = matchStyle;
5269 if (paintState == PaintState::notPainting) {
5270 Redraw();
5271 }
5272 }
5273}
5274
5275void Editor::SetAnnotationHeights(Sci::Line start, Sci::Line end) {
5276 if (vs.annotationVisible != AnnotationVisible::Hidden) {
5277 RefreshStyleData();
5278 bool changedHeight = false;
5279 for (Sci::Line line=start; line<end && line<pdoc->LinesTotal(); line++) {
5280 int linesWrapped = 1;
5281 if (Wrapping()) {
5282 AutoSurface surface(this);
5283 std::shared_ptr<LineLayout> ll = view.RetrieveLineLayout(line, *this);
5284 if (surface && ll) {
5285 view.LayoutLine(*this, surface, vs, ll.get(), wrapWidth);
5286 linesWrapped = ll->lines;
5287 }
5288 }
5289 if (pcs->SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
5290 changedHeight = true;
5291 }
5292 if (changedHeight) {
5293 Redraw();
5294 }
5295 }
5296}
5297
5298void Editor::SetDocPointer(Document *document) {
5299 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
5300 pdoc->RemoveWatcher(this, nullptr);
5301 pdoc->Release();
5302 if (!document) {
5303 pdoc = new Document(DocumentOption::Default);
5304 } else {
5305 pdoc = document;
5306 }
5307 pdoc->AddRef();
5308 pcs = ContractionStateCreate(pdoc->IsLarge());
5309
5310 // Ensure all positions within document
5311 sel.Clear();
5312 targetRange = SelectionSegment();
5313
5314 braces[0] = Sci::invalidPosition;
5315 braces[1] = Sci::invalidPosition;
5316
5317 vs.ReleaseAllExtendedStyles();
5318
5319 SetRepresentations();
5320
5321 // Reset the contraction state to fully shown.
5322 pcs->Clear();
5323 pcs->InsertLines(0, pdoc->LinesTotal() - 1);
5324 SetAnnotationHeights(0, pdoc->LinesTotal());
5325 view.llc.Deallocate();
5326 NeedWrapping();
5327
5328 hotspot = Range(Sci::invalidPosition);
5329 hoverIndicatorPos = Sci::invalidPosition;
5330
5331 view.ClearAllTabstops();
5332
5333 pdoc->AddWatcher(this, nullptr);
5334 SetScrollBars();
5335 Redraw();
5336}
5337
5338void Editor::SetAnnotationVisible(AnnotationVisible visible) {
5339 if (vs.annotationVisible != visible) {
5340 const bool changedFromOrToHidden = ((vs.annotationVisible != AnnotationVisible::Hidden) != (visible != AnnotationVisible::Hidden));
5341 vs.annotationVisible = visible;
5342 if (changedFromOrToHidden) {
5343 const int dir = (vs.annotationVisible!= AnnotationVisible::Hidden) ? 1 : -1;
5344 for (Sci::Line line=0; line<pdoc->LinesTotal(); line++) {
5345 const int annotationLines = pdoc->AnnotationLines(line);
5346 if (annotationLines > 0) {
5347 pcs->SetHeight(line, pcs->GetHeight(line) + annotationLines * dir);
5348 }
5349 }
5350 SetScrollBars();
5351 }
5352 Redraw();
5353 }
5354}
5355
5356void Editor::SetEOLAnnotationVisible(EOLAnnotationVisible visible) {
5357 if (vs.eolAnnotationVisible != visible) {
5358 vs.eolAnnotationVisible = visible;
5359 Redraw();
5360 }
5361}
5362
5363/**
5364 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
5365 */
5366Sci::Line Editor::ExpandLine(Sci::Line line) {
5367 const Sci::Line lineMaxSubord = pdoc->GetLastChild(line);
5368 line++;
5369 while (line <= lineMaxSubord) {
5370 pcs->SetVisible(line, line, true);
5371 const FoldLevel level = pdoc->GetFoldLevel(line);
5372 if (LevelIsHeader(level)) {
5373 if (pcs->GetExpanded(line)) {
5374 line = ExpandLine(line);
5375 } else {
5376 line = pdoc->GetLastChild(line);
5377 }
5378 }
5379 line++;
5380 }
5381 return lineMaxSubord;
5382}
5383
5384void Editor::SetFoldExpanded(Sci::Line lineDoc, bool expanded) {
5385 if (pcs->SetExpanded(lineDoc, expanded)) {
5386 RedrawSelMargin();
5387 }
5388}
5389
5390void Editor::FoldLine(Sci::Line line, FoldAction action) {
5391 if (line >= 0) {
5392 if (action == FoldAction::Toggle) {
5393 if (!LevelIsHeader(pdoc->GetFoldLevel(line))) {
5394 line = pdoc->GetFoldParent(line);
5395 if (line < 0)
5396 return;
5397 }
5398 action = (pcs->GetExpanded(line)) ? FoldAction::Contract : FoldAction::Expand;
5399 }
5400
5401 if (action == FoldAction::Contract) {
5402 const Sci::Line lineMaxSubord = pdoc->GetLastChild(line);
5403 if (lineMaxSubord > line) {
5404 pcs->SetExpanded(line, false);
5405 pcs->SetVisible(line + 1, lineMaxSubord, false);
5406
5407 const Sci::Line lineCurrent =
5408 pdoc->SciLineFromPosition(sel.MainCaret());
5409 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
5410 // This does not re-expand the fold
5411 EnsureCaretVisible();
5412 }
5413 }
5414
5415 } else {
5416 if (!(pcs->GetVisible(line))) {
5417 EnsureLineVisible(line, false);
5418 GoToLine(line);
5419 }
5420 pcs->SetExpanded(line, true);
5421 ExpandLine(line);
5422 }
5423
5424 SetScrollBars();
5425 Redraw();
5426 }
5427}
5428
5429void Editor::FoldExpand(Sci::Line line, FoldAction action, FoldLevel level) {
5430 bool expanding = action == FoldAction::Expand;
5431 if (action == FoldAction::Toggle) {
5432 expanding = !pcs->GetExpanded(line);
5433 }
5434 // Ensure child lines lexed and fold information extracted before
5435 // flipping the state.
5436 pdoc->GetLastChild(line, LevelNumberPart(level));
5437 SetFoldExpanded(line, expanding);
5438 if (expanding && (pcs->HiddenLines() == 0))
5439 // Nothing to do
5440 return;
5441 const Sci::Line lineMaxSubord = pdoc->GetLastChild(line, LevelNumberPart(level));
5442 line++;
5443 pcs->SetVisible(line, lineMaxSubord, expanding);
5444 while (line <= lineMaxSubord) {
5445 const FoldLevel levelLine = pdoc->GetFoldLevel(line);
5446 if (LevelIsHeader(levelLine)) {
5447 SetFoldExpanded(line, expanding);
5448 }
5449 line++;
5450 }
5451 SetScrollBars();
5452 Redraw();
5453}
5454
5455Sci::Line Editor::ContractedFoldNext(Sci::Line lineStart) const {
5456 for (Sci::Line line = lineStart; line<pdoc->LinesTotal();) {
5457 if (!pcs->GetExpanded(line) && LevelIsHeader(pdoc->GetFoldLevel(line)))
5458 return line;
5459 line = pcs->ContractedNext(line+1);
5460 if (line < 0)
5461 return -1;
5462 }
5463
5464 return -1;
5465}
5466
5467/**
5468 * Recurse up from this line to find any folds that prevent this line from being visible
5469 * and unfold them all.
5470 */
5471void Editor::EnsureLineVisible(Sci::Line lineDoc, bool enforcePolicy) {
5472
5473 // In case in need of wrapping to ensure DisplayFromDoc works.
5474 if (lineDoc >= wrapPending.start) {
5475 if (WrapLines(WrapScope::wsAll)) {
5476 Redraw();
5477 }
5478 }
5479
5480 if (!pcs->GetVisible(lineDoc)) {
5481 // Back up to find a non-blank line
5482 Sci::Line lookLine = lineDoc;
5483 FoldLevel lookLineLevel = pdoc->GetFoldLevel(lookLine);
5484 while ((lookLine > 0) && LevelIsWhitespace(lookLineLevel)) {
5485 lookLineLevel = pdoc->GetFoldLevel(--lookLine);
5486 }
5487 Sci::Line lineParent = pdoc->GetFoldParent(lookLine);
5488 if (lineParent < 0) {
5489 // Backed up to a top level line, so try to find parent of initial line
5490 lineParent = pdoc->GetFoldParent(lineDoc);
5491 }
5492 if (lineParent >= 0) {
5493 if (lineDoc != lineParent)
5494 EnsureLineVisible(lineParent, enforcePolicy);
5495 if (!pcs->GetExpanded(lineParent)) {
5496 pcs->SetExpanded(lineParent, true);
5497 ExpandLine(lineParent);
5498 }
5499 }
5500 SetScrollBars();
5501 Redraw();
5502 }
5503 if (enforcePolicy) {
5504 const Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
5505 if (FlagSet(visiblePolicy.policy, VisiblePolicy::Slop)) {
5506 if ((topLine > lineDisplay) || ((FlagSet(visiblePolicy.policy, VisiblePolicy::Strict)) && (topLine + visiblePolicy.slop > lineDisplay))) {
5507 SetTopLine(std::clamp<Sci::Line>(lineDisplay - visiblePolicy.slop, 0, MaxScrollPos()));
5508 SetVerticalScrollPos();
5509 Redraw();
5510 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
5511 ((FlagSet(visiblePolicy.policy, VisiblePolicy::Strict)) && (lineDisplay > topLine + LinesOnScreen() - 1 - visiblePolicy.slop))) {
5512 SetTopLine(std::clamp<Sci::Line>(lineDisplay - LinesOnScreen() + 1 + visiblePolicy.slop, 0, MaxScrollPos()));
5513 SetVerticalScrollPos();
5514 Redraw();
5515 }
5516 } else {
5517 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (FlagSet(visiblePolicy.policy, VisiblePolicy::Strict))) {
5518 SetTopLine(std::clamp<Sci::Line>(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
5519 SetVerticalScrollPos();
5520 Redraw();
5521 }
5522 }
5523 }
5524}
5525
5526void Editor::FoldAll(FoldAction action) {
5527 pdoc->EnsureStyledTo(pdoc->Length());
5528 const Sci::Line maxLine = pdoc->LinesTotal();
5529 bool expanding = action == FoldAction::Expand;
5530 if (action == FoldAction::Toggle) {
5531 // Discover current state
5532 for (int lineSeek = 0; lineSeek < maxLine; lineSeek++) {
5533 if (LevelIsHeader(pdoc->GetFoldLevel(lineSeek))) {
5534 expanding = !pcs->GetExpanded(lineSeek);
5535 break;
5536 }
5537 }
5538 }
5539 if (expanding) {
5540 pcs->SetVisible(0, maxLine-1, true);
5541 for (int line = 0; line < maxLine; line++) {
5542 const FoldLevel levelLine = pdoc->GetFoldLevel(line);
5543 if (LevelIsHeader(levelLine)) {
5544 SetFoldExpanded(line, true);
5545 }
5546 }
5547 } else {
5548 for (Sci::Line line = 0; line < maxLine; line++) {
5549 const FoldLevel level = pdoc->GetFoldLevel(line);
5550 if (LevelIsHeader(level) &&
5551 (FoldLevel::Base == LevelNumberPart(level))) {
5552 SetFoldExpanded(line, false);
5553 const Sci::Line lineMaxSubord = pdoc->GetLastChild(line);
5554 if (lineMaxSubord > line) {
5555 pcs->SetVisible(line + 1, lineMaxSubord, false);
5556 }
5557 }
5558 }
5559 }
5560 SetScrollBars();
5561 Redraw();
5562}
5563
5564void Editor::FoldChanged(Sci::Line line, FoldLevel levelNow, FoldLevel levelPrev) {
5565 if (LevelIsHeader(levelNow)) {
5566 if (!LevelIsHeader(levelPrev)) {
5567 // Adding a fold point.
5568 if (pcs->SetExpanded(line, true)) {
5569 RedrawSelMargin();
5570 }
5571 FoldExpand(line, FoldAction::Expand, levelPrev);
5572 }
5573 } else if (LevelIsHeader(levelPrev)) {
5574 const Sci::Line prevLine = line - 1;
5575 const FoldLevel prevLineLevel = pdoc->GetFoldLevel(prevLine);
5576
5577 // Combining two blocks where the first block is collapsed (e.g. by deleting the line(s) which separate(s) the two blocks)
5578 if ((LevelNumber(prevLineLevel) == LevelNumber(levelNow)) && !pcs->GetVisible(prevLine))
5579 FoldLine(pdoc->GetFoldParent(prevLine), FoldAction::Expand);
5580
5581 if (!pcs->GetExpanded(line)) {
5582 // Removing the fold from one that has been contracted so should expand
5583 // otherwise lines are left invisible with no way to make them visible
5584 if (pcs->SetExpanded(line, true)) {
5585 RedrawSelMargin();
5586 }
5587 // Combining two blocks where the second one is collapsed (e.g. by adding characters in the line which separates the two blocks)
5588 FoldExpand(line, FoldAction::Expand, levelPrev);
5589 }
5590 }
5591 if (!LevelIsWhitespace(levelNow) &&
5592 (LevelNumber(levelPrev) > LevelNumber(levelNow))) {
5593 if (pcs->HiddenLines()) {
5594 // See if should still be hidden
5595 const Sci::Line parentLine = pdoc->GetFoldParent(line);
5596 if ((parentLine < 0) || (pcs->GetExpanded(parentLine) && pcs->GetVisible(parentLine))) {
5597 pcs->SetVisible(line, line, true);
5598 SetScrollBars();
5599 Redraw();
5600 }
5601 }
5602 }
5603
5604 // Combining two blocks where the first one is collapsed (e.g. by adding characters in the line which separates the two blocks)
5605 if (!LevelIsWhitespace(levelNow) && (LevelNumber(levelPrev) < LevelNumber(levelNow))) {
5606 if (pcs->HiddenLines()) {
5607 const Sci::Line parentLine = pdoc->GetFoldParent(line);
5608 if (!pcs->GetExpanded(parentLine) && pcs->GetVisible(line))
5609 FoldLine(parentLine, FoldAction::Expand);
5610 }
5611 }
5612}
5613
5614void Editor::NeedShown(Sci::Position pos, Sci::Position len) {
5615 if (FlagSet(foldAutomatic, AutomaticFold::Show)) {
5616 const Sci::Line lineStart = pdoc->SciLineFromPosition(pos);
5617 const Sci::Line lineEnd = pdoc->SciLineFromPosition(pos+len);
5618 for (Sci::Line line = lineStart; line <= lineEnd; line++) {
5619 EnsureLineVisible(line, false);
5620 }
5621 } else {
5622 NotifyNeedShown(pos, len);
5623 }
5624}
5625
5626Sci::Position Editor::GetTag(char *tagValue, int tagNumber) {
5627 const char *text = nullptr;
5628 Sci::Position length = 0;
5629 if ((tagNumber >= 1) && (tagNumber <= 9)) {
5630 char name[3] = "\\?";
5631 name[1] = static_cast<char>(tagNumber + '0');
5632 length = 2;
5633 text = pdoc->SubstituteByPosition(name, &length);
5634 }
5635 if (tagValue) {
5636 if (text)
5637 memcpy(tagValue, text, length + 1);
5638 else
5639 *tagValue = '\0';
5640 }
5641 return length;
5642}
5643
5644Sci::Position Editor::ReplaceTarget(bool replacePatterns, const char *text, Sci::Position length) {
5645 UndoGroup ug(pdoc);
5646 if (length == -1)
5647 length = strlen(text);
5648 if (replacePatterns) {
5649 text = pdoc->SubstituteByPosition(text, &length);
5650 if (!text) {
5651 return 0;
5652 }
5653 }
5654
5655 // Remove the text inside the range
5656 if (targetRange.Length() > 0)
5657 pdoc->DeleteChars(targetRange.start.Position(), targetRange.Length());
5658 targetRange.end = targetRange.start;
5659
5660 // Realize virtual space of target start
5661 const Sci::Position startAfterSpaceInsertion = RealizeVirtualSpace(targetRange.start.Position(), targetRange.start.VirtualSpace());
5662 targetRange.start.SetPosition(startAfterSpaceInsertion);
5663 targetRange.end = targetRange.start;
5664
5665 // Insert the new text
5666 const Sci::Position lengthInserted = pdoc->InsertString(targetRange.start.Position(), text, length);
5667 targetRange.end.SetPosition(targetRange.start.Position() + lengthInserted);
5668 return length;
5669}
5670
5671bool Editor::IsUnicodeMode() const noexcept {
5672 return pdoc && (CpUtf8 == pdoc->dbcsCodePage);
5673}
5674
5675int Editor::CodePage() const noexcept {
5676 if (pdoc)
5677 return pdoc->dbcsCodePage;
5678 else
5679 return 0;
5680}
5681
5682Sci::Line Editor::WrapCount(Sci::Line line) {
5683 AutoSurface surface(this);
5684 std::shared_ptr<LineLayout> ll = view.RetrieveLineLayout(line, *this);
5685
5686 if (surface && ll) {
5687 view.LayoutLine(*this, surface, vs, ll.get(), wrapWidth);
5688 return ll->lines;
5689 } else {
5690 return 1;
5691 }
5692}
5693
5694void Editor::AddStyledText(const char *buffer, Sci::Position appendLength) {
5695 // The buffer consists of alternating character bytes and style bytes
5696 const Sci::Position textLength = appendLength / 2;
5697 std::string text(textLength, '\0');
5698 Sci::Position i;
5699 for (i = 0; i < textLength; i++) {
5700 text[i] = buffer[i*2];
5701 }
5702 const Sci::Position lengthInserted = pdoc->InsertString(CurrentPosition(), text.c_str(), textLength);
5703 for (i = 0; i < textLength; i++) {
5704 text[i] = buffer[i*2+1];
5705 }
5706 pdoc->StartStyling(CurrentPosition());
5707 pdoc->SetStyles(textLength, text.c_str());
5708 SetEmptySelection(sel.MainCaret() + lengthInserted);
5709}
5710
5711bool Editor::ValidMargin(uptr_t wParam) const noexcept {
5712 return wParam < vs.ms.size();
5713}
5714
5715void Editor::StyleSetMessage(Message iMessage, uptr_t wParam, sptr_t lParam) {
5716 vs.EnsureStyle(wParam);
5717 switch (iMessage) {
5718 case Message::StyleSetFore:
5719 vs.styles[wParam].fore = ColourRGBA::FromIpRGB(lParam);
5720 break;
5721 case Message::StyleSetBack:
5722 vs.styles[wParam].back = ColourRGBA::FromIpRGB(lParam);
5723 break;
5724 case Message::StyleSetBold:
5725 vs.styles[wParam].weight = lParam != 0 ? FontWeight::Bold : FontWeight::Normal;
5726 break;
5727 case Message::StyleSetWeight:
5728 vs.styles[wParam].weight = static_cast<FontWeight>(lParam);
5729 break;
5730 case Message::StyleSetItalic:
5731 vs.styles[wParam].italic = lParam != 0;
5732 break;
5733 case Message::StyleSetEOLFilled:
5734 vs.styles[wParam].eolFilled = lParam != 0;
5735 break;
5736 case Message::StyleSetSize:
5737 vs.styles[wParam].size = static_cast<int>(lParam * FontSizeMultiplier);
5738 break;
5739 case Message::StyleSetSizeFractional:
5740 vs.styles[wParam].size = static_cast<int>(lParam);
5741 break;
5742 case Message::StyleSetFont:
5743 if (lParam != 0) {
5744 vs.SetStyleFontName(static_cast<int>(wParam), ConstCharPtrFromSPtr(lParam));
5745 }
5746 break;
5747 case Message::StyleSetUnderline:
5748 vs.styles[wParam].underline = lParam != 0;
5749 break;
5750 case Message::StyleSetCase:
5751 vs.styles[wParam].caseForce = static_cast<Style::CaseForce>(lParam);
5752 break;
5753 case Message::StyleSetCharacterSet:
5754 vs.styles[wParam].characterSet = static_cast<CharacterSet>(lParam);
5755 pdoc->SetCaseFolder(nullptr);
5756 break;
5757 case Message::StyleSetVisible:
5758 vs.styles[wParam].visible = lParam != 0;
5759 break;
5760 case Message::StyleSetChangeable:
5761 vs.styles[wParam].changeable = lParam != 0;
5762 break;
5763 case Message::StyleSetHotSpot:
5764 vs.styles[wParam].hotspot = lParam != 0;
5765 break;
5766 case Message::StyleSetCheckMonospaced:
5767 vs.styles[wParam].checkMonospaced = lParam != 0;
5768 break;
5769 default:
5770 break;
5771 }
5772 InvalidateStyleRedraw();
5773}
5774
5775sptr_t Editor::StyleGetMessage(Message iMessage, uptr_t wParam, sptr_t lParam) {
5776 vs.EnsureStyle(wParam);
5777 switch (iMessage) {
5778 case Message::StyleGetFore:
5779 return vs.styles[wParam].fore.OpaqueRGB();
5780 case Message::StyleGetBack:
5781 return vs.styles[wParam].back.OpaqueRGB();
5782 case Message::StyleGetBold:
5783 return vs.styles[wParam].weight > FontWeight::Normal;
5784 case Message::StyleGetWeight:
5785 return static_cast<sptr_t>(vs.styles[wParam].weight);
5786 case Message::StyleGetItalic:
5787 return vs.styles[wParam].italic ? 1 : 0;
5788 case Message::StyleGetEOLFilled:
5789 return vs.styles[wParam].eolFilled ? 1 : 0;
5790 case Message::StyleGetSize:
5791 return vs.styles[wParam].size / FontSizeMultiplier;
5792 case Message::StyleGetSizeFractional:
5793 return vs.styles[wParam].size;
5794 case Message::StyleGetFont:
5795 return StringResult(lParam, vs.styles[wParam].fontName);
5796 case Message::StyleGetUnderline:
5797 return vs.styles[wParam].underline ? 1 : 0;
5798 case Message::StyleGetCase:
5799 return static_cast<int>(vs.styles[wParam].caseForce);
5800 case Message::StyleGetCharacterSet:
5801 return static_cast<sptr_t>(vs.styles[wParam].characterSet);
5802 case Message::StyleGetVisible:
5803 return vs.styles[wParam].visible ? 1 : 0;
5804 case Message::StyleGetChangeable:
5805 return vs.styles[wParam].changeable ? 1 : 0;
5806 case Message::StyleGetHotSpot:
5807 return vs.styles[wParam].hotspot ? 1 : 0;
5808 case Message::StyleGetCheckMonospaced:
5809 return vs.styles[wParam].checkMonospaced ? 1 : 0;
5810 default:
5811 break;
5812 }
5813 return 0;
5814}
5815
5816void Editor::SetSelectionNMessage(Message iMessage, uptr_t wParam, sptr_t lParam) {
5817 if (wParam >= sel.Count()) {
5818 return;
5819 }
5820 InvalidateRange(sel.Range(wParam).Start().Position(), sel.Range(wParam).End().Position());
5821
5822 switch (iMessage) {
5823 case Message::SetSelectionNCaret:
5824 sel.Range(wParam).caret.SetPosition(lParam);
5825 break;
5826
5827 case Message::SetSelectionNAnchor:
5828 sel.Range(wParam).anchor.SetPosition(lParam);
5829 break;
5830
5831 case Message::SetSelectionNCaretVirtualSpace:
5832 sel.Range(wParam).caret.SetVirtualSpace(lParam);
5833 break;
5834
5835 case Message::SetSelectionNAnchorVirtualSpace:
5836 sel.Range(wParam).anchor.SetVirtualSpace(lParam);
5837 break;
5838
5839 case Message::SetSelectionNStart:
5840 sel.Range(wParam).anchor.SetPosition(lParam);
5841 break;
5842
5843 case Message::SetSelectionNEnd:
5844 sel.Range(wParam).caret.SetPosition(lParam);
5845 break;
5846
5847 default:
5848 break;
5849
5850 }
5851
5852 InvalidateRange(sel.Range(wParam).Start().Position(), sel.Range(wParam).End().Position());
5853 ContainerNeedsUpdate(Update::Selection);
5854}
5855
5856sptr_t Editor::StringResult(sptr_t lParam, const char *val) noexcept {
5857 const size_t len = val ? strlen(val) : 0;
5858 if (lParam) {
5859 char *ptr = CharPtrFromSPtr(lParam);
5860 if (val)
5861 memcpy(ptr, val, len+1);
5862 else
5863 *ptr = 0;
5864 }
5865 return len; // Not including NUL
5866}
5867
5868sptr_t Editor::BytesResult(sptr_t lParam, const unsigned char *val, size_t len) noexcept {
5869 // No NUL termination: len is number of valid/displayed bytes
5870 if ((lParam) && (len > 0)) {
5871 char *ptr = CharPtrFromSPtr(lParam);
5872 if (val)
5873 memcpy(ptr, val, len);
5874 else
5875 *ptr = 0;
5876 }
5877 return val ? len : 0;
5878}
5879
5880sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
5881 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
5882
5883 // Optional macro recording hook
5884 if (recordingMacro)
5885 NotifyMacroRecord(iMessage, wParam, lParam);
5886
5887 switch (iMessage) {
5888
5889 case Message::GetText: {
5890 if (lParam == 0)
5891 return pdoc->Length();
5892 char *ptr = CharPtrFromSPtr(lParam);
5893 const Sci_Position len = std::min<Sci_Position>(wParam, pdoc->Length());
5894 pdoc->GetCharRange(ptr, 0, len);
5895 ptr[len] = '\0';
5896 return len;
5897 }
5898
5899 case Message::SetText: {
5900 if (lParam == 0)
5901 return 0;
5902 UndoGroup ug(pdoc);
5903 pdoc->DeleteChars(0, pdoc->Length());
5904 SetEmptySelection(0);
5905 const char *text = ConstCharPtrFromSPtr(lParam);
5906 pdoc->InsertString(0, text, strlen(text));
5907 return 1;
5908 }
5909
5910 case Message::GetTextLength:
5911 return pdoc->Length();
5912
5913 case Message::Cut:
5914 Cut();
5915 SetLastXChosen();
5916 break;
5917
5918 case Message::Copy:
5919 Copy();
5920 break;
5921
5922 case Message::CopyAllowLine:
5923 CopyAllowLine();
5924 break;
5925
5926 case Message::VerticalCentreCaret:
5927 VerticalCentreCaret();
5928 break;
5929
5930 case Message::MoveSelectedLinesUp:
5931 MoveSelectedLinesUp();
5932 break;
5933
5934 case Message::MoveSelectedLinesDown:
5935 MoveSelectedLinesDown();
5936 break;
5937
5938 case Message::CopyRange:
5939 CopyRangeToClipboard(PositionFromUPtr(wParam), lParam);
5940 break;
5941
5942 case Message::CopyText:
5943 CopyText(wParam, ConstCharPtrFromSPtr(lParam));
5944 break;
5945
5946 case Message::Paste:
5947 Paste();
5948 if ((caretSticky == CaretSticky::Off) || (caretSticky == CaretSticky::WhiteSpace)) {
5949 SetLastXChosen();
5950 }
5951 EnsureCaretVisible();
5952 break;
5953
5954 case Message::ReplaceRectangular: {
5955 UndoGroup ug(pdoc);
5956 if (!sel.Empty()) {
5957 ClearSelection(); // want to replace rectangular selection contents
5958 }
5959 InsertPasteShape(ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam), PasteShape::rectangular);
5960 break;
5961 }
5962
5963 case Message::Clear:
5964 Clear();
5965 SetLastXChosen();
5966 EnsureCaretVisible();
5967 break;
5968
5969 case Message::Undo:
5970 Undo();
5971 SetLastXChosen();
5972 break;
5973
5974 case Message::CanUndo:
5975 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
5976
5977 case Message::EmptyUndoBuffer:
5978 pdoc->DeleteUndoHistory();
5979 return 0;
5980
5981 case Message::GetFirstVisibleLine:
5982 return topLine;
5983
5984 case Message::SetFirstVisibleLine:
5985 ScrollTo(LineFromUPtr(wParam));
5986 break;
5987
5988 case Message::GetLine: { // Risk of overwriting the end of the buffer
5989 const Sci::Position lineStart =
5990 pdoc->LineStart(LineFromUPtr(wParam));
5991 const Sci::Position lineEnd =
5992 pdoc->LineStart(LineFromUPtr(wParam + 1));
5993 // not NUL terminated
5994 const Sci::Position len = lineEnd - lineStart;
5995 if (lParam == 0) {
5996 return len;
5997 }
5998 char *ptr = CharPtrFromSPtr(lParam);
5999 pdoc->GetCharRange(ptr, lineStart, len);
6000 return len;
6001 }
6002
6003 case Message::GetLineCount:
6004 if (pdoc->LinesTotal() == 0)
6005 return 1;
6006 else
6007 return pdoc->LinesTotal();
6008
6009 case Message::AllocateLines:
6010 pdoc->AllocateLines(wParam);
6011 break;
6012
6013 case Message::GetModify:
6014 return !pdoc->IsSavePoint();
6015
6016 case Message::SetSel: {
6017 Sci::Position nStart = PositionFromUPtr(wParam);
6018 Sci::Position nEnd = lParam;
6019 if (nEnd < 0)
6020 nEnd = pdoc->Length();
6021 if (nStart < 0)
6022 nStart = nEnd; // Remove selection
6023 InvalidateSelection(SelectionRange(nStart, nEnd));
6024 sel.Clear();
6025 sel.selType = Selection::SelTypes::stream;
6026 SetSelection(nEnd, nStart);
6027 EnsureCaretVisible();
6028 }
6029 break;
6030
6031 case Message::GetSelText: {
6032 SelectionText selectedText;
6033 CopySelectionRange(&selectedText);
6034 if (lParam) {
6035 char *ptr = CharPtrFromSPtr(lParam);
6036 size_t iChar = selectedText.Length();
6037 if (iChar) {
6038 memcpy(ptr, selectedText.Data(), iChar);
6039 }
6040 ptr[iChar] = '\0';
6041 }
6042 return selectedText.Length();
6043 }
6044
6045 case Message::LineFromPosition:
6046 if (PositionFromUPtr(wParam) < 0)
6047 return 0;
6048 return pdoc->LineFromPosition(PositionFromUPtr(wParam));
6049
6050 case Message::PositionFromLine:
6051 if (LineFromUPtr(wParam) < 0)
6052 wParam = pdoc->LineFromPosition(SelectionStart().Position());
6053 if (wParam == 0)
6054 return 0; // Even if there is no text, there is a first line that starts at 0
6055 if (LineFromUPtr(wParam) > pdoc->LinesTotal())
6056 return -1;
6057 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
6058 // return -1;
6059 return pdoc->LineStart(LineFromUPtr(wParam));
6060
6061 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
6062 case Message::LineLength:
6063 if ((LineFromUPtr(wParam) < 0) ||
6064 (LineFromUPtr(wParam) > pdoc->LineFromPosition(pdoc->Length())))
6065 return 0;
6066 return pdoc->LineStart(LineFromUPtr(wParam) + 1) - pdoc->LineStart(LineFromUPtr(wParam));
6067
6068 case Message::ReplaceSel: {
6069 if (lParam == 0)
6070 return 0;
6071 UndoGroup ug(pdoc);
6072 ClearSelection();
6073 const char *replacement = ConstCharPtrFromSPtr(lParam);
6074 const Sci::Position lengthInserted = pdoc->InsertString(
6075 sel.MainCaret(), replacement, strlen(replacement));
6076 SetEmptySelection(sel.MainCaret() + lengthInserted);
6077 SetLastXChosen();
6078 EnsureCaretVisible();
6079 }
6080 break;
6081
6082 case Message::SetTargetStart:
6083 targetRange.start.SetPosition(PositionFromUPtr(wParam));
6084 break;
6085
6086 case Message::GetTargetStart:
6087 return targetRange.start.Position();
6088
6089 case Message::SetTargetStartVirtualSpace:
6090 targetRange.start.SetVirtualSpace(PositionFromUPtr(wParam));
6091 break;
6092
6093 case Message::GetTargetStartVirtualSpace:
6094 return targetRange.start.VirtualSpace();
6095
6096 case Message::SetTargetEnd:
6097 targetRange.end.SetPosition(PositionFromUPtr(wParam));
6098 break;
6099
6100 case Message::GetTargetEnd:
6101 return targetRange.end.Position();
6102
6103 case Message::SetTargetEndVirtualSpace:
6104 targetRange.end.SetVirtualSpace(PositionFromUPtr(wParam));
6105 break;
6106
6107 case Message::GetTargetEndVirtualSpace:
6108 return targetRange.end.VirtualSpace();
6109
6110 case Message::SetTargetRange:
6111 targetRange.start.SetPosition(PositionFromUPtr(wParam));
6112 targetRange.end.SetPosition(lParam);
6113 break;
6114
6115 case Message::TargetWholeDocument:
6116 targetRange.start.SetPosition(0);
6117 targetRange.end.SetPosition(pdoc->Length());
6118 break;
6119
6120 case Message::TargetFromSelection:
6121 targetRange.start = sel.RangeMain().Start();
6122 targetRange.end = sel.RangeMain().End();
6123 break;
6124
6125 case Message::GetTargetText: {
6126 std::string text = RangeText(targetRange.start.Position(), targetRange.end.Position());
6127 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(text.c_str()), text.length());
6128 }
6129
6130 case Message::ReplaceTarget:
6131 PLATFORM_ASSERT(lParam);
6132 return ReplaceTarget(false, ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));
6133
6134 case Message::ReplaceTargetRE:
6135 PLATFORM_ASSERT(lParam);
6136 return ReplaceTarget(true, ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));
6137
6138 case Message::SearchInTarget:
6139 PLATFORM_ASSERT(lParam);
6140 return SearchInTarget(ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));
6141
6142 case Message::SetSearchFlags:
6143 searchFlags = static_cast<FindOption>(wParam);
6144 break;
6145
6146 case Message::GetSearchFlags:
6147 return static_cast<sptr_t>(searchFlags);
6148
6149 case Message::GetTag:
6150 return GetTag(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
6151
6152 case Message::PositionBefore:
6153 return pdoc->MovePositionOutsideChar(PositionFromUPtr(wParam) - 1, -1, true);
6154
6155 case Message::PositionAfter:
6156 return pdoc->MovePositionOutsideChar(PositionFromUPtr(wParam) + 1, 1, true);
6157
6158 case Message::PositionRelative:
6159 return std::clamp<Sci::Position>(pdoc->GetRelativePosition(
6160 PositionFromUPtr(wParam), lParam),
6161 0, pdoc->Length());
6162
6163 case Message::PositionRelativeCodeUnits:
6164 return std::clamp<Sci::Position>(pdoc->GetRelativePositionUTF16(
6165 PositionFromUPtr(wParam), lParam),
6166 0, pdoc->Length());
6167
6168 case Message::LineScroll:
6169 ScrollTo(topLine + lParam);
6170 HorizontalScrollTo(xOffset + static_cast<int>(wParam) * static_cast<int>(vs.spaceWidth));
6171 return 1;
6172
6173 case Message::SetXOffset:
6174 xOffset = static_cast<int>(wParam);
6175 ContainerNeedsUpdate(Update::HScroll);
6176 SetHorizontalScrollPos();
6177 Redraw();
6178 break;
6179
6180 case Message::GetXOffset:
6181 return xOffset;
6182
6183 case Message::ChooseCaretX:
6184 SetLastXChosen();
6185 break;
6186
6187 case Message::ScrollCaret:
6188 EnsureCaretVisible();
6189 break;
6190
6191 case Message::SetReadOnly:
6192 pdoc->SetReadOnly(wParam != 0);
6193 return 1;
6194
6195 case Message::GetReadOnly:
6196 return pdoc->IsReadOnly();
6197
6198 case Message::CanPaste:
6199 return CanPaste();
6200
6201 case Message::PointXFromPosition:
6202 if (lParam < 0) {
6203 return 0;
6204 } else {
6205 const Point pt = LocationFromPosition(lParam);
6206 // Convert to view-relative
6207 return static_cast<int>(pt.x) - vs.textStart + vs.fixedColumnWidth;
6208 }
6209
6210 case Message::PointYFromPosition:
6211 if (lParam < 0) {
6212 return 0;
6213 } else {
6214 const Point pt = LocationFromPosition(lParam);
6215 return static_cast<int>(pt.y);
6216 }
6217
6218 case Message::FindText:
6219 return FindText(wParam, lParam);
6220
6221 case Message::GetTextRange: {
6222 if (lParam == 0)
6223 return 0;
6224 TextRange *tr = static_cast<TextRange *>(PtrFromSPtr(lParam));
6225 Sci::Position cpMax = static_cast<Sci::Position>(tr->chrg.cpMax);
6226 if (cpMax == -1)
6227 cpMax = pdoc->Length();
6228 PLATFORM_ASSERT(cpMax <= pdoc->Length());
6229 Sci::Position len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
6230 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
6231 // Spec says copied text is terminated with a NUL
6232 tr->lpstrText[len] = '\0';
6233 return len; // Not including NUL
6234 }
6235
6236 case Message::HideSelection:
6237 view.hideSelection = wParam != 0;
6238 Redraw();
6239 break;
6240
6241 case Message::FormatRange:
6242 return FormatRange(wParam != 0, static_cast<RangeToFormat *>(PtrFromSPtr(lParam)));
6243
6244 case Message::GetMarginLeft:
6245 return vs.leftMarginWidth;
6246
6247 case Message::GetMarginRight:
6248 return vs.rightMarginWidth;
6249
6250 case Message::SetMarginLeft:
6251 lastXChosen += static_cast<int>(lParam) - vs.leftMarginWidth;
6252 vs.leftMarginWidth = static_cast<int>(lParam);
6253 InvalidateStyleRedraw();
6254 break;
6255
6256 case Message::SetMarginRight:
6257 vs.rightMarginWidth = static_cast<int>(lParam);
6258 InvalidateStyleRedraw();
6259 break;
6260
6261 // Control specific messages
6262
6263 case Message::AddText: {
6264 if (lParam == 0)
6265 return 0;
6266 const Sci::Position lengthInserted = pdoc->InsertString(
6267 CurrentPosition(), ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));
6268 SetEmptySelection(sel.MainCaret() + lengthInserted);
6269 return 0;
6270 }
6271
6272 case Message::AddStyledText:
6273 if (lParam)
6274 AddStyledText(ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));
6275 return 0;
6276
6277 case Message::InsertText: {
6278 if (lParam == 0)
6279 return 0;
6280 Sci::Position insertPos = PositionFromUPtr(wParam);
6281 if (insertPos == -1)
6282 insertPos = CurrentPosition();
6283 Sci::Position newCurrent = CurrentPosition();
6284 const char *sz = ConstCharPtrFromSPtr(lParam);
6285 const Sci::Position lengthInserted = pdoc->InsertString(insertPos, sz, strlen(sz));
6286 if (newCurrent > insertPos)
6287 newCurrent += lengthInserted;
6288 SetEmptySelection(newCurrent);
6289 return 0;
6290 }
6291
6292 case Message::ChangeInsertion:
6293 PLATFORM_ASSERT(lParam);
6294 pdoc->ChangeInsertion(ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));
6295 return 0;
6296
6297 case Message::AppendText:
6298 pdoc->InsertString(pdoc->Length(),
6299 ConstCharPtrFromSPtr(lParam), PositionFromUPtr(wParam));
6300 return 0;
6301
6302 case Message::ClearAll:
6303 ClearAll();
6304 return 0;
6305
6306 case Message::DeleteRange:
6307 pdoc->DeleteChars(PositionFromUPtr(wParam), lParam);
6308 return 0;
6309
6310 case Message::ClearDocumentStyle:
6311 ClearDocumentStyle();
6312 return 0;
6313
6314 case Message::SetUndoCollection:
6315 pdoc->SetUndoCollection(wParam != 0);
6316 return 0;
6317
6318 case Message::GetUndoCollection:
6319 return pdoc->IsCollectingUndo();
6320
6321 case Message::BeginUndoAction:
6322 pdoc->BeginUndoAction();
6323 return 0;
6324
6325 case Message::EndUndoAction:
6326 pdoc->EndUndoAction();
6327 return 0;
6328
6329 case Message::GetCaretPeriod:
6330 return caret.period;
6331
6332 case Message::SetCaretPeriod:
6333 CaretSetPeriod(static_cast<int>(wParam));
6334 break;
6335
6336 case Message::GetWordChars:
6337 return pdoc->GetCharsOfClass(CharacterClass::word, UCharPtrFromSPtr(lParam));
6338
6339 case Message::SetWordChars: {
6340 pdoc->SetDefaultCharClasses(false);
6341 if (lParam == 0)
6342 return 0;
6343 pdoc->SetCharClasses(ConstUCharPtrFromSPtr(lParam), CharacterClass::word);
6344 }
6345 break;
6346
6347 case Message::GetWhitespaceChars:
6348 return pdoc->GetCharsOfClass(CharacterClass::space, UCharPtrFromSPtr(lParam));
6349
6350 case Message::SetWhitespaceChars: {
6351 if (lParam == 0)
6352 return 0;
6353 pdoc->SetCharClasses(ConstUCharPtrFromSPtr(lParam), CharacterClass::space);
6354 }
6355 break;
6356
6357 case Message::GetPunctuationChars:
6358 return pdoc->GetCharsOfClass(CharacterClass::punctuation, UCharPtrFromSPtr(lParam));
6359
6360 case Message::SetPunctuationChars: {
6361 if (lParam == 0)
6362 return 0;
6363 pdoc->SetCharClasses(ConstUCharPtrFromSPtr(lParam), CharacterClass::punctuation);
6364 }
6365 break;
6366
6367 case Message::SetCharsDefault:
6368 pdoc->SetDefaultCharClasses(true);
6369 break;
6370
6371 case Message::SetCharacterCategoryOptimization:
6372 pdoc->SetCharacterCategoryOptimization(static_cast<int>(wParam));
6373 break;
6374
6375 case Message::GetCharacterCategoryOptimization:
6376 return pdoc->CharacterCategoryOptimization();
6377
6378 case Message::GetLength:
6379 return pdoc->Length();
6380
6381 case Message::Allocate:
6382 pdoc->Allocate(PositionFromUPtr(wParam));
6383 break;
6384
6385 case Message::GetCharAt:
6386 return pdoc->CharAt(PositionFromUPtr(wParam));
6387
6388 case Message::SetCurrentPos:
6389 if (sel.IsRectangular()) {
6390 sel.Rectangular().caret.SetPosition(PositionFromUPtr(wParam));
6391 SetRectangularRange();
6392 Redraw();
6393 } else {
6394 SetSelection(PositionFromUPtr(wParam), sel.MainAnchor());
6395 }
6396 break;
6397
6398 case Message::GetCurrentPos:
6399 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
6400
6401 case Message::SetAnchor:
6402 if (sel.IsRectangular()) {
6403 sel.Rectangular().anchor.SetPosition(PositionFromUPtr(wParam));
6404 SetRectangularRange();
6405 Redraw();
6406 } else {
6407 SetSelection(sel.MainCaret(), PositionFromUPtr(wParam));
6408 }
6409 break;
6410
6411 case Message::GetAnchor:
6412 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
6413
6414 case Message::SetSelectionStart:
6415 SetSelection(std::max(sel.MainCaret(), PositionFromUPtr(wParam)), PositionFromUPtr(wParam));
6416 break;
6417
6418 case Message::GetSelectionStart:
6419 return sel.LimitsForRectangularElseMain().start.Position();
6420
6421 case Message::SetSelectionEnd:
6422 SetSelection(PositionFromUPtr(wParam), std::min(sel.MainAnchor(), PositionFromUPtr(wParam)));
6423 break;
6424
6425 case Message::GetSelectionEnd:
6426 return sel.LimitsForRectangularElseMain().end.Position();
6427
6428 case Message::SetEmptySelection:
6429 SetEmptySelection(PositionFromUPtr(wParam));
6430 break;
6431
6432 case Message::SetPrintMagnification:
6433 view.printParameters.magnification = static_cast<int>(wParam);
6434 break;
6435
6436 case Message::GetPrintMagnification:
6437 return view.printParameters.magnification;
6438
6439 case Message::SetPrintColourMode:
6440 view.printParameters.colourMode = static_cast<PrintOption>(wParam);
6441 break;
6442
6443 case Message::GetPrintColourMode:
6444 return static_cast<sptr_t>(view.printParameters.colourMode);
6445
6446 case Message::SetPrintWrapMode:
6447 view.printParameters.wrapState = (static_cast<Wrap>(wParam) == Wrap::Word) ? Wrap::Word : Wrap::None;
6448 break;
6449
6450 case Message::GetPrintWrapMode:
6451 return static_cast<sptr_t>(view.printParameters.wrapState);
6452
6453 case Message::GetStyleAt:
6454 if (PositionFromUPtr(wParam) >= pdoc->Length())
6455 return 0;
6456 else
6457 return pdoc->StyleAt(PositionFromUPtr(wParam));
6458
6459 case Message::Redo:
6460 Redo();
6461 break;
6462
6463 case Message::SelectAll:
6464 SelectAll();
6465 break;
6466
6467 case Message::SetSavePoint:
6468 pdoc->SetSavePoint();
6469 break;
6470
6471 case Message::GetStyledText: {
6472 if (lParam == 0)
6473 return 0;
6474 TextRange *tr = static_cast<TextRange *>(PtrFromSPtr(lParam));
6475 Sci::Position iPlace = 0;
6476 for (Sci::Position iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
6477 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
6478 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
6479 }
6480 tr->lpstrText[iPlace] = '\0';
6481 tr->lpstrText[iPlace + 1] = '\0';
6482 return iPlace;
6483 }
6484
6485 case Message::CanRedo:
6486 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
6487
6488 case Message::MarkerLineFromHandle:
6489 return pdoc->LineFromHandle(static_cast<int>(wParam));
6490
6491 case Message::MarkerDeleteHandle:
6492 pdoc->DeleteMarkFromHandle(static_cast<int>(wParam));
6493 break;
6494
6495 case Message::MarkerHandleFromLine:
6496 return pdoc->MarkerHandleFromLine(LineFromUPtr(wParam), static_cast<int>(lParam));
6497
6498 case Message::MarkerNumberFromLine:
6499 return pdoc->MarkerNumberFromLine(LineFromUPtr(wParam), static_cast<int>(lParam));
6500
6501 case Message::GetViewWS:
6502 return static_cast<sptr_t>(vs.viewWhitespace);
6503
6504 case Message::SetViewWS:
6505 vs.viewWhitespace = static_cast<WhiteSpace>(wParam);
6506 Redraw();
6507 break;
6508
6509 case Message::GetTabDrawMode:
6510 return static_cast<sptr_t>(vs.tabDrawMode);
6511
6512 case Message::SetTabDrawMode:
6513 vs.tabDrawMode = static_cast<TabDrawMode>(wParam);
6514 Redraw();
6515 break;
6516
6517 case Message::GetWhitespaceSize:
6518 return vs.whitespaceSize;
6519
6520 case Message::SetWhitespaceSize:
6521 vs.whitespaceSize = static_cast<int>(wParam);
6522 Redraw();
6523 break;
6524
6525 case Message::PositionFromPoint:
6526 return PositionFromLocation(PointFromParameters(wParam, lParam), false, false);
6527
6528 case Message::PositionFromPointClose:
6529 return PositionFromLocation(PointFromParameters(wParam, lParam), true, false);
6530
6531 case Message::CharPositionFromPoint:
6532 return PositionFromLocation(PointFromParameters(wParam, lParam), false, true);
6533
6534 case Message::CharPositionFromPointClose:
6535 return PositionFromLocation(PointFromParameters(wParam, lParam), true, true);
6536
6537 case Message::GotoLine:
6538 GoToLine(LineFromUPtr(wParam));
6539 break;
6540
6541 case Message::GotoPos:
6542 SetEmptySelection(PositionFromUPtr(wParam));
6543 EnsureCaretVisible();
6544 break;
6545
6546 case Message::GetCurLine: {
6547 const Sci::Line lineCurrentPos = pdoc->SciLineFromPosition(sel.MainCaret());
6548 const Sci::Position lineStart = pdoc->LineStart(lineCurrentPos);
6549 const Sci::Position lineEnd = pdoc->LineStart(lineCurrentPos + 1);
6550 if (lParam == 0) {
6551 return lineEnd - lineStart;
6552 }
6553 char *ptr = CharPtrFromSPtr(lParam);
6554 const Sci::Position len = std::min<uptr_t>(lineEnd - lineStart, wParam);
6555 pdoc->GetCharRange(ptr, lineStart, len);
6556 ptr[len] = '\0';
6557 return sel.MainCaret() - lineStart;
6558 }
6559
6560 case Message::GetEndStyled:
6561 return pdoc->GetEndStyled();
6562
6563 case Message::GetEOLMode:
6564 return static_cast<sptr_t>(pdoc->eolMode);
6565
6566 case Message::SetEOLMode:
6567 pdoc->eolMode = static_cast<EndOfLine>(wParam);
6568 break;
6569
6570 case Message::SetLineEndTypesAllowed:
6571 if (pdoc->SetLineEndTypesAllowed(static_cast<LineEndType>(wParam))) {
6572 pcs->Clear();
6573 pcs->InsertLines(0, pdoc->LinesTotal() - 1);
6574 SetAnnotationHeights(0, pdoc->LinesTotal());
6575 InvalidateStyleRedraw();
6576 }
6577 break;
6578
6579 case Message::GetLineEndTypesAllowed:
6580 return static_cast<sptr_t>(pdoc->GetLineEndTypesAllowed());
6581
6582 case Message::GetLineEndTypesActive:
6583 return static_cast<sptr_t>(pdoc->GetLineEndTypesActive());
6584
6585 case Message::StartStyling:
6586 pdoc->StartStyling(PositionFromUPtr(wParam));
6587 break;
6588
6589 case Message::SetStyling:
6590 if (PositionFromUPtr(wParam) < 0)
6591 errorStatus = Status::Failure;
6592 else
6593 pdoc->SetStyleFor(PositionFromUPtr(wParam), static_cast<char>(lParam));
6594 break;
6595
6596 case Message::SetStylingEx: // Specify a complete styling buffer
6597 if (lParam == 0)
6598 return 0;
6599 pdoc->SetStyles(PositionFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));
6600 break;
6601
6602 case Message::SetBufferedDraw:
6603 view.bufferedDraw = wParam != 0;
6604 break;
6605
6606 case Message::GetBufferedDraw:
6607 return view.bufferedDraw;
6608
6609#ifdef INCLUDE_DEPRECATED_FEATURES
6610 case SCI_GETTWOPHASEDRAW:
6611 return view.phasesDraw == EditView::phasesTwo;
6612
6613 case SCI_SETTWOPHASEDRAW:
6614 if (view.SetTwoPhaseDraw(wParam != 0))
6615 InvalidateStyleRedraw();
6616 break;
6617#endif
6618
6619 case Message::GetPhasesDraw:
6620 return static_cast<sptr_t>(view.phasesDraw);
6621
6622 case Message::SetPhasesDraw:
6623 if (view.SetPhasesDraw(static_cast<int>(wParam)))
6624 InvalidateStyleRedraw();
6625 break;
6626
6627 case Message::SetFontQuality:
6628 vs.extraFontFlag = static_cast<FontQuality>(
6629 (static_cast<int>(vs.extraFontFlag) & ~static_cast<int>(FontQuality::QualityMask)) |
6630 (wParam & static_cast<int>(FontQuality::QualityMask)));
6631 InvalidateStyleRedraw();
6632 break;
6633
6634 case Message::GetFontQuality:
6635 return static_cast<int>(vs.extraFontFlag) & static_cast<int>(FontQuality::QualityMask);
6636
6637 case Message::SetTabWidth:
6638 if (wParam > 0) {
6639 pdoc->tabInChars = static_cast<int>(wParam);
6640 if (pdoc->indentInChars == 0)
6641 pdoc->actualIndentInChars = pdoc->tabInChars;
6642 }
6643 InvalidateStyleRedraw();
6644 break;
6645
6646 case Message::GetTabWidth:
6647 return pdoc->tabInChars;
6648
6649 case Message::SetTabMinimumWidth:
6650 SetAppearance(view.tabWidthMinimumPixels, static_cast<int>(wParam));
6651 break;
6652
6653 case Message::GetTabMinimumWidth:
6654 return view.tabWidthMinimumPixels;
6655
6656 case Message::ClearTabStops:
6657 if (view.ClearTabstops(LineFromUPtr(wParam))) {
6658 const DocModification mh(ModificationFlags::ChangeTabStops, 0, 0, 0, nullptr, LineFromUPtr(wParam));
6659 NotifyModified(pdoc, mh, nullptr);
6660 }
6661 break;
6662
6663 case Message::AddTabStop:
6664 if (view.AddTabstop(LineFromUPtr(wParam), static_cast<int>(lParam))) {
6665 const DocModification mh(ModificationFlags::ChangeTabStops, 0, 0, 0, nullptr, LineFromUPtr(wParam));
6666 NotifyModified(pdoc, mh, nullptr);
6667 }
6668 break;
6669
6670 case Message::GetNextTabStop:
6671 return view.GetNextTabstop(LineFromUPtr(wParam), static_cast<int>(lParam));
6672
6673 case Message::SetIndent:
6674 pdoc->indentInChars = static_cast<int>(wParam);
6675 if (pdoc->indentInChars != 0)
6676 pdoc->actualIndentInChars = pdoc->indentInChars;
6677 else
6678 pdoc->actualIndentInChars = pdoc->tabInChars;
6679 InvalidateStyleRedraw();
6680 break;
6681
6682 case Message::GetIndent:
6683 return pdoc->indentInChars;
6684
6685 case Message::SetUseTabs:
6686 pdoc->useTabs = wParam != 0;
6687 InvalidateStyleRedraw();
6688 break;
6689
6690 case Message::GetUseTabs:
6691 return pdoc->useTabs;
6692
6693 case Message::SetLineIndentation:
6694 pdoc->SetLineIndentation(LineFromUPtr(wParam), lParam);
6695 break;
6696
6697 case Message::GetLineIndentation:
6698 return pdoc->GetLineIndentation(LineFromUPtr(wParam));
6699
6700 case Message::GetLineIndentPosition:
6701 return pdoc->GetLineIndentPosition(LineFromUPtr(wParam));
6702
6703 case Message::SetTabIndents:
6704 pdoc->tabIndents = wParam != 0;
6705 break;
6706
6707 case Message::GetTabIndents:
6708 return pdoc->tabIndents;
6709
6710 case Message::SetBackSpaceUnIndents:
6711 pdoc->backspaceUnindents = wParam != 0;
6712 break;
6713
6714 case Message::GetBackSpaceUnIndents:
6715 return pdoc->backspaceUnindents;
6716
6717 case Message::SetMouseDwellTime:
6718 dwellDelay = static_cast<int>(wParam);
6719 ticksToDwell = dwellDelay;
6720 break;
6721
6722 case Message::GetMouseDwellTime:
6723 return dwellDelay;
6724
6725 case Message::WordStartPosition:
6726 return pdoc->ExtendWordSelect(PositionFromUPtr(wParam), -1, lParam != 0);
6727
6728 case Message::WordEndPosition:
6729 return pdoc->ExtendWordSelect(PositionFromUPtr(wParam), 1, lParam != 0);
6730
6731 case Message::IsRangeWord:
6732 return pdoc->IsWordAt(PositionFromUPtr(wParam), lParam);
6733
6734 case Message::SetIdleStyling:
6735 idleStyling = static_cast<IdleStyling>(wParam);
6736 break;
6737
6738 case Message::GetIdleStyling:
6739 return static_cast<sptr_t>(idleStyling);
6740
6741 case Message::SetWrapMode:
6742 if (vs.SetWrapState(static_cast<Wrap>(wParam))) {
6743 xOffset = 0;
6744 ContainerNeedsUpdate(Update::HScroll);
6745 InvalidateStyleRedraw();
6746 ReconfigureScrollBars();
6747 }
6748 break;
6749
6750 case Message::GetWrapMode:
6751 return static_cast<sptr_t>(vs.wrap.state);
6752
6753 case Message::SetWrapVisualFlags:
6754 if (vs.SetWrapVisualFlags(static_cast<WrapVisualFlag>(wParam))) {
6755 InvalidateStyleRedraw();
6756 ReconfigureScrollBars();
6757 }
6758 break;
6759
6760 case Message::GetWrapVisualFlags:
6761 return static_cast<sptr_t>(vs.wrap.visualFlags);
6762
6763 case Message::SetWrapVisualFlagsLocation:
6764 if (vs.SetWrapVisualFlagsLocation(static_cast<WrapVisualLocation>(wParam))) {
6765 InvalidateStyleRedraw();
6766 }
6767 break;
6768
6769 case Message::GetWrapVisualFlagsLocation:
6770 return static_cast<sptr_t>(vs.wrap.visualFlagsLocation);
6771
6772 case Message::SetWrapStartIndent:
6773 if (vs.SetWrapVisualStartIndent(static_cast<int>(wParam))) {
6774 InvalidateStyleRedraw();
6775 ReconfigureScrollBars();
6776 }
6777 break;
6778
6779 case Message::GetWrapStartIndent:
6780 return vs.wrap.visualStartIndent;
6781
6782 case Message::SetWrapIndentMode:
6783 if (vs.SetWrapIndentMode(static_cast<WrapIndentMode>(wParam))) {
6784 InvalidateStyleRedraw();
6785 ReconfigureScrollBars();
6786 }
6787 break;
6788
6789 case Message::GetWrapIndentMode:
6790 return static_cast<sptr_t>(vs.wrap.indentMode);
6791
6792 case Message::SetLayoutCache:
6793 if (static_cast<LineCache>(wParam) <= LineCache::Document) {
6794 view.llc.SetLevel(static_cast<LineCache>(wParam));
6795 }
6796 break;
6797
6798 case Message::GetLayoutCache:
6799 return static_cast<sptr_t>(view.llc.GetLevel());
6800
6801 case Message::SetPositionCache:
6802 view.posCache.SetSize(wParam);
6803 break;
6804
6805 case Message::GetPositionCache:
6806 return view.posCache.GetSize();
6807
6808 case Message::SetScrollWidth:
6809 PLATFORM_ASSERT(wParam > 0);
6810 if ((wParam > 0) && (wParam != static_cast<unsigned int>(scrollWidth))) {
6811 view.lineWidthMaxSeen = 0;
6812 scrollWidth = static_cast<int>(wParam);
6813 SetScrollBars();
6814 }
6815 break;
6816
6817 case Message::GetScrollWidth:
6818 return scrollWidth;
6819
6820 case Message::SetScrollWidthTracking:
6821 trackLineWidth = wParam != 0;
6822 break;
6823
6824 case Message::GetScrollWidthTracking:
6825 return trackLineWidth;
6826
6827 case Message::LinesJoin:
6828 LinesJoin();
6829 break;
6830
6831 case Message::LinesSplit:
6832 LinesSplit(static_cast<int>(wParam));
6833 break;
6834
6835 case Message::TextWidth:
6836 PLATFORM_ASSERT(wParam < vs.styles.size());
6837 PLATFORM_ASSERT(lParam);
6838 return TextWidth(wParam, ConstCharPtrFromSPtr(lParam));
6839
6840 case Message::TextHeight:
6841 RefreshStyleData();
6842 return vs.lineHeight;
6843
6844 case Message::SetEndAtLastLine:
6845 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
6846 if (endAtLastLine != (wParam != 0)) {
6847 endAtLastLine = wParam != 0;
6848 SetScrollBars();
6849 }
6850 break;
6851
6852 case Message::GetEndAtLastLine:
6853 return endAtLastLine;
6854
6855 case Message::SetCaretSticky:
6856 PLATFORM_ASSERT(static_cast<CaretSticky>(wParam) <= CaretSticky::WhiteSpace);
6857 if (static_cast<CaretSticky>(wParam) <= CaretSticky::WhiteSpace) {
6858 caretSticky = static_cast<CaretSticky>(wParam);
6859 }
6860 break;
6861
6862 case Message::GetCaretSticky:
6863 return static_cast<sptr_t>(caretSticky);
6864
6865 case Message::ToggleCaretSticky:
6866 caretSticky = (caretSticky == CaretSticky::Off) ? CaretSticky::On : CaretSticky::Off;
6867 break;
6868
6869 case Message::GetColumn:
6870 return pdoc->GetColumn(PositionFromUPtr(wParam));
6871
6872 case Message::FindColumn:
6873 return pdoc->FindColumn(LineFromUPtr(wParam), lParam);
6874
6875 case Message::SetHScrollBar :
6876 if (horizontalScrollBarVisible != (wParam != 0)) {
6877 horizontalScrollBarVisible = wParam != 0;
6878 SetScrollBars();
6879 ReconfigureScrollBars();
6880 }
6881 break;
6882
6883 case Message::GetHScrollBar:
6884 return horizontalScrollBarVisible;
6885
6886 case Message::SetVScrollBar:
6887 if (verticalScrollBarVisible != (wParam != 0)) {
6888 verticalScrollBarVisible = wParam != 0;
6889 SetScrollBars();
6890 ReconfigureScrollBars();
6891 if (verticalScrollBarVisible)
6892 SetVerticalScrollPos();
6893 }
6894 break;
6895
6896 case Message::GetVScrollBar:
6897 return verticalScrollBarVisible;
6898
6899 case Message::SetIndentationGuides:
6900 vs.viewIndentationGuides = static_cast<IndentView>(wParam);
6901 Redraw();
6902 break;
6903
6904 case Message::GetIndentationGuides:
6905 return static_cast<sptr_t>(vs.viewIndentationGuides);
6906
6907 case Message::SetHighlightGuide:
6908 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
6909 highlightGuideColumn = static_cast<int>(wParam);
6910 Redraw();
6911 }
6912 break;
6913
6914 case Message::GetHighlightGuide:
6915 return highlightGuideColumn;
6916
6917 case Message::GetLineEndPosition:
6918 return pdoc->LineEnd(LineFromUPtr(wParam));
6919
6920 case Message::SetCodePage:
6921 if (ValidCodePage(static_cast<int>(wParam))) {
6922 if (pdoc->SetDBCSCodePage(static_cast<int>(wParam))) {
6923 pcs->Clear();
6924 pcs->InsertLines(0, pdoc->LinesTotal() - 1);
6925 SetAnnotationHeights(0, pdoc->LinesTotal());
6926 InvalidateStyleRedraw();
6927 SetRepresentations();
6928 }
6929 }
6930 break;
6931
6932 case Message::GetCodePage:
6933 return pdoc->dbcsCodePage;
6934
6935 case Message::SetIMEInteraction:
6936 imeInteraction = static_cast<IMEInteraction>(wParam);
6937 break;
6938
6939 case Message::GetIMEInteraction:
6940 return static_cast<sptr_t>(imeInteraction);
6941
6942 case Message::SetBidirectional:
6943 // Message::SetBidirectional is implemented on platform subclasses if they support bidirectional text.
6944 break;
6945
6946 case Message::GetBidirectional:
6947 return static_cast<sptr_t>(bidirectional);
6948
6949 case Message::GetLineCharacterIndex:
6950 return static_cast<sptr_t>(pdoc->LineCharacterIndex());
6951
6952 case Message::AllocateLineCharacterIndex:
6953 pdoc->AllocateLineCharacterIndex(static_cast<LineCharacterIndexType>(wParam));
6954 break;
6955
6956 case Message::ReleaseLineCharacterIndex:
6957 pdoc->ReleaseLineCharacterIndex(static_cast<LineCharacterIndexType>(wParam));
6958 break;
6959
6960 case Message::LineFromIndexPosition:
6961 return pdoc->LineFromPositionIndex(PositionFromUPtr(wParam), static_cast<LineCharacterIndexType>(lParam));
6962
6963 case Message::IndexPositionFromLine:
6964 return pdoc->IndexLineStart(LineFromUPtr(wParam), static_cast<LineCharacterIndexType>(lParam));
6965
6966 // Marker definition and setting
6967 case Message::MarkerDefine:
6968 if (wParam <= MarkerMax) {
6969 vs.markers[wParam].markType = static_cast<MarkerSymbol>(lParam);
6970 vs.CalcLargestMarkerHeight();
6971 }
6972 InvalidateStyleData();
6973 RedrawSelMargin();
6974 break;
6975
6976 case Message::MarkerSymbolDefined:
6977 if (wParam <= MarkerMax)
6978 return static_cast<sptr_t>(vs.markers[wParam].markType);
6979 else
6980 return 0;
6981
6982 case Message::MarkerSetFore:
6983 if (wParam <= MarkerMax)
6984 vs.markers[wParam].fore = ColourRGBA::FromIpRGB(lParam);
6985 InvalidateStyleData();
6986 RedrawSelMargin();
6987 break;
6988 case Message::MarkerSetBack:
6989 if (wParam <= MarkerMax)
6990 vs.markers[wParam].back = ColourRGBA::FromIpRGB(lParam);
6991 InvalidateStyleData();
6992 RedrawSelMargin();
6993 break;
6994 case Message::MarkerSetBackSelected:
6995 if (wParam <= MarkerMax)
6996 vs.markers[wParam].backSelected = ColourRGBA::FromIpRGB(lParam);
6997 InvalidateStyleData();
6998 RedrawSelMargin();
6999 break;
7000 case Message::MarkerSetForeTranslucent:
7001 if (wParam <= MarkerMax)
7002 vs.markers[wParam].fore = ColourRGBA(static_cast<int>(lParam));
7003 InvalidateStyleData();
7004 RedrawSelMargin();
7005 break;
7006 case Message::MarkerSetBackTranslucent:
7007 if (wParam <= MarkerMax)
7008 vs.markers[wParam].back = ColourRGBA(static_cast<int>(lParam));
7009 InvalidateStyleData();
7010 RedrawSelMargin();
7011 break;
7012 case Message::MarkerSetBackSelectedTranslucent:
7013 if (wParam <= MarkerMax)
7014 vs.markers[wParam].backSelected = ColourRGBA(static_cast<int>(lParam));
7015 InvalidateStyleData();
7016 RedrawSelMargin();
7017 break;
7018 case Message::MarkerSetStrokeWidth:
7019 if (wParam <= MarkerMax)
7020 vs.markers[wParam].strokeWidth = lParam / 100.0f;
7021 InvalidateStyleData();
7022 RedrawSelMargin();
7023 break;
7024 case Message::MarkerEnableHighlight:
7025 marginView.highlightDelimiter.isEnabled = wParam == 1;
7026 RedrawSelMargin();
7027 break;
7028 case Message::MarkerSetAlpha:
7029 if (wParam <= MarkerMax) {
7030 if (static_cast<Alpha>(lParam) == Alpha::NoAlpha) {
7031 SetAppearance(vs.markers[wParam].alpha, Alpha::Opaque);
7032 SetAppearance(vs.markers[wParam].layer, Layer::Base);
7033 } else {
7034 SetAppearance(vs.markers[wParam].alpha, static_cast<Alpha>(lParam));
7035 SetAppearance(vs.markers[wParam].layer, Layer::OverText);
7036 }
7037 }
7038 break;
7039 case Message::MarkerSetLayer:
7040 if (wParam <= MarkerMax) {
7041 SetAppearance(vs.markers[wParam].layer, static_cast<Layer>(lParam));
7042 }
7043 break;
7044 case Message::MarkerGetLayer:
7045 if (wParam <= MarkerMax) {
7046 return static_cast<sptr_t>(vs.markers[wParam].layer);
7047 }
7048 return 0;
7049 case Message::MarkerAdd: {
7050 const int markerID = pdoc->AddMark(LineFromUPtr(wParam), static_cast<int>(lParam));
7051 return markerID;
7052 }
7053 case Message::MarkerAddSet:
7054 if (lParam != 0)
7055 pdoc->AddMarkSet(LineFromUPtr(wParam), static_cast<int>(lParam));
7056 break;
7057
7058 case Message::MarkerDelete:
7059 pdoc->DeleteMark(LineFromUPtr(wParam), static_cast<int>(lParam));
7060 break;
7061
7062 case Message::MarkerDeleteAll:
7063 pdoc->DeleteAllMarks(static_cast<int>(wParam));
7064 break;
7065
7066 case Message::MarkerGet:
7067 return pdoc->GetMark(LineFromUPtr(wParam));
7068
7069 case Message::MarkerNext:
7070 return pdoc->MarkerNext(LineFromUPtr(wParam), static_cast<int>(lParam));
7071
7072 case Message::MarkerPrevious: {
7073 for (Sci::Line iLine = LineFromUPtr(wParam); iLine >= 0; iLine--) {
7074 if ((pdoc->GetMark(iLine) & lParam) != 0)
7075 return iLine;
7076 }
7077 }
7078 return -1;
7079
7080 case Message::MarkerDefinePixmap:
7081 if (wParam <= MarkerMax) {
7082 vs.markers[wParam].SetXPM(ConstCharPtrFromSPtr(lParam));
7083 vs.CalcLargestMarkerHeight();
7084 }
7085 InvalidateStyleData();
7086 RedrawSelMargin();
7087 break;
7088
7089 case Message::RGBAImageSetWidth:
7090 sizeRGBAImage.x = static_cast<XYPOSITION>(wParam);
7091 break;
7092
7093 case Message::RGBAImageSetHeight:
7094 sizeRGBAImage.y = static_cast<XYPOSITION>(wParam);
7095 break;
7096
7097 case Message::RGBAImageSetScale:
7098 scaleRGBAImage = static_cast<float>(wParam);
7099 break;
7100
7101 case Message::MarkerDefineRGBAImage:
7102 if (wParam <= MarkerMax) {
7103 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0f, ConstUCharPtrFromSPtr(lParam));
7104 vs.CalcLargestMarkerHeight();
7105 }
7106 InvalidateStyleData();
7107 RedrawSelMargin();
7108 break;
7109
7110 case Message::SetMarginTypeN:
7111 if (ValidMargin(wParam)) {
7112 vs.ms[wParam].style = static_cast<MarginType>(lParam);
7113 InvalidateStyleRedraw();
7114 }
7115 break;
7116
7117 case Message::GetMarginTypeN:
7118 if (ValidMargin(wParam))
7119 return static_cast<sptr_t>(vs.ms[wParam].style);
7120 else
7121 return 0;
7122
7123 case Message::SetMarginWidthN:
7124 if (ValidMargin(wParam)) {
7125 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
7126 if (vs.ms[wParam].width != lParam) {
7127 lastXChosen += static_cast<int>(lParam) - vs.ms[wParam].width;
7128 vs.ms[wParam].width = static_cast<int>(lParam);
7129 InvalidateStyleRedraw();
7130 }
7131 }
7132 break;
7133
7134 case Message::GetMarginWidthN:
7135 if (ValidMargin(wParam))
7136 return vs.ms[wParam].width;
7137 else
7138 return 0;
7139
7140 case Message::SetMarginMaskN:
7141 if (ValidMargin(wParam)) {
7142 vs.ms[wParam].mask = static_cast<int>(lParam);
7143 InvalidateStyleRedraw();
7144 }
7145 break;
7146
7147 case Message::GetMarginMaskN:
7148 if (ValidMargin(wParam))
7149 return vs.ms[wParam].mask;
7150 else
7151 return 0;
7152
7153 case Message::SetMarginSensitiveN:
7154 if (ValidMargin(wParam)) {
7155 vs.ms[wParam].sensitive = lParam != 0;
7156 InvalidateStyleRedraw();
7157 }
7158 break;
7159
7160 case Message::GetMarginSensitiveN:
7161 if (ValidMargin(wParam))
7162 return vs.ms[wParam].sensitive ? 1 : 0;
7163 else
7164 return 0;
7165
7166 case Message::SetMarginCursorN:
7167 if (ValidMargin(wParam))
7168 vs.ms[wParam].cursor = static_cast<CursorShape>(lParam);
7169 break;
7170
7171 case Message::GetMarginCursorN:
7172 if (ValidMargin(wParam))
7173 return static_cast<sptr_t>(vs.ms[wParam].cursor);
7174 else
7175 return 0;
7176
7177 case Message::SetMarginBackN:
7178 if (ValidMargin(wParam)) {
7179 vs.ms[wParam].back = ColourRGBA::FromIpRGB(lParam);
7180 InvalidateStyleRedraw();
7181 }
7182 break;
7183
7184 case Message::GetMarginBackN:
7185 if (ValidMargin(wParam))
7186 return vs.ms[wParam].back.OpaqueRGB();
7187 else
7188 return 0;
7189
7190 case Message::SetMargins:
7191 if (wParam < 1000)
7192 vs.ms.resize(wParam);
7193 break;
7194
7195 case Message::GetMargins:
7196 return vs.ms.size();
7197
7198 case Message::StyleClearAll:
7199 vs.ClearStyles();
7200 InvalidateStyleRedraw();
7201 break;
7202
7203 case Message::StyleSetFore:
7204 case Message::StyleSetBack:
7205 case Message::StyleSetBold:
7206 case Message::StyleSetWeight:
7207 case Message::StyleSetItalic:
7208 case Message::StyleSetEOLFilled:
7209 case Message::StyleSetSize:
7210 case Message::StyleSetSizeFractional:
7211 case Message::StyleSetFont:
7212 case Message::StyleSetUnderline:
7213 case Message::StyleSetCase:
7214 case Message::StyleSetCharacterSet:
7215 case Message::StyleSetVisible:
7216 case Message::StyleSetChangeable:
7217 case Message::StyleSetHotSpot:
7218 case Message::StyleSetCheckMonospaced:
7219 StyleSetMessage(iMessage, wParam, lParam);
7220 break;
7221
7222 case Message::StyleGetFore:
7223 case Message::StyleGetBack:
7224 case Message::StyleGetBold:
7225 case Message::StyleGetWeight:
7226 case Message::StyleGetItalic:
7227 case Message::StyleGetEOLFilled:
7228 case Message::StyleGetSize:
7229 case Message::StyleGetSizeFractional:
7230 case Message::StyleGetFont:
7231 case Message::StyleGetUnderline:
7232 case Message::StyleGetCase:
7233 case Message::StyleGetCharacterSet:
7234 case Message::StyleGetVisible:
7235 case Message::StyleGetChangeable:
7236 case Message::StyleGetHotSpot:
7237 case Message::StyleGetCheckMonospaced:
7238 return StyleGetMessage(iMessage, wParam, lParam);
7239
7240 case Message::StyleResetDefault:
7241 vs.ResetDefaultStyle();
7242 InvalidateStyleRedraw();
7243 break;
7244
7245 case Message::SetElementColour:
7246 if (vs.SetElementColour(static_cast<Element>(wParam), ColourRGBA(static_cast<int>(lParam)))) {
7247 InvalidateStyleRedraw();
7248 }
7249 break;
7250
7251 case Message::GetElementColour:
7252 return vs.ElementColour(static_cast<Element>(wParam)).value_or(ColourRGBA()).AsInteger();
7253
7254 case Message::ResetElementColour:
7255 if (vs.ResetElement(static_cast<Element>(wParam))) {
7256 InvalidateStyleRedraw();
7257 }
7258 break;
7259
7260 case Message::GetElementIsSet:
7261 return vs.ElementColour(static_cast<Element>(wParam)).has_value();
7262
7263 case Message::GetElementAllowsTranslucent:
7264 return vs.ElementAllowsTranslucent(static_cast<Element>(wParam));
7265
7266 case Message::GetElementBaseColour:
7267 return vs.elementBaseColours[static_cast<Element>(wParam)].value_or(ColourRGBA()).AsInteger();
7268
7269 case Message::SetFontLocale:
7270 if (lParam) {
7271 vs.SetFontLocaleName(ConstCharPtrFromSPtr(lParam));
7272 InvalidateStyleRedraw();
7273 }
7274 break;
7275
7276 case Message::GetFontLocale:
7277 return StringResult(lParam, vs.localeName.c_str());
7278
7279#ifdef INCLUDE_DEPRECATED_FEATURES
7280 case SCI_SETSTYLEBITS:
7281 vs.EnsureStyle(0xff);
7282 break;
7283
7284 case SCI_GETSTYLEBITS:
7285 return 8;
7286#endif
7287
7288 case Message::SetLineState:
7289 return pdoc->SetLineState(LineFromUPtr(wParam), static_cast<int>(lParam));
7290
7291 case Message::GetLineState:
7292 return pdoc->GetLineState(LineFromUPtr(wParam));
7293
7294 case Message::GetMaxLineState:
7295 return pdoc->GetMaxLineState();
7296
7297 case Message::GetCaretLineVisible:
7298 return vs.ElementColour(Element::CaretLineBack) ? 1 : 0;
7299 case Message::SetCaretLineVisible:
7300 if (wParam) {
7301 if (!vs.elementColours.count(Element::CaretLineBack)) {
7302 vs.elementColours[Element::CaretLineBack] = ColourRGBA(0xFF, 0xFF, 0);
7303 InvalidateStyleRedraw();
7304 }
7305 } else {
7306 if (vs.ResetElement(Element::CaretLineBack)) {
7307 InvalidateStyleRedraw();
7308 }
7309 }
7310 break;
7311 case Message::GetCaretLineVisibleAlways:
7312 return vs.caretLine.alwaysShow;
7313 case Message::SetCaretLineVisibleAlways:
7314 vs.caretLine.alwaysShow = wParam != 0;
7315 InvalidateStyleRedraw();
7316 break;
7317
7318 case Message::GetCaretLineHighlightSubLine:
7319 return vs.caretLine.subLine;
7320 case Message::SetCaretLineHighlightSubLine:
7321 vs.caretLine.subLine = wParam != 0;
7322 InvalidateStyleRedraw();
7323 break;
7324
7325 case Message::GetCaretLineFrame:
7326 return vs.caretLine.frame;
7327 case Message::SetCaretLineFrame:
7328 vs.caretLine.frame = static_cast<int>(wParam);
7329 InvalidateStyleRedraw();
7330 break;
7331 case Message::GetCaretLineBack:
7332 if (vs.ElementColour(Element::CaretLineBack))
7333 return vs.ElementColour(Element::CaretLineBack)->OpaqueRGB();
7334 else
7335 return 0;
7336
7337 case Message::SetCaretLineBack:
7338 vs.SetElementRGB(Element::CaretLineBack, static_cast<int>(wParam));
7339 InvalidateStyleRedraw();
7340 break;
7341
7342 case Message::GetCaretLineLayer:
7343 return static_cast<sptr_t>(vs.caretLine.layer);
7344
7345 case Message::SetCaretLineLayer:
7346 if (vs.caretLine.layer != static_cast<Layer>(wParam)) {
7347 vs.caretLine.layer = static_cast<Layer>(wParam);
7348 UpdateBaseElements();
7349 InvalidateStyleRedraw();
7350 }
7351 break;
7352
7353 case Message::GetCaretLineBackAlpha:
7354 if (vs.caretLine.layer == Layer::Base)
7355 return static_cast<sptr_t>(Alpha::NoAlpha);
7356 return vs.ElementColour(Element::CaretLineBack).value_or(ColourRGBA()).GetAlpha();
7357
7358 case Message::SetCaretLineBackAlpha: {
7359 const Layer layerNew = (static_cast<Alpha>(wParam) == Alpha::NoAlpha) ? Layer::Base : Layer::OverText;
7360 vs.caretLine.layer = layerNew;
7361 if (vs.ElementColour(Element::CaretLineBack)) {
7362 vs.SetElementAlpha(Element::CaretLineBack, static_cast<int>(wParam));
7363 }
7364 InvalidateStyleRedraw();
7365 }
7366 break;
7367
7368 // Folding messages
7369
7370 case Message::VisibleFromDocLine:
7371 return pcs->DisplayFromDoc(LineFromUPtr(wParam));
7372
7373 case Message::DocLineFromVisible:
7374 return pcs->DocFromDisplay(LineFromUPtr(wParam));
7375
7376 case Message::WrapCount:
7377 return WrapCount(LineFromUPtr(wParam));
7378
7379 case Message::SetFoldLevel: {
7380 const int prev = pdoc->SetLevel(LineFromUPtr(wParam), static_cast<int>(lParam));
7381 if (prev != static_cast<int>(lParam))
7382 RedrawSelMargin();
7383 return prev;
7384 }
7385
7386 case Message::GetFoldLevel:
7387 return pdoc->GetLevel(LineFromUPtr(wParam));
7388
7389 case Message::GetLastChild:
7390 return pdoc->GetLastChild(LineFromUPtr(wParam), OptionalFoldLevel(lParam));
7391
7392 case Message::GetFoldParent:
7393 return pdoc->GetFoldParent(LineFromUPtr(wParam));
7394
7395 case Message::ShowLines:
7396 pcs->SetVisible(LineFromUPtr(wParam), lParam, true);
7397 SetScrollBars();
7398 Redraw();
7399 break;
7400
7401 case Message::HideLines:
7402 pcs->SetVisible(LineFromUPtr(wParam), lParam, false);
7403 SetScrollBars();
7404 Redraw();
7405 break;
7406
7407 case Message::GetLineVisible:
7408 return pcs->GetVisible(LineFromUPtr(wParam));
7409
7410 case Message::GetAllLinesVisible:
7411 return pcs->HiddenLines() ? 0 : 1;
7412
7413 case Message::SetFoldExpanded:
7414 SetFoldExpanded(LineFromUPtr(wParam), lParam != 0);
7415 break;
7416
7417 case Message::GetFoldExpanded:
7418 return pcs->GetExpanded(LineFromUPtr(wParam));
7419
7420 case Message::SetAutomaticFold:
7421 foldAutomatic = static_cast<AutomaticFold>(wParam);
7422 break;
7423
7424 case Message::GetAutomaticFold:
7425 return static_cast<sptr_t>(foldAutomatic);
7426
7427 case Message::SetFoldFlags:
7428 foldFlags = static_cast<FoldFlag>(wParam);
7429 Redraw();
7430 break;
7431
7432 case Message::ToggleFoldShowText:
7433 pcs->SetFoldDisplayText(LineFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));
7434 FoldLine(LineFromUPtr(wParam), FoldAction::Toggle);
7435 break;
7436
7437 case Message::FoldDisplayTextSetStyle:
7438 foldDisplayTextStyle = static_cast<FoldDisplayTextStyle>(wParam);
7439 Redraw();
7440 break;
7441
7442 case Message::FoldDisplayTextGetStyle:
7443 return static_cast<sptr_t>(foldDisplayTextStyle);
7444
7445 case Message::SetDefaultFoldDisplayText:
7446 SetDefaultFoldDisplayText(ConstCharPtrFromSPtr(lParam));
7447 Redraw();
7448 break;
7449
7450 case Message::GetDefaultFoldDisplayText:
7451 return StringResult(lParam, GetDefaultFoldDisplayText());
7452
7453 case Message::ToggleFold:
7454 FoldLine(LineFromUPtr(wParam), FoldAction::Toggle);
7455 break;
7456
7457 case Message::FoldLine:
7458 FoldLine(LineFromUPtr(wParam), static_cast<FoldAction>(lParam));
7459 break;
7460
7461 case Message::FoldChildren:
7462 FoldExpand(LineFromUPtr(wParam), static_cast<FoldAction>(lParam), pdoc->GetFoldLevel(LineFromUPtr(wParam)));
7463 break;
7464
7465 case Message::FoldAll:
7466 FoldAll(static_cast<FoldAction>(wParam));
7467 break;
7468
7469 case Message::ExpandChildren:
7470 FoldExpand(LineFromUPtr(wParam), FoldAction::Expand, static_cast<FoldLevel>(lParam));
7471 break;
7472
7473 case Message::ContractedFoldNext:
7474 return ContractedFoldNext(LineFromUPtr(wParam));
7475
7476 case Message::EnsureVisible:
7477 EnsureLineVisible(LineFromUPtr(wParam), false);
7478 break;
7479
7480 case Message::EnsureVisibleEnforcePolicy:
7481 EnsureLineVisible(LineFromUPtr(wParam), true);
7482 break;
7483
7484 case Message::ScrollRange:
7485 ScrollRange(SelectionRange(PositionFromUPtr(wParam), lParam));
7486 break;
7487
7488 case Message::SearchAnchor:
7489 SearchAnchor();
7490 break;
7491
7492 case Message::SearchNext:
7493 case Message::SearchPrev:
7494 return SearchText(iMessage, wParam, lParam);
7495
7496 case Message::SetXCaretPolicy:
7497 caretPolicies.x = CaretPolicySlop(wParam, lParam);
7498 break;
7499
7500 case Message::SetYCaretPolicy:
7501 caretPolicies.y = CaretPolicySlop(wParam, lParam);
7502 break;
7503
7504 case Message::SetVisiblePolicy:
7505 visiblePolicy = VisiblePolicySlop(wParam, lParam);
7506 break;
7507
7508 case Message::LinesOnScreen:
7509 return LinesOnScreen();
7510
7511 case Message::SetSelFore:
7512 vs.elementColours[Element::SelectionText] = OptionalColour(wParam, lParam);
7513 vs.elementColours[Element::SelectionAdditionalText] = OptionalColour(wParam, lParam);
7514 InvalidateStyleRedraw();
7515 break;
7516
7517 case Message::SetSelBack:
7518 if (wParam) {
7519 vs.SetElementRGB(Element::SelectionBack, static_cast<int>(lParam));
7520 vs.SetElementRGB(Element::SelectionAdditionalBack, static_cast<int>(lParam));
7521 } else {
7522 vs.ResetElement(Element::SelectionBack);
7523 vs.ResetElement(Element::SelectionAdditionalBack);
7524 }
7525 InvalidateStyleRedraw();
7526 break;
7527
7528 case Message::SetSelAlpha: {
7529 const Layer layerNew = (static_cast<Alpha>(wParam) == Alpha::NoAlpha) ? Layer::Base : Layer::OverText;
7530 if (vs.selection.layer != layerNew) {
7531 vs.selection.layer = layerNew;
7532 UpdateBaseElements();
7533 }
7534 const int alpha = static_cast<int>(wParam);
7535 vs.SetElementAlpha(Element::SelectionBack, alpha);
7536 vs.SetElementAlpha(Element::SelectionAdditionalBack, alpha);
7537 vs.SetElementAlpha(Element::SelectionSecondaryBack, alpha);
7538 vs.SetElementAlpha(Element::SelectionInactiveBack, alpha);
7539 InvalidateStyleRedraw();
7540 }
7541 break;
7542
7543 case Message::GetSelAlpha:
7544 if (vs.selection.layer == Layer::Base)
7545 return static_cast<sptr_t>(Alpha::NoAlpha);
7546 return vs.ElementColour(Element::SelectionBack)->GetAlpha();
7547
7548 case Message::GetSelEOLFilled:
7549 return vs.selection.eolFilled;
7550
7551 case Message::SetSelEOLFilled:
7552 vs.selection.eolFilled = wParam != 0;
7553 InvalidateStyleRedraw();
7554 break;
7555
7556 case Message::SetWhitespaceFore:
7557 if (vs.SetElementColourOptional(Element::WhiteSpace, wParam, lParam)) {
7558 InvalidateStyleRedraw();
7559 }
7560 break;
7561
7562 case Message::SetWhitespaceBack:
7563 if (vs.SetElementColourOptional(Element::WhiteSpaceBack, wParam, lParam)) {
7564 InvalidateStyleRedraw();
7565 }
7566 break;
7567
7568 case Message::SetSelectionLayer:
7569 if (vs.selection.layer != static_cast<Layer>(wParam)) {
7570 vs.selection.layer = static_cast<Layer>(wParam);
7571 UpdateBaseElements();
7572 InvalidateStyleRedraw();
7573 }
7574 break;
7575
7576 case Message::GetSelectionLayer:
7577 return static_cast<sptr_t>(vs.selection.layer);
7578
7579 case Message::SetCaretFore:
7580 vs.elementColours[Element::Caret] = ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam));
7581 InvalidateStyleRedraw();
7582 break;
7583
7584 case Message::GetCaretFore:
7585 return vs.ElementColour(Element::Caret)->OpaqueRGB();
7586
7587 case Message::SetCaretStyle:
7588 if (static_cast<CaretStyle>(wParam) <= (CaretStyle::Block | CaretStyle::OverstrikeBlock | CaretStyle::Curses | CaretStyle::BlockAfter))
7589 vs.caret.style = static_cast<CaretStyle>(wParam);
7590 else
7591 /* Default to the line caret */
7592 vs.caret.style = CaretStyle::Line;
7593 InvalidateStyleRedraw();
7594 break;
7595
7596 case Message::GetCaretStyle:
7597 return static_cast<sptr_t>(vs.caret.style);
7598
7599 case Message::SetCaretWidth:
7600 vs.caret.width = std::clamp(static_cast<int>(wParam), 0, 20);
7601 InvalidateStyleRedraw();
7602 break;
7603
7604 case Message::GetCaretWidth:
7605 return vs.caret.width;
7606
7607 case Message::AssignCmdKey:
7608 kmap.AssignCmdKey(static_cast<Keys>(LowShortFromWParam(wParam)),
7609 static_cast<KeyMod>(HighShortFromWParam(wParam)), static_cast<Message>(lParam));
7610 break;
7611
7612 case Message::ClearCmdKey:
7613 kmap.AssignCmdKey(static_cast<Keys>(LowShortFromWParam(wParam)),
7614 static_cast<KeyMod>(HighShortFromWParam(wParam)), Message::Null);
7615 break;
7616
7617 case Message::ClearAllCmdKeys:
7618 kmap.Clear();
7619 break;
7620
7621 case Message::IndicSetStyle:
7622 if (wParam <= IndicatorMax) {
7623 vs.indicators[wParam].sacNormal.style = static_cast<IndicatorStyle>(lParam);
7624 vs.indicators[wParam].sacHover.style = static_cast<IndicatorStyle>(lParam);
7625 InvalidateStyleRedraw();
7626 }
7627 break;
7628
7629 case Message::IndicGetStyle:
7630 return (wParam <= IndicatorMax) ?
7631 static_cast<sptr_t>(vs.indicators[wParam].sacNormal.style) : 0;
7632
7633 case Message::IndicSetFore:
7634 if (wParam <= IndicatorMax) {
7635 vs.indicators[wParam].sacNormal.fore = ColourRGBA::FromIpRGB(lParam);
7636 vs.indicators[wParam].sacHover.fore = ColourRGBA::FromIpRGB(lParam);
7637 InvalidateStyleRedraw();
7638 }
7639 break;
7640
7641 case Message::IndicGetFore:
7642 return (wParam <= IndicatorMax) ?
7643 vs.indicators[wParam].sacNormal.fore.OpaqueRGB() : 0;
7644
7645 case Message::IndicSetHoverStyle:
7646 if (wParam <= IndicatorMax) {
7647 vs.indicators[wParam].sacHover.style = static_cast<IndicatorStyle>(lParam);
7648 InvalidateStyleRedraw();
7649 }
7650 break;
7651
7652 case Message::IndicGetHoverStyle:
7653 return (wParam <= IndicatorMax) ?
7654 static_cast<sptr_t>(vs.indicators[wParam].sacHover.style) : 0;
7655
7656 case Message::IndicSetHoverFore:
7657 if (wParam <= IndicatorMax) {
7658 vs.indicators[wParam].sacHover.fore = ColourRGBA::FromIpRGB(lParam);
7659 InvalidateStyleRedraw();
7660 }
7661 break;
7662
7663 case Message::IndicGetHoverFore:
7664 return (wParam <= IndicatorMax) ?
7665 vs.indicators[wParam].sacHover.fore.OpaqueRGB() : 0;
7666
7667 case Message::IndicSetFlags:
7668 if (wParam <= IndicatorMax) {
7669 vs.indicators[wParam].SetFlags(static_cast<IndicFlag>(lParam));
7670 InvalidateStyleRedraw();
7671 }
7672 break;
7673
7674 case Message::IndicGetFlags:
7675 return (wParam <= IndicatorMax) ?
7676 static_cast<sptr_t>(vs.indicators[wParam].Flags()) : 0;
7677
7678 case Message::IndicSetUnder:
7679 if (wParam <= IndicatorMax) {
7680 vs.indicators[wParam].under = lParam != 0;
7681 InvalidateStyleRedraw();
7682 }
7683 break;
7684
7685 case Message::IndicGetUnder:
7686 return (wParam <= IndicatorMax) ?
7687 vs.indicators[wParam].under : 0;
7688
7689 case Message::IndicSetAlpha:
7690 if (wParam <= IndicatorMax && lParam >=0 && lParam <= 255) {
7691 vs.indicators[wParam].fillAlpha = static_cast<int>(lParam);
7692 InvalidateStyleRedraw();
7693 }
7694 break;
7695
7696 case Message::IndicGetAlpha:
7697 return (wParam <= IndicatorMax)
7698 ? vs.indicators[wParam].fillAlpha : 0;
7699
7700 case Message::IndicSetOutlineAlpha:
7701 if (wParam <= IndicatorMax && lParam >=0 && lParam <= 255) {
7702 vs.indicators[wParam].outlineAlpha = static_cast<int>(lParam);
7703 InvalidateStyleRedraw();
7704 }
7705 break;
7706
7707 case Message::IndicGetOutlineAlpha:
7708 return (wParam <= IndicatorMax) ? vs.indicators[wParam].outlineAlpha : 0;
7709
7710 case Message::IndicSetStrokeWidth:
7711 if (wParam <= IndicatorMax && lParam >= 0 && lParam <= 1000) {
7712 vs.indicators[wParam].strokeWidth = lParam / 100.0f;
7713 InvalidateStyleRedraw();
7714 }
7715 break;
7716
7717 case Message::IndicGetStrokeWidth:
7718 if (wParam <= IndicatorMax) {
7719 return std::lround(vs.indicators[wParam].strokeWidth * 100);
7720 }
7721 break;
7722
7723 case Message::SetIndicatorCurrent:
7724 pdoc->DecorationSetCurrentIndicator(static_cast<int>(wParam));
7725 break;
7726 case Message::GetIndicatorCurrent:
7727 return pdoc->decorations->GetCurrentIndicator();
7728 case Message::SetIndicatorValue:
7729 pdoc->decorations->SetCurrentValue(static_cast<int>(wParam));
7730 break;
7731 case Message::GetIndicatorValue:
7732 return pdoc->decorations->GetCurrentValue();
7733
7734 case Message::IndicatorFillRange:
7735 pdoc->DecorationFillRange(PositionFromUPtr(wParam),
7736 pdoc->decorations->GetCurrentValue(), lParam);
7737 break;
7738
7739 case Message::IndicatorClearRange:
7740 pdoc->DecorationFillRange(PositionFromUPtr(wParam), 0,
7741 lParam);
7742 break;
7743
7744 case Message::IndicatorAllOnFor:
7745 return pdoc->decorations->AllOnFor(PositionFromUPtr(wParam));
7746
7747 case Message::IndicatorValueAt:
7748 return pdoc->decorations->ValueAt(static_cast<int>(wParam), lParam);
7749
7750 case Message::IndicatorStart:
7751 return pdoc->decorations->Start(static_cast<int>(wParam), lParam);
7752
7753 case Message::IndicatorEnd:
7754 return pdoc->decorations->End(static_cast<int>(wParam), lParam);
7755
7756 case Message::LineDown:
7757 case Message::LineDownExtend:
7758 case Message::ParaDown:
7759 case Message::ParaDownExtend:
7760 case Message::LineUp:
7761 case Message::LineUpExtend:
7762 case Message::ParaUp:
7763 case Message::ParaUpExtend:
7764 case Message::CharLeft:
7765 case Message::CharLeftExtend:
7766 case Message::CharRight:
7767 case Message::CharRightExtend:
7768 case Message::WordLeft:
7769 case Message::WordLeftExtend:
7770 case Message::WordRight:
7771 case Message::WordRightExtend:
7772 case Message::WordLeftEnd:
7773 case Message::WordLeftEndExtend:
7774 case Message::WordRightEnd:
7775 case Message::WordRightEndExtend:
7776 case Message::Home:
7777 case Message::HomeExtend:
7778 case Message::LineEnd:
7779 case Message::LineEndExtend:
7780 case Message::HomeWrap:
7781 case Message::HomeWrapExtend:
7782 case Message::LineEndWrap:
7783 case Message::LineEndWrapExtend:
7784 case Message::DocumentStart:
7785 case Message::DocumentStartExtend:
7786 case Message::DocumentEnd:
7787 case Message::DocumentEndExtend:
7788 case Message::ScrollToStart:
7789 case Message::ScrollToEnd:
7790
7791 case Message::StutteredPageUp:
7792 case Message::StutteredPageUpExtend:
7793 case Message::StutteredPageDown:
7794 case Message::StutteredPageDownExtend:
7795
7796 case Message::PageUp:
7797 case Message::PageUpExtend:
7798 case Message::PageDown:
7799 case Message::PageDownExtend:
7800 case Message::EditToggleOvertype:
7801 case Message::Cancel:
7802 case Message::DeleteBack:
7803 case Message::Tab:
7804 case Message::BackTab:
7805 case Message::NewLine:
7806 case Message::FormFeed:
7807 case Message::VCHome:
7808 case Message::VCHomeExtend:
7809 case Message::VCHomeWrap:
7810 case Message::VCHomeWrapExtend:
7811 case Message::VCHomeDisplay:
7812 case Message::VCHomeDisplayExtend:
7813 case Message::ZoomIn:
7814 case Message::ZoomOut:
7815 case Message::DelWordLeft:
7816 case Message::DelWordRight:
7817 case Message::DelWordRightEnd:
7818 case Message::DelLineLeft:
7819 case Message::DelLineRight:
7820 case Message::LineCopy:
7821 case Message::LineCut:
7822 case Message::LineDelete:
7823 case Message::LineTranspose:
7824 case Message::LineReverse:
7825 case Message::LineDuplicate:
7826 case Message::LowerCase:
7827 case Message::UpperCase:
7828 case Message::LineScrollDown:
7829 case Message::LineScrollUp:
7830 case Message::WordPartLeft:
7831 case Message::WordPartLeftExtend:
7832 case Message::WordPartRight:
7833 case Message::WordPartRightExtend:
7834 case Message::DeleteBackNotLine:
7835 case Message::HomeDisplay:
7836 case Message::HomeDisplayExtend:
7837 case Message::LineEndDisplay:
7838 case Message::LineEndDisplayExtend:
7839 case Message::LineDownRectExtend:
7840 case Message::LineUpRectExtend:
7841 case Message::CharLeftRectExtend:
7842 case Message::CharRightRectExtend:
7843 case Message::HomeRectExtend:
7844 case Message::VCHomeRectExtend:
7845 case Message::LineEndRectExtend:
7846 case Message::PageUpRectExtend:
7847 case Message::PageDownRectExtend:
7848 case Message::SelectionDuplicate:
7849 return KeyCommand(iMessage);
7850
7851 case Message::BraceHighlight:
7852 SetBraceHighlight(PositionFromUPtr(wParam), lParam, StyleBraceLight);
7853 break;
7854
7855 case Message::BraceHighlightIndicator:
7856 if (lParam >= 0 && static_cast<size_t>(lParam) <= IndicatorMax) {
7857 vs.braceHighlightIndicatorSet = wParam != 0;
7858 vs.braceHighlightIndicator = static_cast<int>(lParam);
7859 }
7860 break;
7861
7862 case Message::BraceBadLight:
7863 SetBraceHighlight(PositionFromUPtr(wParam), -1, StyleBraceBad);
7864 break;
7865
7866 case Message::BraceBadLightIndicator:
7867 if (lParam >= 0 && static_cast<size_t>(lParam) <= IndicatorMax) {
7868 vs.braceBadLightIndicatorSet = wParam != 0;
7869 vs.braceBadLightIndicator = static_cast<int>(lParam);
7870 }
7871 break;
7872
7873 case Message::BraceMatch:
7874 // wParam is position of char to find brace for,
7875 // lParam is maximum amount of text to restyle to find it
7876 return pdoc->BraceMatch(PositionFromUPtr(wParam), lParam, 0, false);
7877
7878 case Message::BraceMatchNext:
7879 return pdoc->BraceMatch(PositionFromUPtr(wParam), 0, lParam, true);
7880
7881 case Message::GetViewEOL:
7882 return vs.viewEOL;
7883
7884 case Message::SetViewEOL:
7885 vs.viewEOL = wParam != 0;
7886 InvalidateStyleRedraw();
7887 break;
7888
7889 case Message::SetZoom: {
7890 const int zoomLevel = static_cast<int>(wParam);
7891 if (zoomLevel != vs.zoomLevel) {
7892 vs.zoomLevel = zoomLevel;
7893 InvalidateStyleRedraw();
7894 NotifyZoom();
7895 }
7896 break;
7897 }
7898
7899 case Message::GetZoom:
7900 return vs.zoomLevel;
7901
7902 case Message::GetEdgeColumn:
7903 return vs.theEdge.column;
7904
7905 case Message::SetEdgeColumn:
7906 vs.theEdge.column = static_cast<int>(wParam);
7907 InvalidateStyleRedraw();
7908 break;
7909
7910 case Message::GetEdgeMode:
7911 return static_cast<sptr_t>(vs.edgeState);
7912
7913 case Message::SetEdgeMode:
7914 vs.edgeState = static_cast<EdgeVisualStyle>(wParam);
7915 InvalidateStyleRedraw();
7916 break;
7917
7918 case Message::GetEdgeColour:
7919 return vs.theEdge.colour.OpaqueRGB();
7920
7921 case Message::SetEdgeColour:
7922 vs.theEdge.colour = ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam));
7923 InvalidateStyleRedraw();
7924 break;
7925
7926 case Message::MultiEdgeAddLine:
7927 vs.AddMultiEdge(static_cast<int>(wParam), ColourRGBA::FromIpRGB(lParam));
7928 InvalidateStyleRedraw();
7929 break;
7930
7931 case Message::MultiEdgeClearAll:
7932 std::vector<EdgeProperties>().swap(vs.theMultiEdge); // Free vector and memory, C++03 compatible
7933 InvalidateStyleRedraw();
7934 break;
7935
7936 case Message::GetMultiEdgeColumn: {
7937 const size_t which = wParam;
7938 // size_t is unsigned so this also handles negative inputs.
7939 if (which >= vs.theMultiEdge.size()) {
7940 return -1;
7941 }
7942 return vs.theMultiEdge[which].column;
7943 }
7944
7945 case Message::GetAccessibility:
7946 return static_cast<sptr_t>(Accessibility::Disabled);
7947
7948 case Message::SetAccessibility:
7949 // May be implemented by platform code.
7950 break;
7951
7952 case Message::GetDocPointer:
7953 return reinterpret_cast<sptr_t>(pdoc);
7954
7955 case Message::SetDocPointer:
7956 CancelModes();
7957 SetDocPointer(static_cast<Document *>(PtrFromSPtr(lParam)));
7958 return 0;
7959
7960 case Message::CreateDocument: {
7961 Document *doc = new Document(static_cast<DocumentOption>(lParam));
7962 doc->AddRef();
7963 doc->Allocate(PositionFromUPtr(wParam));
7964 pcs = ContractionStateCreate(pdoc->IsLarge());
7965 return reinterpret_cast<sptr_t>(doc);
7966 }
7967
7968 case Message::AddRefDocument:
7969 (static_cast<Document *>(PtrFromSPtr(lParam)))->AddRef();
7970 break;
7971
7972 case Message::ReleaseDocument:
7973 (static_cast<Document *>(PtrFromSPtr(lParam)))->Release();
7974 break;
7975
7976 case Message::GetDocumentOptions:
7977 return static_cast<sptr_t>(pdoc->Options());
7978
7979 case Message::CreateLoader: {
7980 Document *doc = new Document(static_cast<DocumentOption>(lParam));
7981 doc->AddRef();
7982 doc->Allocate(PositionFromUPtr(wParam));
7983 doc->SetUndoCollection(false);
7984 pcs = ContractionStateCreate(pdoc->IsLarge());
7985 return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
7986 }
7987
7988 case Message::SetModEventMask:
7989 modEventMask = static_cast<ModificationFlags>(wParam);
7990 return 0;
7991
7992 case Message::GetModEventMask:
7993 return static_cast<sptr_t>(modEventMask);
7994
7995 case Message::SetCommandEvents:
7996 commandEvents = static_cast<bool>(wParam);
7997 return 0;
7998
7999 case Message::GetCommandEvents:
8000 return commandEvents;
8001
8002 case Message::ConvertEOLs:
8003 pdoc->ConvertLineEnds(static_cast<EndOfLine>(wParam));
8004 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
8005 return 0;
8006
8007 case Message::SetLengthForEncode:
8008 lengthForEncode = PositionFromUPtr(wParam);
8009 return 0;
8010
8011 case Message::SelectionIsRectangle:
8012 return sel.selType == Selection::SelTypes::rectangle ? 1 : 0;
8013
8014 case Message::SetSelectionMode: {
8015 switch (static_cast<SelectionMode>(wParam)) {
8016 case SelectionMode::Stream:
8017 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::SelTypes::stream));
8018 sel.selType = Selection::SelTypes::stream;
8019 break;
8020 case SelectionMode::Rectangle:
8021 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::SelTypes::rectangle));
8022 sel.selType = Selection::SelTypes::rectangle;
8023 sel.Rectangular() = sel.RangeMain(); // adjust current selection
8024 break;
8025 case SelectionMode::Lines:
8026 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::SelTypes::lines));
8027 sel.selType = Selection::SelTypes::lines;
8028 SetSelection(sel.RangeMain().caret, sel.RangeMain().anchor); // adjust current selection
8029 break;
8030 case SelectionMode::Thin:
8031 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::SelTypes::thin));
8032 sel.selType = Selection::SelTypes::thin;
8033 break;
8034 default:
8035 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::SelTypes::stream));
8036 sel.selType = Selection::SelTypes::stream;
8037 }
8038 InvalidateWholeSelection();
8039 break;
8040 }
8041 case Message::GetSelectionMode:
8042 switch (sel.selType) {
8043 case Selection::SelTypes::stream:
8044 return static_cast<sptr_t>(SelectionMode::Stream);
8045 case Selection::SelTypes::rectangle:
8046 return static_cast<sptr_t>(SelectionMode::Rectangle);
8047 case Selection::SelTypes::lines:
8048 return static_cast<sptr_t>(SelectionMode::Lines);
8049 case Selection::SelTypes::thin:
8050 return static_cast<sptr_t>(SelectionMode::Thin);
8051 default: // ?!
8052 return static_cast<sptr_t>(SelectionMode::Stream);
8053 }
8054 case Message::GetMoveExtendsSelection:
8055 return sel.MoveExtends();
8056 case Message::GetLineSelStartPosition:
8057 case Message::GetLineSelEndPosition: {
8058 const SelectionSegment segmentLine(
8059 SelectionPosition(pdoc->LineStart(LineFromUPtr(wParam))),
8060 SelectionPosition(pdoc->LineEnd(LineFromUPtr(wParam))));
8061 for (size_t r=0; r<sel.Count(); r++) {
8062 const SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
8063 if (portion.start.IsValid()) {
8064 return (iMessage == Message::GetLineSelStartPosition) ? portion.start.Position() : portion.end.Position();
8065 }
8066 }
8067 return Sci::invalidPosition;
8068 }
8069
8070 case Message::SetOvertype:
8071 if (inOverstrike != (wParam != 0)) {
8072 inOverstrike = wParam != 0;
8073 ContainerNeedsUpdate(Update::Selection);
8074 ShowCaretAtCurrentPosition();
8075 SetIdle(true);
8076 }
8077 break;
8078
8079 case Message::GetOvertype:
8080 return inOverstrike ? 1 : 0;
8081
8082 case Message::SetFocus:
8083 SetFocusState(wParam != 0);
8084 break;
8085
8086 case Message::GetFocus:
8087 return hasFocus;
8088
8089 case Message::SetStatus:
8090 errorStatus = static_cast<Status>(wParam);
8091 break;
8092
8093 case Message::GetStatus:
8094 return static_cast<sptr_t>(errorStatus);
8095
8096 case Message::SetMouseDownCaptures:
8097 mouseDownCaptures = wParam != 0;
8098 break;
8099
8100 case Message::GetMouseDownCaptures:
8101 return mouseDownCaptures;
8102
8103 case Message::SetMouseWheelCaptures:
8104 mouseWheelCaptures = wParam != 0;
8105 break;
8106
8107 case Message::GetMouseWheelCaptures:
8108 return mouseWheelCaptures;
8109
8110 case Message::SetCursor:
8111 cursorMode = static_cast<CursorShape>(wParam);
8112 DisplayCursor(Window::Cursor::text);
8113 break;
8114
8115 case Message::GetCursor:
8116 return static_cast<sptr_t>(cursorMode);
8117
8118 case Message::SetControlCharSymbol:
8119 vs.controlCharSymbol = static_cast<int>(wParam);
8120 InvalidateStyleRedraw();
8121 break;
8122
8123 case Message::GetControlCharSymbol:
8124 return vs.controlCharSymbol;
8125
8126 case Message::SetRepresentation:
8127 reprs.SetRepresentation(ConstCharPtrFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));
8128 break;
8129
8130 case Message::GetRepresentation: {
8131 const Representation *repr = reprs.RepresentationFromCharacter(
8132 ConstCharPtrFromUPtr(wParam));
8133 if (repr) {
8134 return StringResult(lParam, repr->stringRep.c_str());
8135 }
8136 return 0;
8137 }
8138
8139 case Message::ClearRepresentation:
8140 reprs.ClearRepresentation(ConstCharPtrFromUPtr(wParam));
8141 break;
8142
8143 case Message::ClearAllRepresentations:
8144 SetRepresentations();
8145 break;
8146
8147 case Message::SetRepresentationAppearance:
8148 reprs.SetRepresentationAppearance(ConstCharPtrFromUPtr(wParam), static_cast<RepresentationAppearance>(lParam));
8149 break;
8150
8151 case Message::GetRepresentationAppearance: {
8152 const Representation *repr = reprs.RepresentationFromCharacter(
8153 ConstCharPtrFromUPtr(wParam));
8154 if (repr) {
8155 return static_cast<sptr_t>(repr->appearance);
8156 }
8157 return 0;
8158 }
8159 case Message::SetRepresentationColour:
8160 reprs.SetRepresentationColour(ConstCharPtrFromUPtr(wParam), ColourRGBA(static_cast<int>(lParam)));
8161 break;
8162
8163 case Message::GetRepresentationColour: {
8164 const Representation *repr = reprs.RepresentationFromCharacter(
8165 ConstCharPtrFromUPtr(wParam));
8166 if (repr) {
8167 return repr->colour.AsInteger();
8168 }
8169 return 0;
8170 }
8171
8172 case Message::StartRecord:
8173 recordingMacro = true;
8174 return 0;
8175
8176 case Message::StopRecord:
8177 recordingMacro = false;
8178 return 0;
8179
8180 case Message::MoveCaretInsideView:
8181 MoveCaretInsideView();
8182 break;
8183
8184 case Message::SetFoldMarginColour:
8185 vs.foldmarginColour = OptionalColour(wParam, lParam);
8186 InvalidateStyleRedraw();
8187 break;
8188
8189 case Message::SetFoldMarginHiColour:
8190 vs.foldmarginHighlightColour = OptionalColour(wParam, lParam);
8191 InvalidateStyleRedraw();
8192 break;
8193
8194 case Message::SetHotspotActiveFore:
8195 if (vs.SetElementColourOptional(Element::HotSpotActive, wParam, lParam)) {
8196 InvalidateStyleRedraw();
8197 }
8198 break;
8199
8200 case Message::GetHotspotActiveFore:
8201 return vs.ElementColour(Element::HotSpotActive).value_or(ColourRGBA()).OpaqueRGB();
8202
8203 case Message::SetHotspotActiveBack:
8204 if (vs.SetElementColourOptional(Element::HotSpotActiveBack, wParam, lParam)) {
8205 InvalidateStyleRedraw();
8206 }
8207 break;
8208
8209 case Message::GetHotspotActiveBack:
8210 return vs.ElementColour(Element::HotSpotActiveBack).value_or(ColourRGBA()).OpaqueRGB();
8211
8212 case Message::SetHotspotActiveUnderline:
8213 vs.hotspotUnderline = wParam != 0;
8214 InvalidateStyleRedraw();
8215 break;
8216
8217 case Message::GetHotspotActiveUnderline:
8218 return vs.hotspotUnderline ? 1 : 0;
8219
8220 case Message::SetHotspotSingleLine:
8221 hotspotSingleLine = wParam != 0;
8222 InvalidateStyleRedraw();
8223 break;
8224
8225 case Message::GetHotspotSingleLine:
8226 return hotspotSingleLine ? 1 : 0;
8227
8228 case Message::SetPasteConvertEndings:
8229 convertPastes = wParam != 0;
8230 break;
8231
8232 case Message::GetPasteConvertEndings:
8233 return convertPastes ? 1 : 0;
8234
8235 case Message::GetCharacterPointer:
8236 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
8237
8238 case Message::GetRangePointer:
8239 return reinterpret_cast<sptr_t>(pdoc->RangePointer(
8240 PositionFromUPtr(wParam), lParam));
8241
8242 case Message::GetGapPosition:
8243 return pdoc->GapPosition();
8244
8245 case Message::SetExtraAscent:
8246 vs.extraAscent = static_cast<int>(wParam);
8247 InvalidateStyleRedraw();
8248 break;
8249
8250 case Message::GetExtraAscent:
8251 return vs.extraAscent;
8252
8253 case Message::SetExtraDescent:
8254 vs.extraDescent = static_cast<int>(wParam);
8255 InvalidateStyleRedraw();
8256 break;
8257
8258 case Message::GetExtraDescent:
8259 return vs.extraDescent;
8260
8261 case Message::MarginSetStyleOffset:
8262 vs.marginStyleOffset = static_cast<int>(wParam);
8263 InvalidateStyleRedraw();
8264 break;
8265
8266 case Message::MarginGetStyleOffset:
8267 return vs.marginStyleOffset;
8268
8269 case Message::SetMarginOptions:
8270 marginOptions = static_cast<MarginOption>(wParam);
8271 break;
8272
8273 case Message::GetMarginOptions:
8274 return static_cast<sptr_t>(marginOptions);
8275
8276 case Message::MarginSetText:
8277 pdoc->MarginSetText(LineFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));
8278 break;
8279
8280 case Message::MarginGetText: {
8281 const StyledText st = pdoc->MarginStyledText(LineFromUPtr(wParam));
8282 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
8283 }
8284
8285 case Message::MarginSetStyle:
8286 pdoc->MarginSetStyle(LineFromUPtr(wParam), static_cast<int>(lParam));
8287 break;
8288
8289 case Message::MarginGetStyle: {
8290 const StyledText st = pdoc->MarginStyledText(LineFromUPtr(wParam));
8291 return st.style;
8292 }
8293
8294 case Message::MarginSetStyles:
8295 pdoc->MarginSetStyles(LineFromUPtr(wParam), ConstUCharPtrFromSPtr(lParam));
8296 break;
8297
8298 case Message::MarginGetStyles: {
8299 const StyledText st = pdoc->MarginStyledText(LineFromUPtr(wParam));
8300 return BytesResult(lParam, st.styles, st.length);
8301 }
8302
8303 case Message::MarginTextClearAll:
8304 pdoc->MarginClearAll();
8305 break;
8306
8307 case Message::AnnotationSetText:
8308 pdoc->AnnotationSetText(LineFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));
8309 break;
8310
8311 case Message::AnnotationGetText: {
8312 const StyledText st = pdoc->AnnotationStyledText(LineFromUPtr(wParam));
8313 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
8314 }
8315
8316 case Message::AnnotationGetStyle: {
8317 const StyledText st = pdoc->AnnotationStyledText(LineFromUPtr(wParam));
8318 return st.style;
8319 }
8320
8321 case Message::AnnotationSetStyle:
8322 pdoc->AnnotationSetStyle(LineFromUPtr(wParam), static_cast<int>(lParam));
8323 break;
8324
8325 case Message::AnnotationSetStyles:
8326 pdoc->AnnotationSetStyles(LineFromUPtr(wParam), ConstUCharPtrFromSPtr(lParam));
8327 break;
8328
8329 case Message::AnnotationGetStyles: {
8330 const StyledText st = pdoc->AnnotationStyledText(LineFromUPtr(wParam));
8331 return BytesResult(lParam, st.styles, st.length);
8332 }
8333
8334 case Message::AnnotationGetLines:
8335 return pdoc->AnnotationLines(LineFromUPtr(wParam));
8336
8337 case Message::AnnotationClearAll:
8338 pdoc->AnnotationClearAll();
8339 break;
8340
8341 case Message::AnnotationSetVisible:
8342 SetAnnotationVisible(static_cast<AnnotationVisible>(wParam));
8343 break;
8344
8345 case Message::AnnotationGetVisible:
8346 return static_cast<sptr_t>(vs.annotationVisible);
8347
8348 case Message::AnnotationSetStyleOffset:
8349 vs.annotationStyleOffset = static_cast<int>(wParam);
8350 InvalidateStyleRedraw();
8351 break;
8352
8353 case Message::AnnotationGetStyleOffset:
8354 return vs.annotationStyleOffset;
8355
8356 case Message::EOLAnnotationSetText:
8357 pdoc->EOLAnnotationSetText(LineFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));
8358 break;
8359
8360 case Message::EOLAnnotationGetText: {
8361 const StyledText st = pdoc->EOLAnnotationStyledText(LineFromUPtr(wParam));
8362 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
8363 }
8364
8365 case Message::EOLAnnotationGetStyle: {
8366 const StyledText st = pdoc->EOLAnnotationStyledText(LineFromUPtr(wParam));
8367 return st.style;
8368 }
8369
8370 case Message::EOLAnnotationSetStyle:
8371 pdoc->EOLAnnotationSetStyle(LineFromUPtr(wParam), static_cast<int>(lParam));
8372 break;
8373
8374 case Message::EOLAnnotationClearAll:
8375 pdoc->EOLAnnotationClearAll();
8376 break;
8377
8378 case Message::EOLAnnotationSetVisible:
8379 SetEOLAnnotationVisible(static_cast<EOLAnnotationVisible>(wParam));
8380 break;
8381
8382 case Message::EOLAnnotationGetVisible:
8383 return static_cast<sptr_t>(vs.eolAnnotationVisible);
8384
8385 case Message::EOLAnnotationSetStyleOffset:
8386 vs.eolAnnotationStyleOffset = static_cast<int>(wParam);
8387 InvalidateStyleRedraw();
8388 break;
8389
8390 case Message::EOLAnnotationGetStyleOffset:
8391 return vs.eolAnnotationStyleOffset;
8392
8393 case Message::ReleaseAllExtendedStyles:
8394 vs.ReleaseAllExtendedStyles();
8395 break;
8396
8397 case Message::AllocateExtendedStyles:
8398 return vs.AllocateExtendedStyles(static_cast<int>(wParam));
8399
8400 case Message::SupportsFeature:
8401 return SupportsFeature(static_cast<Supports>(wParam));
8402
8403 case Message::AddUndoAction:
8404 pdoc->AddUndoAction(PositionFromUPtr(wParam),
8405 FlagSet(static_cast<UndoFlags>(lParam), UndoFlags::MayCoalesce));
8406 break;
8407
8408 case Message::SetMouseSelectionRectangularSwitch:
8409 mouseSelectionRectangularSwitch = wParam != 0;
8410 break;
8411
8412 case Message::GetMouseSelectionRectangularSwitch:
8413 return mouseSelectionRectangularSwitch;
8414
8415 case Message::SetMultipleSelection:
8416 multipleSelection = wParam != 0;
8417 InvalidateCaret();
8418 break;
8419
8420 case Message::GetMultipleSelection:
8421 return multipleSelection;
8422
8423 case Message::SetAdditionalSelectionTyping:
8424 additionalSelectionTyping = wParam != 0;
8425 InvalidateCaret();
8426 break;
8427
8428 case Message::GetAdditionalSelectionTyping:
8429 return additionalSelectionTyping;
8430
8431 case Message::SetMultiPaste:
8432 multiPasteMode = static_cast<MultiPaste>(wParam);
8433 break;
8434
8435 case Message::GetMultiPaste:
8436 return static_cast<sptr_t>(multiPasteMode);
8437
8438 case Message::SetAdditionalCaretsBlink:
8439 view.additionalCaretsBlink = wParam != 0;
8440 InvalidateCaret();
8441 break;
8442
8443 case Message::GetAdditionalCaretsBlink:
8444 return view.additionalCaretsBlink;
8445
8446 case Message::SetAdditionalCaretsVisible:
8447 view.additionalCaretsVisible = wParam != 0;
8448 InvalidateCaret();
8449 break;
8450
8451 case Message::GetAdditionalCaretsVisible:
8452 return view.additionalCaretsVisible;
8453
8454 case Message::GetSelections:
8455 return sel.Count();
8456
8457 case Message::GetSelectionEmpty:
8458 return sel.Empty();
8459
8460 case Message::ClearSelections:
8461 sel.Clear();
8462 ContainerNeedsUpdate(Update::Selection);
8463 Redraw();
8464 break;
8465
8466 case Message::SetSelection:
8467 sel.SetSelection(SelectionRange(PositionFromUPtr(wParam), lParam));
8468 Redraw();
8469 break;
8470
8471 case Message::AddSelection:
8472 sel.AddSelection(SelectionRange(PositionFromUPtr(wParam), lParam));
8473 ContainerNeedsUpdate(Update::Selection);
8474 Redraw();
8475 break;
8476
8477 case Message::DropSelectionN:
8478 sel.DropSelection(wParam);
8479 ContainerNeedsUpdate(Update::Selection);
8480 Redraw();
8481 break;
8482
8483 case Message::SetMainSelection:
8484 sel.SetMain(wParam);
8485 ContainerNeedsUpdate(Update::Selection);
8486 Redraw();
8487 break;
8488
8489 case Message::GetMainSelection:
8490 return sel.Main();
8491
8492 case Message::SetSelectionNCaret:
8493 case Message::SetSelectionNAnchor:
8494 case Message::SetSelectionNCaretVirtualSpace:
8495 case Message::SetSelectionNAnchorVirtualSpace:
8496 case Message::SetSelectionNStart:
8497 case Message::SetSelectionNEnd:
8498 SetSelectionNMessage(iMessage, wParam, lParam);
8499 break;
8500
8501 case Message::GetSelectionNCaret:
8502 return sel.Range(wParam).caret.Position();
8503
8504 case Message::GetSelectionNAnchor:
8505 return sel.Range(wParam).anchor.Position();
8506
8507 case Message::GetSelectionNCaretVirtualSpace:
8508 return sel.Range(wParam).caret.VirtualSpace();
8509
8510 case Message::GetSelectionNAnchorVirtualSpace:
8511 return sel.Range(wParam).anchor.VirtualSpace();
8512
8513 case Message::GetSelectionNStart:
8514 return sel.Range(wParam).Start().Position();
8515
8516 case Message::GetSelectionNStartVirtualSpace:
8517 return sel.Range(wParam).Start().VirtualSpace();
8518
8519 case Message::GetSelectionNEnd:
8520 return sel.Range(wParam).End().Position();
8521
8522 case Message::GetSelectionNEndVirtualSpace:
8523 return sel.Range(wParam).End().VirtualSpace();
8524
8525 case Message::SetRectangularSelectionCaret:
8526 if (!sel.IsRectangular())
8527 sel.Clear();
8528 sel.selType = Selection::SelTypes::rectangle;
8529 sel.Rectangular().caret.SetPosition(PositionFromUPtr(wParam));
8530 SetRectangularRange();
8531 Redraw();
8532 break;
8533
8534 case Message::GetRectangularSelectionCaret:
8535 return sel.Rectangular().caret.Position();
8536
8537 case Message::SetRectangularSelectionAnchor:
8538 if (!sel.IsRectangular())
8539 sel.Clear();
8540 sel.selType = Selection::SelTypes::rectangle;
8541 sel.Rectangular().anchor.SetPosition(PositionFromUPtr(wParam));
8542 SetRectangularRange();
8543 Redraw();
8544 break;
8545
8546 case Message::GetRectangularSelectionAnchor:
8547 return sel.Rectangular().anchor.Position();
8548
8549 case Message::SetRectangularSelectionCaretVirtualSpace:
8550 if (!sel.IsRectangular())
8551 sel.Clear();
8552 sel.selType = Selection::SelTypes::rectangle;
8553 sel.Rectangular().caret.SetVirtualSpace(PositionFromUPtr(wParam));
8554 SetRectangularRange();
8555 Redraw();
8556 break;
8557
8558 case Message::GetRectangularSelectionCaretVirtualSpace:
8559 return sel.Rectangular().caret.VirtualSpace();
8560
8561 case Message::SetRectangularSelectionAnchorVirtualSpace:
8562 if (!sel.IsRectangular())
8563 sel.Clear();
8564 sel.selType = Selection::SelTypes::rectangle;
8565 sel.Rectangular().anchor.SetVirtualSpace(PositionFromUPtr(wParam));
8566 SetRectangularRange();
8567 Redraw();
8568 break;
8569
8570 case Message::GetRectangularSelectionAnchorVirtualSpace:
8571 return sel.Rectangular().anchor.VirtualSpace();
8572
8573 case Message::SetVirtualSpaceOptions:
8574 virtualSpaceOptions = static_cast<VirtualSpace>(wParam);
8575 break;
8576
8577 case Message::GetVirtualSpaceOptions:
8578 return static_cast<sptr_t>(virtualSpaceOptions);
8579
8580 case Message::SetAdditionalSelFore:
8581 vs.elementColours[Element::SelectionAdditionalText] = ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam));
8582 InvalidateStyleRedraw();
8583 break;
8584
8585 case Message::SetAdditionalSelBack:
8586 vs.SetElementRGB(Element::SelectionAdditionalBack, static_cast<int>(wParam));
8587 InvalidateStyleRedraw();
8588 break;
8589
8590 case Message::SetAdditionalSelAlpha:
8591 vs.SetElementAlpha(Element::SelectionAdditionalBack, static_cast<int>(wParam));
8592 InvalidateStyleRedraw();
8593 break;
8594
8595 case Message::GetAdditionalSelAlpha:
8596 if (vs.selection.layer == Layer::Base)
8597 return static_cast<sptr_t>(Alpha::NoAlpha);
8598 return vs.ElementColour(Element::SelectionAdditionalBack)->GetAlpha();
8599
8600 case Message::SetAdditionalCaretFore:
8601 vs.elementColours[Element::CaretAdditional] = ColourRGBA::FromIpRGB(SPtrFromUPtr(wParam));
8602 InvalidateStyleRedraw();
8603 break;
8604
8605 case Message::GetAdditionalCaretFore:
8606 return vs.ElementColour(Element::CaretAdditional)->OpaqueRGB();
8607
8608 case Message::RotateSelection:
8609 sel.RotateMain();
8610 InvalidateWholeSelection();
8611 break;
8612
8613 case Message::SwapMainAnchorCaret:
8614 InvalidateSelection(sel.RangeMain());
8615 sel.RangeMain().Swap();
8616 break;
8617
8618 case Message::MultipleSelectAddNext:
8619 MultipleSelectAdd(AddNumber::one);
8620 break;
8621
8622 case Message::MultipleSelectAddEach:
8623 MultipleSelectAdd(AddNumber::each);
8624 break;
8625
8626 case Message::ChangeLexerState:
8627 pdoc->ChangeLexerState(PositionFromUPtr(wParam), lParam);
8628 break;
8629
8630 case Message::SetIdentifier:
8631 SetCtrlID(static_cast<int>(wParam));
8632 break;
8633
8634 case Message::GetIdentifier:
8635 return GetCtrlID();
8636
8637 case Message::SetTechnology:
8638 // No action by default
8639 break;
8640
8641 case Message::GetTechnology:
8642 return static_cast<sptr_t>(technology);
8643
8644 case Message::CountCharacters:
8645 return pdoc->CountCharacters(PositionFromUPtr(wParam), lParam);
8646
8647 case Message::CountCodeUnits:
8648 return pdoc->CountUTF16(PositionFromUPtr(wParam), lParam);
8649
8650 default:
8651 return DefWndProc(iMessage, wParam, lParam);
8652 }
8653 //Platform::DebugPrintf("end wnd proc\n");
8654 return 0;
8655}
8656