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