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