Update Scintilla to version 3.6.3
[geany-mirror.git] / scintilla / src / Editor.cxx
blob08248a3d9d5ab3dd39f323fb4639124304b708ff
1 // Scintilla source code edit control
2 /** @file Editor.cxx
3 ** Main code for the edit control.
4 **/
5 // Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <assert.h>
12 #include <ctype.h>
14 #include <cmath>
15 #include <stdexcept>
16 #include <string>
17 #include <vector>
18 #include <map>
19 #include <algorithm>
20 #include <memory>
22 #include "Platform.h"
24 #include "ILexer.h"
25 #include "Scintilla.h"
27 #include "StringCopy.h"
28 #include "Position.h"
29 #include "SplitVector.h"
30 #include "Partitioning.h"
31 #include "RunStyles.h"
32 #include "ContractionState.h"
33 #include "CellBuffer.h"
34 #include "PerLine.h"
35 #include "KeyMap.h"
36 #include "Indicator.h"
37 #include "XPM.h"
38 #include "LineMarker.h"
39 #include "Style.h"
40 #include "ViewStyle.h"
41 #include "CharClassify.h"
42 #include "Decoration.h"
43 #include "CaseFolder.h"
44 #include "Document.h"
45 #include "UniConversion.h"
46 #include "Selection.h"
47 #include "PositionCache.h"
48 #include "EditModel.h"
49 #include "MarginView.h"
50 #include "EditView.h"
51 #include "Editor.h"
53 #ifdef SCI_NAMESPACE
54 using namespace Scintilla;
55 #endif
58 return whether this modification represents an operation that
59 may reasonably be deferred (not done now OR [possibly] at all)
61 static bool CanDeferToLastStep(const DocModification &mh) {
62 if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
63 return true; // CAN skip
64 if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)))
65 return false; // MUST do
66 if (mh.modificationType & SC_MULTISTEPUNDOREDO)
67 return true; // CAN skip
68 return false; // PRESUMABLY must do
71 static bool CanEliminate(const DocModification &mh) {
72 return
73 (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
77 return whether this modification represents the FINAL step
78 in a [possibly lengthy] multi-step Undo/Redo sequence
80 static bool IsLastStep(const DocModification &mh) {
81 return
82 (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0
83 && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
84 && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
85 && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
88 Timer::Timer() :
89 ticking(false), ticksToWait(0), tickerID(0) {}
91 Idler::Idler() :
92 state(false), idlerID(0) {}
94 static inline bool IsAllSpacesOrTabs(const char *s, unsigned int len) {
95 for (unsigned int i = 0; i < len; i++) {
96 // This is safe because IsSpaceOrTab() will return false for null terminators
97 if (!IsSpaceOrTab(s[i]))
98 return false;
100 return true;
103 Editor::Editor() {
104 ctrlID = 0;
106 stylesValid = false;
107 technology = SC_TECHNOLOGY_DEFAULT;
108 scaleRGBAImage = 100.0f;
110 cursorMode = SC_CURSORNORMAL;
112 hasFocus = false;
113 errorStatus = 0;
114 mouseDownCaptures = true;
116 lastClickTime = 0;
117 doubleClickCloseThreshold = Point(3, 3);
118 dwellDelay = SC_TIME_FOREVER;
119 ticksToDwell = SC_TIME_FOREVER;
120 dwelling = false;
121 ptMouseLast.x = 0;
122 ptMouseLast.y = 0;
123 inDragDrop = ddNone;
124 dropWentOutside = false;
125 posDrop = SelectionPosition(invalidPosition);
126 hotSpotClickPos = INVALID_POSITION;
127 selectionType = selChar;
129 lastXChosen = 0;
130 lineAnchorPos = 0;
131 originalAnchorPos = 0;
132 wordSelectAnchorStartPos = 0;
133 wordSelectAnchorEndPos = 0;
134 wordSelectInitialCaretPos = -1;
136 caretXPolicy = CARET_SLOP | CARET_EVEN;
137 caretXSlop = 50;
139 caretYPolicy = CARET_EVEN;
140 caretYSlop = 0;
142 visiblePolicy = 0;
143 visibleSlop = 0;
145 searchAnchor = 0;
147 xCaretMargin = 50;
148 horizontalScrollBarVisible = true;
149 scrollWidth = 2000;
150 verticalScrollBarVisible = true;
151 endAtLastLine = true;
152 caretSticky = SC_CARETSTICKY_OFF;
153 marginOptions = SC_MARGINOPTION_NONE;
154 mouseSelectionRectangularSwitch = false;
155 multipleSelection = false;
156 additionalSelectionTyping = false;
157 multiPasteMode = SC_MULTIPASTE_ONCE;
158 virtualSpaceOptions = SCVS_NONE;
160 targetStart = 0;
161 targetEnd = 0;
162 searchFlags = 0;
164 topLine = 0;
165 posTopLine = 0;
167 lengthForEncode = -1;
169 needUpdateUI = 0;
170 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
172 paintState = notPainting;
173 paintAbandonedByStyling = false;
174 paintingAllText = false;
175 willRedrawAll = false;
176 idleStyling = SC_IDLESTYLING_NONE;
177 needIdleStyling = false;
179 modEventMask = SC_MODEVENTMASKALL;
181 pdoc->AddWatcher(this, 0);
183 recordingMacro = false;
184 foldAutomatic = 0;
186 convertPastes = true;
188 SetRepresentations();
191 Editor::~Editor() {
192 pdoc->RemoveWatcher(this, 0);
193 DropGraphics(true);
196 void Editor::Finalise() {
197 SetIdle(false);
198 CancelModes();
201 void Editor::SetRepresentations() {
202 reprs.Clear();
204 // C0 control set
205 const char *reps[] = {
206 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
207 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
208 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
209 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
211 for (size_t j=0; j < ELEMENTS(reps); j++) {
212 char c[2] = { static_cast<char>(j), 0 };
213 reprs.SetRepresentation(c, reps[j]);
216 // C1 control set
217 // As well as Unicode mode, ISO-8859-1 should use these
218 if (IsUnicodeMode()) {
219 const char *repsC1[] = {
220 "PAD", "HOP", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
221 "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
222 "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
223 "SOS", "SGCI", "SCI", "CSI", "ST", "OSC", "PM", "APC"
225 for (size_t j=0; j < ELEMENTS(repsC1); j++) {
226 char c1[3] = { '\xc2', static_cast<char>(0x80+j), 0 };
227 reprs.SetRepresentation(c1, repsC1[j]);
229 reprs.SetRepresentation("\xe2\x80\xa8", "LS");
230 reprs.SetRepresentation("\xe2\x80\xa9", "PS");
233 // UTF-8 invalid bytes
234 if (IsUnicodeMode()) {
235 for (int k=0x80; k < 0x100; k++) {
236 char hiByte[2] = { static_cast<char>(k), 0 };
237 char hexits[4];
238 sprintf(hexits, "x%2X", k);
239 reprs.SetRepresentation(hiByte, hexits);
244 void Editor::DropGraphics(bool freeObjects) {
245 marginView.DropGraphics(freeObjects);
246 view.DropGraphics(freeObjects);
249 void Editor::AllocateGraphics() {
250 marginView.AllocateGraphics(vs);
251 view.AllocateGraphics(vs);
254 void Editor::InvalidateStyleData() {
255 stylesValid = false;
256 vs.technology = technology;
257 DropGraphics(false);
258 AllocateGraphics();
259 view.llc.Invalidate(LineLayout::llInvalid);
260 view.posCache.Clear();
263 void Editor::InvalidateStyleRedraw() {
264 NeedWrapping();
265 InvalidateStyleData();
266 Redraw();
269 void Editor::RefreshStyleData() {
270 if (!stylesValid) {
271 stylesValid = true;
272 AutoSurface surface(this);
273 if (surface) {
274 vs.Refresh(*surface, pdoc->tabInChars);
276 SetScrollBars();
277 SetRectangularRange();
281 Point Editor::GetVisibleOriginInMain() const {
282 return Point(0,0);
285 Point Editor::DocumentPointFromView(Point ptView) const {
286 Point ptDocument = ptView;
287 if (wMargin.GetID()) {
288 Point ptOrigin = GetVisibleOriginInMain();
289 ptDocument.x += ptOrigin.x;
290 ptDocument.y += ptOrigin.y;
291 } else {
292 ptDocument.x += xOffset;
293 ptDocument.y += topLine * vs.lineHeight;
295 return ptDocument;
298 int Editor::TopLineOfMain() const {
299 if (wMargin.GetID())
300 return 0;
301 else
302 return topLine;
305 PRectangle Editor::GetClientRectangle() const {
306 Window &win = const_cast<Window &>(wMain);
307 return win.GetClientPosition();
310 PRectangle Editor::GetClientDrawingRectangle() {
311 return GetClientRectangle();
314 PRectangle Editor::GetTextRectangle() const {
315 PRectangle rc = GetClientRectangle();
316 rc.left += vs.textStart;
317 rc.right -= vs.rightMarginWidth;
318 return rc;
321 int Editor::LinesOnScreen() const {
322 PRectangle rcClient = GetClientRectangle();
323 int htClient = static_cast<int>(rcClient.bottom - rcClient.top);
324 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
325 return htClient / vs.lineHeight;
328 int Editor::LinesToScroll() const {
329 int retVal = LinesOnScreen() - 1;
330 if (retVal < 1)
331 return 1;
332 else
333 return retVal;
336 int Editor::MaxScrollPos() const {
337 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
338 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
339 int retVal = cs.LinesDisplayed();
340 if (endAtLastLine) {
341 retVal -= LinesOnScreen();
342 } else {
343 retVal--;
345 if (retVal < 0) {
346 return 0;
347 } else {
348 return retVal;
352 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
353 if (sp.Position() < 0) {
354 return SelectionPosition(0);
355 } else if (sp.Position() > pdoc->Length()) {
356 return SelectionPosition(pdoc->Length());
357 } else {
358 // If not at end of line then set offset to 0
359 if (!pdoc->IsLineEndPosition(sp.Position()))
360 sp.SetVirtualSpace(0);
361 return sp;
365 Point Editor::LocationFromPosition(SelectionPosition pos) {
366 RefreshStyleData();
367 AutoSurface surface(this);
368 return view.LocationFromPosition(surface, *this, pos, topLine, vs);
371 Point Editor::LocationFromPosition(int pos) {
372 return LocationFromPosition(SelectionPosition(pos));
375 int Editor::XFromPosition(int pos) {
376 Point pt = LocationFromPosition(pos);
377 return static_cast<int>(pt.x) - vs.textStart + xOffset;
380 int Editor::XFromPosition(SelectionPosition sp) {
381 Point pt = LocationFromPosition(sp);
382 return static_cast<int>(pt.x) - vs.textStart + xOffset;
385 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
386 RefreshStyleData();
387 AutoSurface surface(this);
389 if (canReturnInvalid) {
390 PRectangle rcClient = GetTextRectangle();
391 // May be in scroll view coordinates so translate back to main view
392 Point ptOrigin = GetVisibleOriginInMain();
393 rcClient.Move(-ptOrigin.x, -ptOrigin.y);
394 if (!rcClient.Contains(pt))
395 return SelectionPosition(INVALID_POSITION);
396 if (pt.x < vs.textStart)
397 return SelectionPosition(INVALID_POSITION);
398 if (pt.y < 0)
399 return SelectionPosition(INVALID_POSITION);
401 pt = DocumentPointFromView(pt);
402 return view.SPositionFromLocation(surface, *this, pt, canReturnInvalid, charPosition, virtualSpace, vs);
405 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
406 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
410 * Find the document position corresponding to an x coordinate on a particular document line.
411 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
412 * This method is used for rectangular selections and does not work on wrapped lines.
414 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
415 RefreshStyleData();
416 if (lineDoc >= pdoc->LinesTotal())
417 return SelectionPosition(pdoc->Length());
418 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
419 AutoSurface surface(this);
420 return view.SPositionFromLineX(surface, *this, lineDoc, x, vs);
423 int Editor::PositionFromLineX(int lineDoc, int x) {
424 return SPositionFromLineX(lineDoc, x).Position();
427 int Editor::LineFromLocation(Point pt) const {
428 return cs.DocFromDisplay(static_cast<int>(pt.y) / vs.lineHeight + topLine);
431 void Editor::SetTopLine(int topLineNew) {
432 if ((topLine != topLineNew) && (topLineNew >= 0)) {
433 topLine = topLineNew;
434 ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
436 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
440 * If painting then abandon the painting because a wider redraw is needed.
441 * @return true if calling code should stop drawing.
443 bool Editor::AbandonPaint() {
444 if ((paintState == painting) && !paintingAllText) {
445 paintState = paintAbandoned;
447 return paintState == paintAbandoned;
450 void Editor::RedrawRect(PRectangle rc) {
451 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
453 // Clip the redraw rectangle into the client area
454 PRectangle rcClient = GetClientRectangle();
455 if (rc.top < rcClient.top)
456 rc.top = rcClient.top;
457 if (rc.bottom > rcClient.bottom)
458 rc.bottom = rcClient.bottom;
459 if (rc.left < rcClient.left)
460 rc.left = rcClient.left;
461 if (rc.right > rcClient.right)
462 rc.right = rcClient.right;
464 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
465 wMain.InvalidateRectangle(rc);
469 void Editor::DiscardOverdraw() {
470 // Overridden on platforms that may draw outside visible area.
473 void Editor::Redraw() {
474 //Platform::DebugPrintf("Redraw all\n");
475 PRectangle rcClient = GetClientRectangle();
476 wMain.InvalidateRectangle(rcClient);
477 if (wMargin.GetID())
478 wMargin.InvalidateAll();
479 //wMain.InvalidateAll();
482 void Editor::RedrawSelMargin(int line, bool allAfter) {
483 const bool markersInText = vs.maskInLine || vs.maskDrawInText;
484 if (!wMargin.GetID() || markersInText) { // May affect text area so may need to abandon and retry
485 if (AbandonPaint()) {
486 return;
489 if (wMargin.GetID() && markersInText) {
490 Redraw();
491 return;
493 PRectangle rcMarkers = GetClientRectangle();
494 if (!markersInText) {
495 // Normal case: just draw the margin
496 rcMarkers.right = rcMarkers.left + vs.fixedColumnWidth;
498 if (line != -1) {
499 PRectangle rcLine = RectangleFromRange(Range(pdoc->LineStart(line)), 0);
501 // Inflate line rectangle if there are image markers with height larger than line height
502 if (vs.largestMarkerHeight > vs.lineHeight) {
503 int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
504 rcLine.top -= delta;
505 rcLine.bottom += delta;
506 if (rcLine.top < rcMarkers.top)
507 rcLine.top = rcMarkers.top;
508 if (rcLine.bottom > rcMarkers.bottom)
509 rcLine.bottom = rcMarkers.bottom;
512 rcMarkers.top = rcLine.top;
513 if (!allAfter)
514 rcMarkers.bottom = rcLine.bottom;
515 if (rcMarkers.Empty())
516 return;
518 if (wMargin.GetID()) {
519 Point ptOrigin = GetVisibleOriginInMain();
520 rcMarkers.Move(-ptOrigin.x, -ptOrigin.y);
521 wMargin.InvalidateRectangle(rcMarkers);
522 } else {
523 wMain.InvalidateRectangle(rcMarkers);
527 PRectangle Editor::RectangleFromRange(Range r, int overlap) {
528 const int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(r.First()));
529 const int maxLine = cs.DisplayLastFromDoc(pdoc->LineFromPosition(r.Last()));
530 const PRectangle rcClientDrawing = GetClientDrawingRectangle();
531 PRectangle rc;
532 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
533 rc.left = static_cast<XYPOSITION>(vs.textStart - leftTextOverlap);
534 rc.top = static_cast<XYPOSITION>((minLine - TopLineOfMain()) * vs.lineHeight - overlap);
535 if (rc.top < rcClientDrawing.top)
536 rc.top = rcClientDrawing.top;
537 // Extend to right of prepared area if any to prevent artifacts from caret line highlight
538 rc.right = rcClientDrawing.right;
539 rc.bottom = static_cast<XYPOSITION>((maxLine - TopLineOfMain() + 1) * vs.lineHeight + overlap);
541 return rc;
544 void Editor::InvalidateRange(int start, int end) {
545 RedrawRect(RectangleFromRange(Range(start, end), view.LinesOverlap() ? vs.lineOverlap : 0));
548 int Editor::CurrentPosition() const {
549 return sel.MainCaret();
552 bool Editor::SelectionEmpty() const {
553 return sel.Empty();
556 SelectionPosition Editor::SelectionStart() {
557 return sel.RangeMain().Start();
560 SelectionPosition Editor::SelectionEnd() {
561 return sel.RangeMain().End();
564 void Editor::SetRectangularRange() {
565 if (sel.IsRectangular()) {
566 int xAnchor = XFromPosition(sel.Rectangular().anchor);
567 int xCaret = XFromPosition(sel.Rectangular().caret);
568 if (sel.selType == Selection::selThin) {
569 xCaret = xAnchor;
571 int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
572 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
573 int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
574 for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
575 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
576 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
577 range.ClearVirtualSpace();
578 if (line == lineAnchorRect)
579 sel.SetSelection(range);
580 else
581 sel.AddSelectionWithoutTrim(range);
586 void Editor::ThinRectangularRange() {
587 if (sel.IsRectangular()) {
588 sel.selType = Selection::selThin;
589 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
590 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
591 } else {
592 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
594 SetRectangularRange();
598 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
599 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
600 invalidateWholeSelection = true;
602 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
603 // +1 for lastAffected ensures caret repainted
604 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
605 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
606 if (invalidateWholeSelection) {
607 for (size_t r=0; r<sel.Count(); r++) {
608 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
609 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
610 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
611 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
614 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
615 InvalidateRange(firstAffected, lastAffected);
618 void Editor::InvalidateWholeSelection() {
619 InvalidateSelection(sel.RangeMain(), true);
622 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
623 currentPos_ = ClampPositionIntoDocument(currentPos_);
624 anchor_ = ClampPositionIntoDocument(anchor_);
625 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
626 /* For Line selection - ensure the anchor and caret are always
627 at the beginning and end of the region lines. */
628 if (sel.selType == Selection::selLines) {
629 if (currentPos_ > anchor_) {
630 anchor_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
631 currentPos_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
632 } else {
633 currentPos_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
634 anchor_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
637 SelectionRange rangeNew(currentPos_, anchor_);
638 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
639 InvalidateSelection(rangeNew);
641 sel.RangeMain() = rangeNew;
642 SetRectangularRange();
643 ClaimSelection();
644 SetHoverIndicatorPosition(sel.MainCaret());
646 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
647 RedrawSelMargin();
649 QueueIdleWork(WorkNeeded::workUpdateUI);
652 void Editor::SetSelection(int currentPos_, int anchor_) {
653 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
656 // Just move the caret on the main selection
657 void Editor::SetSelection(SelectionPosition currentPos_) {
658 currentPos_ = ClampPositionIntoDocument(currentPos_);
659 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
660 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
661 InvalidateSelection(SelectionRange(currentPos_));
663 if (sel.IsRectangular()) {
664 sel.Rectangular() =
665 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
666 SetRectangularRange();
667 } else {
668 sel.RangeMain() =
669 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
671 ClaimSelection();
672 SetHoverIndicatorPosition(sel.MainCaret());
674 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
675 RedrawSelMargin();
677 QueueIdleWork(WorkNeeded::workUpdateUI);
680 void Editor::SetSelection(int currentPos_) {
681 SetSelection(SelectionPosition(currentPos_));
684 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
685 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
686 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
687 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
688 InvalidateSelection(rangeNew);
690 sel.Clear();
691 sel.RangeMain() = rangeNew;
692 SetRectangularRange();
693 ClaimSelection();
694 SetHoverIndicatorPosition(sel.MainCaret());
696 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
697 RedrawSelMargin();
699 QueueIdleWork(WorkNeeded::workUpdateUI);
702 void Editor::SetEmptySelection(int currentPos_) {
703 SetEmptySelection(SelectionPosition(currentPos_));
706 void Editor::MultipleSelectAdd(AddNumber addNumber) {
707 if (SelectionEmpty() || !multipleSelection) {
708 // Select word at caret
709 const int startWord = pdoc->ExtendWordSelect(sel.MainCaret(), -1, true);
710 const int endWord = pdoc->ExtendWordSelect(startWord, 1, true);
711 TrimAndSetSelection(endWord, startWord);
713 } else {
715 if (!pdoc->HasCaseFolder())
716 pdoc->SetCaseFolder(CaseFolderForEncoding());
718 const Range rangeMainSelection(sel.RangeMain().Start().Position(), sel.RangeMain().End().Position());
719 const std::string selectedText = RangeText(rangeMainSelection.start, rangeMainSelection.end);
721 const Range rangeTarget(targetStart, targetEnd);
722 std::vector<Range> searchRanges;
723 // Search should be over the target range excluding the current selection so
724 // may need to search 2 ranges, after the selection then before the selection.
725 if (rangeTarget.Overlaps(rangeMainSelection)) {
726 // Common case is that the selection is completely within the target but
727 // may also have overlap at start or end.
728 if (rangeMainSelection.end < rangeTarget.end)
729 searchRanges.push_back(Range(rangeMainSelection.end, rangeTarget.end));
730 if (rangeTarget.start < rangeMainSelection.start)
731 searchRanges.push_back(Range(rangeTarget.start, rangeMainSelection.start));
732 } else {
733 // No overlap
734 searchRanges.push_back(rangeTarget);
737 for (std::vector<Range>::const_iterator it = searchRanges.begin(); it != searchRanges.end(); ++it) {
738 int searchStart = it->start;
739 const int searchEnd = it->end;
740 for (;;) {
741 int lengthFound = static_cast<int>(selectedText.length());
742 int pos = pdoc->FindText(searchStart, searchEnd, selectedText.c_str(),
743 searchFlags, &lengthFound);
744 if (pos >= 0) {
745 sel.AddSelection(SelectionRange(pos + lengthFound, pos));
746 ScrollRange(sel.RangeMain());
747 Redraw();
748 if (addNumber == addOne)
749 return;
750 searchStart = pos + lengthFound;
751 } else {
752 break;
759 bool Editor::RangeContainsProtected(int start, int end) const {
760 if (vs.ProtectionActive()) {
761 if (start > end) {
762 int t = start;
763 start = end;
764 end = t;
766 for (int pos = start; pos < end; pos++) {
767 if (vs.styles[pdoc->StyleIndexAt(pos)].IsProtected())
768 return true;
771 return false;
774 bool Editor::SelectionContainsProtected() {
775 for (size_t r=0; r<sel.Count(); r++) {
776 if (RangeContainsProtected(sel.Range(r).Start().Position(),
777 sel.Range(r).End().Position())) {
778 return true;
781 return false;
785 * Asks document to find a good position and then moves out of any invisible positions.
787 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
788 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
791 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
792 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
793 if (posMoved != pos.Position())
794 pos.SetPosition(posMoved);
795 if (vs.ProtectionActive()) {
796 if (moveDir > 0) {
797 if ((pos.Position() > 0) && vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()) {
798 while ((pos.Position() < pdoc->Length()) &&
799 (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()))
800 pos.Add(1);
802 } else if (moveDir < 0) {
803 if (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()) {
804 while ((pos.Position() > 0) &&
805 (vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()))
806 pos.Add(-1);
810 return pos;
813 void Editor::MovedCaret(SelectionPosition newPos, SelectionPosition previousPos, bool ensureVisible) {
814 const int currentLine = pdoc->LineFromPosition(newPos.Position());
815 if (ensureVisible) {
816 // In case in need of wrapping to ensure DisplayFromDoc works.
817 if (currentLine >= wrapPending.start)
818 WrapLines(wsAll);
819 XYScrollPosition newXY = XYScrollToMakeVisible(
820 SelectionRange(posDrag.IsValid() ? posDrag : newPos), xysDefault);
821 if (previousPos.IsValid() && (newXY.xOffset == xOffset)) {
822 // simple vertical scroll then invalidate
823 ScrollTo(newXY.topLine);
824 InvalidateSelection(SelectionRange(previousPos), true);
825 } else {
826 SetXYScroll(newXY);
830 ShowCaretAtCurrentPosition();
832 ClaimSelection();
833 SetHoverIndicatorPosition(sel.MainCaret());
834 QueueIdleWork(WorkNeeded::workUpdateUI);
836 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
837 RedrawSelMargin();
841 void Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
842 const SelectionPosition spCaret = ((sel.Count() == 1) && sel.Empty()) ?
843 sel.Last() : SelectionPosition(INVALID_POSITION);
845 int delta = newPos.Position() - sel.MainCaret();
846 newPos = ClampPositionIntoDocument(newPos);
847 newPos = MovePositionOutsideChar(newPos, delta);
848 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
849 // Can't turn into multiple selection so clear additional selections
850 InvalidateSelection(SelectionRange(newPos), true);
851 sel.DropAdditionalRanges();
853 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
854 // Switching to rectangular
855 InvalidateSelection(sel.RangeMain(), false);
856 SelectionRange rangeMain = sel.RangeMain();
857 sel.Clear();
858 sel.Rectangular() = rangeMain;
860 if (selt != Selection::noSel) {
861 sel.selType = selt;
863 if (selt != Selection::noSel || sel.MoveExtends()) {
864 SetSelection(newPos);
865 } else {
866 SetEmptySelection(newPos);
869 MovedCaret(newPos, spCaret, ensureVisible);
872 void Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
873 MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
876 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
877 pos = ClampPositionIntoDocument(pos);
878 pos = MovePositionOutsideChar(pos, moveDir);
879 int lineDoc = pdoc->LineFromPosition(pos.Position());
880 if (cs.GetVisible(lineDoc)) {
881 return pos;
882 } else {
883 int lineDisplay = cs.DisplayFromDoc(lineDoc);
884 if (moveDir > 0) {
885 // lineDisplay is already line before fold as lines in fold use display line of line after fold
886 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
887 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
888 } else {
889 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
890 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
895 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
896 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
899 Point Editor::PointMainCaret() {
900 return LocationFromPosition(sel.Range(sel.Main()).caret);
904 * Choose the x position that the caret will try to stick to
905 * as it moves up and down.
907 void Editor::SetLastXChosen() {
908 Point pt = PointMainCaret();
909 lastXChosen = static_cast<int>(pt.x) + xOffset;
912 void Editor::ScrollTo(int line, bool moveThumb) {
913 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
914 if (topLineNew != topLine) {
915 // Try to optimise small scrolls
916 #ifndef UNDER_CE
917 int linesToMove = topLine - topLineNew;
918 bool performBlit = (abs(linesToMove) <= 10) && (paintState == notPainting);
919 willRedrawAll = !performBlit;
920 #endif
921 SetTopLine(topLineNew);
922 // Optimize by styling the view as this will invalidate any needed area
923 // which could abort the initial paint if discovered later.
924 StyleAreaBounded(GetClientRectangle(), true);
925 #ifndef UNDER_CE
926 // Perform redraw rather than scroll if many lines would be redrawn anyway.
927 if (performBlit) {
928 ScrollText(linesToMove);
929 } else {
930 Redraw();
932 willRedrawAll = false;
933 #else
934 Redraw();
935 #endif
936 if (moveThumb) {
937 SetVerticalScrollPos();
942 void Editor::ScrollText(int /* linesToMove */) {
943 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
944 Redraw();
947 void Editor::HorizontalScrollTo(int xPos) {
948 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
949 if (xPos < 0)
950 xPos = 0;
951 if (!Wrapping() && (xOffset != xPos)) {
952 xOffset = xPos;
953 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
954 SetHorizontalScrollPos();
955 RedrawRect(GetClientRectangle());
959 void Editor::VerticalCentreCaret() {
960 int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
961 int lineDisplay = cs.DisplayFromDoc(lineDoc);
962 int newTop = lineDisplay - (LinesOnScreen() / 2);
963 if (topLine != newTop) {
964 SetTopLine(newTop > 0 ? newTop : 0);
965 RedrawRect(GetClientRectangle());
969 // Avoid 64 bit compiler warnings.
970 // Scintilla does not support text buffers larger than 2**31
971 static int istrlen(const char *s) {
972 return static_cast<int>(s ? strlen(s) : 0);
975 void Editor::MoveSelectedLines(int lineDelta) {
977 // if selection doesn't start at the beginning of the line, set the new start
978 int selectionStart = SelectionStart().Position();
979 int startLine = pdoc->LineFromPosition(selectionStart);
980 int beginningOfStartLine = pdoc->LineStart(startLine);
981 selectionStart = beginningOfStartLine;
983 // if selection doesn't end at the beginning of a line greater than that of the start,
984 // then set it at the beginning of the next one
985 int selectionEnd = SelectionEnd().Position();
986 int endLine = pdoc->LineFromPosition(selectionEnd);
987 int beginningOfEndLine = pdoc->LineStart(endLine);
988 bool appendEol = false;
989 if (selectionEnd > beginningOfEndLine
990 || selectionStart == selectionEnd) {
991 selectionEnd = pdoc->LineStart(endLine + 1);
992 appendEol = (selectionEnd == pdoc->Length() && pdoc->LineFromPosition(selectionEnd) == endLine);
995 // if there's nowhere for the selection to move
996 // (i.e. at the beginning going up or at the end going down),
997 // stop it right there!
998 if ((selectionStart == 0 && lineDelta < 0)
999 || (selectionEnd == pdoc->Length() && lineDelta > 0)
1000 || selectionStart == selectionEnd) {
1001 return;
1004 UndoGroup ug(pdoc);
1006 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
1007 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
1008 ClearSelection();
1009 selectionEnd = CurrentPosition();
1011 SetSelection(selectionStart, selectionEnd);
1013 SelectionText selectedText;
1014 CopySelectionRange(&selectedText);
1016 int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
1017 Point currentLocation = LocationFromPosition(CurrentPosition());
1018 int currentLine = LineFromLocation(currentLocation);
1020 if (appendEol)
1021 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
1022 ClearSelection();
1024 const char *eol = StringFromEOLMode(pdoc->eolMode);
1025 if (currentLine + lineDelta >= pdoc->LinesTotal())
1026 pdoc->InsertString(pdoc->Length(), eol, istrlen(eol));
1027 GoToLine(currentLine + lineDelta);
1029 selectionLength = pdoc->InsertString(CurrentPosition(), selectedText.Data(), selectionLength);
1030 if (appendEol) {
1031 const int lengthInserted = pdoc->InsertString(CurrentPosition() + selectionLength, eol, istrlen(eol));
1032 selectionLength += lengthInserted;
1034 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1037 void Editor::MoveSelectedLinesUp() {
1038 MoveSelectedLines(-1);
1041 void Editor::MoveSelectedLinesDown() {
1042 MoveSelectedLines(1);
1045 void Editor::MoveCaretInsideView(bool ensureVisible) {
1046 PRectangle rcClient = GetTextRectangle();
1047 Point pt = PointMainCaret();
1048 if (pt.y < rcClient.top) {
1049 MovePositionTo(SPositionFromLocation(
1050 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top)),
1051 false, false, UserVirtualSpace()),
1052 Selection::noSel, ensureVisible);
1053 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1054 int yOfLastLineFullyDisplayed = static_cast<int>(rcClient.top) + (LinesOnScreen() - 1) * vs.lineHeight;
1055 MovePositionTo(SPositionFromLocation(
1056 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top) + yOfLastLineFullyDisplayed),
1057 false, false, UserVirtualSpace()),
1058 Selection::noSel, ensureVisible);
1062 int Editor::DisplayFromPosition(int pos) {
1063 AutoSurface surface(this);
1064 return view.DisplayFromPosition(surface, *this, pos, vs);
1068 * Ensure the caret is reasonably visible in context.
1070 Caret policy in SciTE
1072 If slop is set, we can define a slop value.
1073 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1074 This zone is defined as a number of pixels near the vertical margins,
1075 and as a number of lines near the horizontal margins.
1076 By keeping the caret away from the edges, it is seen within its context,
1077 so it is likely that the identifier that the caret is on can be completely seen,
1078 and that the current line is seen with some of the lines following it which are
1079 often dependent on that line.
1081 If strict is set, the policy is enforced... strictly.
1082 The caret is centred on the display if slop is not set,
1083 and cannot go in the UZ if slop is set.
1085 If jumps is set, the display is moved more energetically
1086 so the caret can move in the same direction longer before the policy is applied again.
1087 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1089 If even is not set, instead of having symmetrical UZs,
1090 the left and bottom UZs are extended up to right and top UZs respectively.
1091 This way, we favour the displaying of useful information: the beginning of lines,
1092 where most code reside, and the lines after the caret, eg. the body of a function.
1094 | | | | |
1095 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1096 | | | | | visibility or going into the UZ) display is...
1097 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1098 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1099 0 | 0 | 0 | 1 | Yes | moved by one position
1100 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1101 0 | 0 | 1 | 1 | Yes | centred on the caret
1102 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1103 0 | 1 | - | 1 | No, caret is always centred | -
1104 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1105 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1106 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1107 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1108 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1109 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1110 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1113 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange &range, const XYScrollOptions options) {
1114 PRectangle rcClient = GetTextRectangle();
1115 Point pt = LocationFromPosition(range.caret);
1116 Point ptAnchor = LocationFromPosition(range.anchor);
1117 const Point ptOrigin = GetVisibleOriginInMain();
1118 pt.x += ptOrigin.x;
1119 pt.y += ptOrigin.y;
1120 ptAnchor.x += ptOrigin.x;
1121 ptAnchor.y += ptOrigin.y;
1122 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1124 XYScrollPosition newXY(xOffset, topLine);
1125 if (rcClient.Empty()) {
1126 return newXY;
1129 // Vertical positioning
1130 if ((options & xysVertical) && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1131 const int lineCaret = DisplayFromPosition(range.caret.Position());
1132 const int linesOnScreen = LinesOnScreen();
1133 const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1134 const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
1135 const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
1136 const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
1137 const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
1139 // It should be possible to scroll the window to show the caret,
1140 // but this fails to remove the caret on GTK+
1141 if (bSlop) { // A margin is defined
1142 int yMoveT, yMoveB;
1143 if (bStrict) {
1144 int yMarginT, yMarginB;
1145 if (!(options & xysUseMargin)) {
1146 // In drag mode, avoid moves
1147 // otherwise, a double click will select several lines.
1148 yMarginT = yMarginB = 0;
1149 } else {
1150 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1151 // a maximum of slightly less than half the heigth of the text area.
1152 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1153 if (bEven) {
1154 yMarginB = yMarginT;
1155 } else {
1156 yMarginB = linesOnScreen - yMarginT - 1;
1159 yMoveT = yMarginT;
1160 if (bEven) {
1161 if (bJump) {
1162 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1164 yMoveB = yMoveT;
1165 } else {
1166 yMoveB = linesOnScreen - yMoveT - 1;
1168 if (lineCaret < topLine + yMarginT) {
1169 // Caret goes too high
1170 newXY.topLine = lineCaret - yMoveT;
1171 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1172 // Caret goes too low
1173 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1175 } else { // Not strict
1176 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1177 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1178 if (bEven) {
1179 yMoveB = yMoveT;
1180 } else {
1181 yMoveB = linesOnScreen - yMoveT - 1;
1183 if (lineCaret < topLine) {
1184 // Caret goes too high
1185 newXY.topLine = lineCaret - yMoveT;
1186 } else if (lineCaret > topLine + linesOnScreen - 1) {
1187 // Caret goes too low
1188 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1191 } else { // No slop
1192 if (!bStrict && !bJump) {
1193 // Minimal move
1194 if (lineCaret < topLine) {
1195 // Caret goes too high
1196 newXY.topLine = lineCaret;
1197 } else if (lineCaret > topLine + linesOnScreen - 1) {
1198 // Caret goes too low
1199 if (bEven) {
1200 newXY.topLine = lineCaret - linesOnScreen + 1;
1201 } else {
1202 newXY.topLine = lineCaret;
1205 } else { // Strict or going out of display
1206 if (bEven) {
1207 // Always center caret
1208 newXY.topLine = lineCaret - halfScreen;
1209 } else {
1210 // Always put caret on top of display
1211 newXY.topLine = lineCaret;
1215 if (!(range.caret == range.anchor)) {
1216 const int lineAnchor = DisplayFromPosition(range.anchor.Position());
1217 if (lineAnchor < lineCaret) {
1218 // Shift up to show anchor or as much of range as possible
1219 newXY.topLine = std::min(newXY.topLine, lineAnchor);
1220 newXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());
1221 } else {
1222 // Shift down to show anchor or as much of range as possible
1223 newXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());
1224 newXY.topLine = std::min(newXY.topLine, lineCaret);
1227 newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
1230 // Horizontal positioning
1231 if ((options & xysHorizontal) && !Wrapping()) {
1232 const int halfScreen = Platform::Maximum(static_cast<int>(rcClient.Width()) - 4, 4) / 2;
1233 const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
1234 const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
1235 const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
1236 const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
1238 if (bSlop) { // A margin is defined
1239 int xMoveL, xMoveR;
1240 if (bStrict) {
1241 int xMarginL, xMarginR;
1242 if (!(options & xysUseMargin)) {
1243 // In drag mode, avoid moves unless very near of the margin
1244 // otherwise, a simple click will select text.
1245 xMarginL = xMarginR = 2;
1246 } else {
1247 // xMargin must equal to caretXSlop, with a minimum of 2 and
1248 // a maximum of slightly less than half the width of the text area.
1249 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1250 if (bEven) {
1251 xMarginL = xMarginR;
1252 } else {
1253 xMarginL = static_cast<int>(rcClient.Width()) - xMarginR - 4;
1256 if (bJump && bEven) {
1257 // Jump is used only in even mode
1258 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1259 } else {
1260 xMoveL = xMoveR = 0; // Not used, avoid a warning
1262 if (pt.x < rcClient.left + xMarginL) {
1263 // Caret is on the left of the display
1264 if (bJump && bEven) {
1265 newXY.xOffset -= xMoveL;
1266 } else {
1267 // Move just enough to allow to display the caret
1268 newXY.xOffset -= static_cast<int>((rcClient.left + xMarginL) - pt.x);
1270 } else if (pt.x >= rcClient.right - xMarginR) {
1271 // Caret is on the right of the display
1272 if (bJump && bEven) {
1273 newXY.xOffset += xMoveR;
1274 } else {
1275 // Move just enough to allow to display the caret
1276 newXY.xOffset += static_cast<int>(pt.x - (rcClient.right - xMarginR) + 1);
1279 } else { // Not strict
1280 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1281 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1282 if (bEven) {
1283 xMoveL = xMoveR;
1284 } else {
1285 xMoveL = static_cast<int>(rcClient.Width()) - xMoveR - 4;
1287 if (pt.x < rcClient.left) {
1288 // Caret is on the left of the display
1289 newXY.xOffset -= xMoveL;
1290 } else if (pt.x >= rcClient.right) {
1291 // Caret is on the right of the display
1292 newXY.xOffset += xMoveR;
1295 } else { // No slop
1296 if (bStrict ||
1297 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1298 // Strict or going out of display
1299 if (bEven) {
1300 // Center caret
1301 newXY.xOffset += static_cast<int>(pt.x - rcClient.left - halfScreen);
1302 } else {
1303 // Put caret on right
1304 newXY.xOffset += static_cast<int>(pt.x - rcClient.right + 1);
1306 } else {
1307 // Move just enough to allow to display the caret
1308 if (pt.x < rcClient.left) {
1309 // Caret is on the left of the display
1310 if (bEven) {
1311 newXY.xOffset -= static_cast<int>(rcClient.left - pt.x);
1312 } else {
1313 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1315 } else if (pt.x >= rcClient.right) {
1316 // Caret is on the right of the display
1317 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1321 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1322 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1323 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 2;
1324 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1325 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 2;
1326 if ((vs.caretStyle == CARETSTYLE_BLOCK) || view.imeCaretBlockOverride) {
1327 // Ensure we can see a good portion of the block caret
1328 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1331 if (!(range.caret == range.anchor)) {
1332 if (ptAnchor.x < pt.x) {
1333 // Shift to left to show anchor or as much of range as possible
1334 int maxOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.left) - 1;
1335 int minOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 1;
1336 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1337 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1338 } else {
1339 // Shift to right to show anchor or as much of range as possible
1340 int minOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.right) + 1;
1341 int maxOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 1;
1342 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1343 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1346 if (newXY.xOffset < 0) {
1347 newXY.xOffset = 0;
1351 return newXY;
1354 void Editor::SetXYScroll(XYScrollPosition newXY) {
1355 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1356 if (newXY.topLine != topLine) {
1357 SetTopLine(newXY.topLine);
1358 SetVerticalScrollPos();
1360 if (newXY.xOffset != xOffset) {
1361 xOffset = newXY.xOffset;
1362 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1363 if (newXY.xOffset > 0) {
1364 PRectangle rcText = GetTextRectangle();
1365 if (horizontalScrollBarVisible &&
1366 rcText.Width() + xOffset > scrollWidth) {
1367 scrollWidth = xOffset + static_cast<int>(rcText.Width());
1368 SetScrollBars();
1371 SetHorizontalScrollPos();
1373 Redraw();
1374 UpdateSystemCaret();
1378 void Editor::ScrollRange(SelectionRange range) {
1379 SetXYScroll(XYScrollToMakeVisible(range, xysDefault));
1382 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1383 SetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret),
1384 static_cast<XYScrollOptions>((useMargin?xysUseMargin:0)|(vert?xysVertical:0)|(horiz?xysHorizontal:0))));
1387 void Editor::ShowCaretAtCurrentPosition() {
1388 if (hasFocus) {
1389 caret.active = true;
1390 caret.on = true;
1391 if (FineTickerAvailable()) {
1392 FineTickerCancel(tickCaret);
1393 if (caret.period > 0)
1394 FineTickerStart(tickCaret, caret.period, caret.period/10);
1395 } else {
1396 SetTicking(true);
1398 } else {
1399 caret.active = false;
1400 caret.on = false;
1401 if (FineTickerAvailable()) {
1402 FineTickerCancel(tickCaret);
1405 InvalidateCaret();
1408 void Editor::DropCaret() {
1409 caret.active = false;
1410 if (FineTickerAvailable()) {
1411 FineTickerCancel(tickCaret);
1413 InvalidateCaret();
1416 void Editor::CaretSetPeriod(int period) {
1417 if (caret.period != period) {
1418 caret.period = period;
1419 caret.on = true;
1420 if (FineTickerAvailable()) {
1421 FineTickerCancel(tickCaret);
1422 if ((caret.active) && (caret.period > 0))
1423 FineTickerStart(tickCaret, caret.period, caret.period/10);
1425 InvalidateCaret();
1429 void Editor::InvalidateCaret() {
1430 if (posDrag.IsValid()) {
1431 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1432 } else {
1433 for (size_t r=0; r<sel.Count(); r++) {
1434 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1437 UpdateSystemCaret();
1440 void Editor::UpdateSystemCaret() {
1443 bool Editor::Wrapping() const {
1444 return vs.wrapState != eWrapNone;
1447 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1448 //Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd);
1449 if (wrapPending.AddRange(docLineStart, docLineEnd)) {
1450 view.llc.Invalidate(LineLayout::llPositions);
1452 // Wrap lines during idle.
1453 if (Wrapping() && wrapPending.NeedsWrap()) {
1454 SetIdle(true);
1458 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1459 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(lineToWrap, *this));
1460 int linesWrapped = 1;
1461 if (ll) {
1462 view.LayoutLine(*this, lineToWrap, surface, vs, ll, wrapWidth);
1463 linesWrapped = ll->lines;
1465 return cs.SetHeight(lineToWrap, linesWrapped +
1466 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1469 // Perform wrapping for a subset of the lines needing wrapping.
1470 // wsAll: wrap all lines which need wrapping in this single call
1471 // wsVisible: wrap currently visible lines
1472 // wsIdle: wrap one page + 100 lines
1473 // Return true if wrapping occurred.
1474 bool Editor::WrapLines(enum wrapScope ws) {
1475 int goodTopLine = topLine;
1476 bool wrapOccurred = false;
1477 if (!Wrapping()) {
1478 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1479 wrapWidth = LineLayout::wrapWidthInfinite;
1480 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1481 cs.SetHeight(lineDoc, 1 +
1482 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1484 wrapOccurred = true;
1486 wrapPending.Reset();
1488 } else if (wrapPending.NeedsWrap()) {
1489 wrapPending.start = std::min(wrapPending.start, pdoc->LinesTotal());
1490 if (!SetIdle(true)) {
1491 // Idle processing not supported so full wrap required.
1492 ws = wsAll;
1494 // Decide where to start wrapping
1495 int lineToWrap = wrapPending.start;
1496 int lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal());
1497 const int lineDocTop = cs.DocFromDisplay(topLine);
1498 const int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1499 if (ws == wsVisible) {
1500 lineToWrap = Platform::Clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal());
1501 // Priority wrap to just after visible area.
1502 // Since wrapping could reduce display lines, treat each
1503 // as taking only one display line.
1504 lineToWrapEnd = lineDocTop;
1505 int lines = LinesOnScreen() + 1;
1506 while ((lineToWrapEnd < cs.LinesInDoc()) && (lines>0)) {
1507 if (cs.GetVisible(lineToWrapEnd))
1508 lines--;
1509 lineToWrapEnd++;
1511 // .. and if the paint window is outside pending wraps
1512 if ((lineToWrap > wrapPending.end) || (lineToWrapEnd < wrapPending.start)) {
1513 // Currently visible text does not need wrapping
1514 return false;
1516 } else if (ws == wsIdle) {
1517 lineToWrapEnd = lineToWrap + LinesOnScreen() + 100;
1519 const int lineEndNeedWrap = std::min(wrapPending.end, pdoc->LinesTotal());
1520 lineToWrapEnd = std::min(lineToWrapEnd, lineEndNeedWrap);
1522 // Ensure all lines being wrapped are styled.
1523 pdoc->EnsureStyledTo(pdoc->LineStart(lineToWrapEnd));
1525 if (lineToWrap < lineToWrapEnd) {
1527 PRectangle rcTextArea = GetClientRectangle();
1528 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1529 rcTextArea.right -= vs.rightMarginWidth;
1530 wrapWidth = static_cast<int>(rcTextArea.Width());
1531 RefreshStyleData();
1532 AutoSurface surface(this);
1533 if (surface) {
1534 //Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
1536 while (lineToWrap < lineToWrapEnd) {
1537 if (WrapOneLine(surface, lineToWrap)) {
1538 wrapOccurred = true;
1540 wrapPending.Wrapped(lineToWrap);
1541 lineToWrap++;
1544 goodTopLine = cs.DisplayFromDoc(lineDocTop) + std::min(subLineTop, cs.GetHeight(lineDocTop)-1);
1548 // If wrapping is done, bring it to resting position
1549 if (wrapPending.start >= lineEndNeedWrap) {
1550 wrapPending.Reset();
1554 if (wrapOccurred) {
1555 SetScrollBars();
1556 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1557 SetVerticalScrollPos();
1560 return wrapOccurred;
1563 void Editor::LinesJoin() {
1564 if (!RangeContainsProtected(targetStart, targetEnd)) {
1565 UndoGroup ug(pdoc);
1566 bool prevNonWS = true;
1567 for (int pos = targetStart; pos < targetEnd; pos++) {
1568 if (pdoc->IsPositionInLineEnd(pos)) {
1569 targetEnd -= pdoc->LenChar(pos);
1570 pdoc->DelChar(pos);
1571 if (prevNonWS) {
1572 // Ensure at least one space separating previous lines
1573 const int lengthInserted = pdoc->InsertString(pos, " ", 1);
1574 targetEnd += lengthInserted;
1576 } else {
1577 prevNonWS = pdoc->CharAt(pos) != ' ';
1583 const char *Editor::StringFromEOLMode(int eolMode) {
1584 if (eolMode == SC_EOL_CRLF) {
1585 return "\r\n";
1586 } else if (eolMode == SC_EOL_CR) {
1587 return "\r";
1588 } else {
1589 return "\n";
1593 void Editor::LinesSplit(int pixelWidth) {
1594 if (!RangeContainsProtected(targetStart, targetEnd)) {
1595 if (pixelWidth == 0) {
1596 PRectangle rcText = GetTextRectangle();
1597 pixelWidth = static_cast<int>(rcText.Width());
1599 int lineStart = pdoc->LineFromPosition(targetStart);
1600 int lineEnd = pdoc->LineFromPosition(targetEnd);
1601 const char *eol = StringFromEOLMode(pdoc->eolMode);
1602 UndoGroup ug(pdoc);
1603 for (int line = lineStart; line <= lineEnd; line++) {
1604 AutoSurface surface(this);
1605 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
1606 if (surface && ll) {
1607 unsigned int posLineStart = pdoc->LineStart(line);
1608 view.LayoutLine(*this, line, surface, vs, ll, pixelWidth);
1609 int lengthInsertedTotal = 0;
1610 for (int subLine = 1; subLine < ll->lines; subLine++) {
1611 const int lengthInserted = pdoc->InsertString(
1612 static_cast<int>(posLineStart + lengthInsertedTotal +
1613 ll->LineStart(subLine)),
1614 eol, istrlen(eol));
1615 targetEnd += lengthInserted;
1616 lengthInsertedTotal += lengthInserted;
1619 lineEnd = pdoc->LineFromPosition(targetEnd);
1624 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1625 if (vs.fixedColumnWidth == 0)
1626 return;
1628 AllocateGraphics();
1629 RefreshStyleData();
1630 RefreshPixMaps(surfWindow);
1632 // On GTK+ with Ubuntu overlay scroll bars, the surface may have been finished
1633 // at this point. The Initialised call checks for this case and sets the status
1634 // to be bad which avoids crashes in following calls.
1635 if (!surfWindow->Initialised()) {
1636 return;
1639 PRectangle rcMargin = GetClientRectangle();
1640 Point ptOrigin = GetVisibleOriginInMain();
1641 rcMargin.Move(0, -ptOrigin.y);
1642 rcMargin.left = 0;
1643 rcMargin.right = static_cast<XYPOSITION>(vs.fixedColumnWidth);
1645 if (!rc.Intersects(rcMargin))
1646 return;
1648 Surface *surface;
1649 if (view.bufferedDraw) {
1650 surface = marginView.pixmapSelMargin;
1651 } else {
1652 surface = surfWindow;
1655 // Clip vertically to paint area to avoid drawing line numbers
1656 if (rcMargin.bottom > rc.bottom)
1657 rcMargin.bottom = rc.bottom;
1658 if (rcMargin.top < rc.top)
1659 rcMargin.top = rc.top;
1661 marginView.PaintMargin(surface, topLine, rc, rcMargin, *this, vs);
1663 if (view.bufferedDraw) {
1664 surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *marginView.pixmapSelMargin);
1668 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
1669 view.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1670 marginView.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1671 if (view.bufferedDraw) {
1672 PRectangle rcClient = GetClientRectangle();
1673 if (!view.pixmapLine->Initialised()) {
1675 view.pixmapLine->InitPixMap(static_cast<int>(rcClient.Width()), vs.lineHeight,
1676 surfaceWindow, wMain.GetID());
1678 if (!marginView.pixmapSelMargin->Initialised()) {
1679 marginView.pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
1680 static_cast<int>(rcClient.Height()), surfaceWindow, wMain.GetID());
1685 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
1686 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
1687 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
1688 AllocateGraphics();
1690 RefreshStyleData();
1691 if (paintState == paintAbandoned)
1692 return; // Scroll bars may have changed so need redraw
1693 RefreshPixMaps(surfaceWindow);
1695 paintAbandonedByStyling = false;
1697 StyleAreaBounded(rcArea, false);
1699 PRectangle rcClient = GetClientRectangle();
1700 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
1701 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1703 if (NotifyUpdateUI()) {
1704 RefreshStyleData();
1705 RefreshPixMaps(surfaceWindow);
1708 // Wrap the visible lines if needed.
1709 if (WrapLines(wsVisible)) {
1710 // The wrapping process has changed the height of some lines so
1711 // abandon this paint for a complete repaint.
1712 if (AbandonPaint()) {
1713 return;
1715 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
1717 PLATFORM_ASSERT(marginView.pixmapSelPattern->Initialised());
1719 if (!view.bufferedDraw)
1720 surfaceWindow->SetClip(rcArea);
1722 if (paintState != paintAbandoned) {
1723 if (vs.marginInside) {
1724 PaintSelMargin(surfaceWindow, rcArea);
1725 PRectangle rcRightMargin = rcClient;
1726 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
1727 if (rcArea.Intersects(rcRightMargin)) {
1728 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
1730 } else { // Else separate view so separate paint event but leftMargin included to allow overlap
1731 PRectangle rcLeftMargin = rcArea;
1732 rcLeftMargin.left = 0;
1733 rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
1734 if (rcArea.Intersects(rcLeftMargin)) {
1735 surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[STYLE_DEFAULT].back);
1740 if (paintState == paintAbandoned) {
1741 // Either styling or NotifyUpdateUI noticed that painting is needed
1742 // outside the current painting rectangle
1743 //Platform::DebugPrintf("Abandoning paint\n");
1744 if (Wrapping()) {
1745 if (paintAbandonedByStyling) {
1746 // Styling has spilled over a line end, such as occurs by starting a multiline
1747 // comment. The width of subsequent text may have changed, so rewrap.
1748 NeedWrapping(cs.DocFromDisplay(topLine));
1751 return;
1754 view.PaintText(surfaceWindow, *this, rcArea, rcClient, vs);
1756 if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) {
1757 if (FineTickerAvailable()) {
1758 scrollWidth = view.lineWidthMaxSeen;
1759 if (!FineTickerRunning(tickWiden)) {
1760 FineTickerStart(tickWiden, 50, 5);
1765 NotifyPainted();
1768 // This is mostly copied from the Paint method but with some things omitted
1769 // such as the margin markers, line numbers, selection and caret
1770 // Should be merged back into a combined Draw method.
1771 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
1772 if (!pfr)
1773 return 0;
1775 AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
1776 if (!surface)
1777 return 0;
1778 AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
1779 if (!surfaceMeasure) {
1780 return 0;
1782 return view.FormatRange(draw, pfr, surface, surfaceMeasure, *this, vs);
1785 int Editor::TextWidth(int style, const char *text) {
1786 RefreshStyleData();
1787 AutoSurface surface(this);
1788 if (surface) {
1789 return static_cast<int>(surface->WidthText(vs.styles[style].font, text, istrlen(text)));
1790 } else {
1791 return 1;
1795 // Empty method is overridden on GTK+ to show / hide scrollbars
1796 void Editor::ReconfigureScrollBars() {}
1798 void Editor::SetScrollBars() {
1799 RefreshStyleData();
1801 int nMax = MaxScrollPos();
1802 int nPage = LinesOnScreen();
1803 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
1804 if (modified) {
1805 DwellEnd(true);
1808 // TODO: ensure always showing as many lines as possible
1809 // May not be, if, for example, window made larger
1810 if (topLine > MaxScrollPos()) {
1811 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
1812 SetVerticalScrollPos();
1813 Redraw();
1815 if (modified) {
1816 if (!AbandonPaint())
1817 Redraw();
1819 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
1822 void Editor::ChangeSize() {
1823 DropGraphics(false);
1824 SetScrollBars();
1825 if (Wrapping()) {
1826 PRectangle rcTextArea = GetClientRectangle();
1827 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1828 rcTextArea.right -= vs.rightMarginWidth;
1829 if (wrapWidth != rcTextArea.Width()) {
1830 NeedWrapping();
1831 Redraw();
1836 int Editor::InsertSpace(int position, unsigned int spaces) {
1837 if (spaces > 0) {
1838 std::string spaceText(spaces, ' ');
1839 const int lengthInserted = pdoc->InsertString(position, spaceText.c_str(), spaces);
1840 position += lengthInserted;
1842 return position;
1845 void Editor::AddChar(char ch) {
1846 char s[2];
1847 s[0] = ch;
1848 s[1] = '\0';
1849 AddCharUTF(s, 1);
1852 void Editor::FilterSelections() {
1853 if (!additionalSelectionTyping && (sel.Count() > 1)) {
1854 InvalidateWholeSelection();
1855 sel.DropAdditionalRanges();
1859 static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
1860 return *a < *b;
1863 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
1864 void Editor::AddCharUTF(const char *s, unsigned int len, bool treatAsDBCS) {
1865 FilterSelections();
1867 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
1869 // Vector elements point into selection in order to change selection.
1870 std::vector<SelectionRange *> selPtrs;
1871 for (size_t r = 0; r < sel.Count(); r++) {
1872 selPtrs.push_back(&sel.Range(r));
1874 // Order selections by position in document.
1875 std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
1877 // Loop in reverse to avoid disturbing positions of selections yet to be processed.
1878 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
1879 rit != selPtrs.rend(); ++rit) {
1880 SelectionRange *currentSel = *rit;
1881 if (!RangeContainsProtected(currentSel->Start().Position(),
1882 currentSel->End().Position())) {
1883 int positionInsert = currentSel->Start().Position();
1884 if (!currentSel->Empty()) {
1885 if (currentSel->Length()) {
1886 pdoc->DeleteChars(positionInsert, currentSel->Length());
1887 currentSel->ClearVirtualSpace();
1888 } else {
1889 // Range is all virtual so collapse to start of virtual space
1890 currentSel->MinimizeVirtualSpace();
1892 } else if (inOverstrike) {
1893 if (positionInsert < pdoc->Length()) {
1894 if (!pdoc->IsPositionInLineEnd(positionInsert)) {
1895 pdoc->DelChar(positionInsert);
1896 currentSel->ClearVirtualSpace();
1900 positionInsert = InsertSpace(positionInsert, currentSel->caret.VirtualSpace());
1901 const int lengthInserted = pdoc->InsertString(positionInsert, s, len);
1902 if (lengthInserted > 0) {
1903 currentSel->caret.SetPosition(positionInsert + lengthInserted);
1904 currentSel->anchor.SetPosition(positionInsert + lengthInserted);
1906 currentSel->ClearVirtualSpace();
1907 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1908 if (Wrapping()) {
1909 AutoSurface surface(this);
1910 if (surface) {
1911 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
1912 SetScrollBars();
1913 SetVerticalScrollPos();
1914 Redraw();
1921 if (Wrapping()) {
1922 SetScrollBars();
1924 ThinRectangularRange();
1925 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1926 EnsureCaretVisible();
1927 // Avoid blinking during rapid typing:
1928 ShowCaretAtCurrentPosition();
1929 if ((caretSticky == SC_CARETSTICKY_OFF) ||
1930 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
1931 SetLastXChosen();
1934 if (treatAsDBCS) {
1935 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
1936 static_cast<unsigned char>(s[1]));
1937 } else if (len > 0) {
1938 int byte = static_cast<unsigned char>(s[0]);
1939 if ((byte < 0xC0) || (1 == len)) {
1940 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
1941 // characters when not in UTF-8 mode.
1942 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
1943 // characters representing themselves.
1944 } else {
1945 unsigned int utf32[1] = { 0 };
1946 UTF32FromUTF8(s, len, utf32, ELEMENTS(utf32));
1947 byte = utf32[0];
1949 NotifyChar(byte);
1952 if (recordingMacro) {
1953 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
1957 void Editor::ClearBeforeTentativeStart() {
1958 // Make positions for the first composition string.
1959 FilterSelections();
1960 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
1961 for (size_t r = 0; r<sel.Count(); r++) {
1962 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
1963 sel.Range(r).End().Position())) {
1964 int positionInsert = sel.Range(r).Start().Position();
1965 if (!sel.Range(r).Empty()) {
1966 if (sel.Range(r).Length()) {
1967 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
1968 sel.Range(r).ClearVirtualSpace();
1969 } else {
1970 // Range is all virtual so collapse to start of virtual space
1971 sel.Range(r).MinimizeVirtualSpace();
1974 InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
1975 sel.Range(r).ClearVirtualSpace();
1980 void Editor::InsertPaste(const char *text, int len) {
1981 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
1982 SelectionPosition selStart = sel.Start();
1983 selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
1984 const int lengthInserted = pdoc->InsertString(selStart.Position(), text, len);
1985 if (lengthInserted > 0) {
1986 SetEmptySelection(selStart.Position() + lengthInserted);
1988 } else {
1989 // SC_MULTIPASTE_EACH
1990 for (size_t r=0; r<sel.Count(); r++) {
1991 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
1992 sel.Range(r).End().Position())) {
1993 int positionInsert = sel.Range(r).Start().Position();
1994 if (!sel.Range(r).Empty()) {
1995 if (sel.Range(r).Length()) {
1996 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
1997 sel.Range(r).ClearVirtualSpace();
1998 } else {
1999 // Range is all virtual so collapse to start of virtual space
2000 sel.Range(r).MinimizeVirtualSpace();
2003 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
2004 const int lengthInserted = pdoc->InsertString(positionInsert, text, len);
2005 if (lengthInserted > 0) {
2006 sel.Range(r).caret.SetPosition(positionInsert + lengthInserted);
2007 sel.Range(r).anchor.SetPosition(positionInsert + lengthInserted);
2009 sel.Range(r).ClearVirtualSpace();
2015 void Editor::InsertPasteShape(const char *text, int len, PasteShape shape) {
2016 std::string convertedText;
2017 if (convertPastes) {
2018 // Convert line endings of the paste into our local line-endings mode
2019 convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
2020 len = static_cast<int>(convertedText.length());
2021 text = convertedText.c_str();
2023 if (shape == pasteRectangular) {
2024 PasteRectangular(sel.Start(), text, len);
2025 } else {
2026 if (shape == pasteLine) {
2027 int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
2028 int lengthInserted = pdoc->InsertString(insertPos, text, len);
2029 // add the newline if necessary
2030 if ((len > 0) && (text[len - 1] != '\n' && text[len - 1] != '\r')) {
2031 const char *endline = StringFromEOLMode(pdoc->eolMode);
2032 int length = static_cast<int>(strlen(endline));
2033 lengthInserted += pdoc->InsertString(insertPos + lengthInserted, endline, length);
2035 if (sel.MainCaret() == insertPos) {
2036 SetEmptySelection(sel.MainCaret() + lengthInserted);
2038 } else {
2039 InsertPaste(text, len);
2044 void Editor::ClearSelection(bool retainMultipleSelections) {
2045 if (!sel.IsRectangular() && !retainMultipleSelections)
2046 FilterSelections();
2047 UndoGroup ug(pdoc);
2048 for (size_t r=0; r<sel.Count(); r++) {
2049 if (!sel.Range(r).Empty()) {
2050 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
2051 sel.Range(r).End().Position())) {
2052 pdoc->DeleteChars(sel.Range(r).Start().Position(),
2053 sel.Range(r).Length());
2054 sel.Range(r) = SelectionRange(sel.Range(r).Start());
2058 ThinRectangularRange();
2059 sel.RemoveDuplicates();
2060 ClaimSelection();
2061 SetHoverIndicatorPosition(sel.MainCaret());
2064 void Editor::ClearAll() {
2066 UndoGroup ug(pdoc);
2067 if (0 != pdoc->Length()) {
2068 pdoc->DeleteChars(0, pdoc->Length());
2070 if (!pdoc->IsReadOnly()) {
2071 cs.Clear();
2072 pdoc->AnnotationClearAll();
2073 pdoc->MarginClearAll();
2077 view.ClearAllTabstops();
2079 sel.Clear();
2080 SetTopLine(0);
2081 SetVerticalScrollPos();
2082 InvalidateStyleRedraw();
2085 void Editor::ClearDocumentStyle() {
2086 Decoration *deco = pdoc->decorations.root;
2087 while (deco) {
2088 // Save next in case deco deleted
2089 Decoration *decoNext = deco->next;
2090 if (deco->indicator < INDIC_CONTAINER) {
2091 pdoc->decorations.SetCurrentIndicator(deco->indicator);
2092 pdoc->DecorationFillRange(0, 0, pdoc->Length());
2094 deco = decoNext;
2096 pdoc->StartStyling(0, '\377');
2097 pdoc->SetStyleFor(pdoc->Length(), 0);
2098 cs.ShowAll();
2099 SetAnnotationHeights(0, pdoc->LinesTotal());
2100 pdoc->ClearLevels();
2103 void Editor::CopyAllowLine() {
2104 SelectionText selectedText;
2105 CopySelectionRange(&selectedText, true);
2106 CopyToClipboard(selectedText);
2109 void Editor::Cut() {
2110 pdoc->CheckReadOnly();
2111 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
2112 Copy();
2113 ClearSelection();
2117 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
2118 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
2119 return;
2121 sel.Clear();
2122 sel.RangeMain() = SelectionRange(pos);
2123 int line = pdoc->LineFromPosition(sel.MainCaret());
2124 UndoGroup ug(pdoc);
2125 sel.RangeMain().caret = SelectionPosition(
2126 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
2127 int xInsert = XFromPosition(sel.RangeMain().caret);
2128 bool prevCr = false;
2129 while ((len > 0) && IsEOLChar(ptr[len-1]))
2130 len--;
2131 for (int i = 0; i < len; i++) {
2132 if (IsEOLChar(ptr[i])) {
2133 if ((ptr[i] == '\r') || (!prevCr))
2134 line++;
2135 if (line >= pdoc->LinesTotal()) {
2136 if (pdoc->eolMode != SC_EOL_LF)
2137 pdoc->InsertString(pdoc->Length(), "\r", 1);
2138 if (pdoc->eolMode != SC_EOL_CR)
2139 pdoc->InsertString(pdoc->Length(), "\n", 1);
2141 // Pad the end of lines with spaces if required
2142 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
2143 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
2144 while (XFromPosition(sel.MainCaret()) < xInsert) {
2145 assert(pdoc);
2146 const int lengthInserted = pdoc->InsertString(sel.MainCaret(), " ", 1);
2147 sel.RangeMain().caret.Add(lengthInserted);
2150 prevCr = ptr[i] == '\r';
2151 } else {
2152 const int lengthInserted = pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
2153 sel.RangeMain().caret.Add(lengthInserted);
2154 prevCr = false;
2157 SetEmptySelection(pos);
2160 bool Editor::CanPaste() {
2161 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
2164 void Editor::Clear() {
2165 // If multiple selections, don't delete EOLS
2166 if (sel.Empty()) {
2167 bool singleVirtual = false;
2168 if ((sel.Count() == 1) &&
2169 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
2170 sel.RangeMain().Start().VirtualSpace()) {
2171 singleVirtual = true;
2173 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
2174 for (size_t r=0; r<sel.Count(); r++) {
2175 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
2176 if (sel.Range(r).Start().VirtualSpace()) {
2177 if (sel.Range(r).anchor < sel.Range(r).caret)
2178 sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
2179 else
2180 sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
2182 if ((sel.Count() == 1) || !pdoc->IsPositionInLineEnd(sel.Range(r).caret.Position())) {
2183 pdoc->DelChar(sel.Range(r).caret.Position());
2184 sel.Range(r).ClearVirtualSpace();
2185 } // else multiple selection so don't eat line ends
2186 } else {
2187 sel.Range(r).ClearVirtualSpace();
2190 } else {
2191 ClearSelection();
2193 sel.RemoveDuplicates();
2194 ShowCaretAtCurrentPosition(); // Avoid blinking
2197 void Editor::SelectAll() {
2198 sel.Clear();
2199 SetSelection(0, pdoc->Length());
2200 Redraw();
2203 void Editor::Undo() {
2204 if (pdoc->CanUndo()) {
2205 InvalidateCaret();
2206 int newPos = pdoc->Undo();
2207 if (newPos >= 0)
2208 SetEmptySelection(newPos);
2209 EnsureCaretVisible();
2213 void Editor::Redo() {
2214 if (pdoc->CanRedo()) {
2215 int newPos = pdoc->Redo();
2216 if (newPos >= 0)
2217 SetEmptySelection(newPos);
2218 EnsureCaretVisible();
2222 void Editor::DelCharBack(bool allowLineStartDeletion) {
2223 RefreshStyleData();
2224 if (!sel.IsRectangular())
2225 FilterSelections();
2226 if (sel.IsRectangular())
2227 allowLineStartDeletion = false;
2228 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
2229 if (sel.Empty()) {
2230 for (size_t r=0; r<sel.Count(); r++) {
2231 if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
2232 if (sel.Range(r).caret.VirtualSpace()) {
2233 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
2234 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
2235 } else {
2236 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
2237 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
2238 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
2239 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
2240 UndoGroup ugInner(pdoc, !ug.Needed());
2241 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
2242 int indentationStep = pdoc->IndentSize();
2243 int indentationChange = indentation % indentationStep;
2244 if (indentationChange == 0)
2245 indentationChange = indentationStep;
2246 const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationChange);
2247 // SetEmptySelection
2248 sel.Range(r) = SelectionRange(posSelect);
2249 } else {
2250 pdoc->DelCharBack(sel.Range(r).caret.Position());
2254 } else {
2255 sel.Range(r).ClearVirtualSpace();
2258 ThinRectangularRange();
2259 } else {
2260 ClearSelection();
2262 sel.RemoveDuplicates();
2263 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
2264 // Avoid blinking during rapid typing:
2265 ShowCaretAtCurrentPosition();
2268 int Editor::ModifierFlags(bool shift, bool ctrl, bool alt, bool meta) {
2269 return
2270 (shift ? SCI_SHIFT : 0) |
2271 (ctrl ? SCI_CTRL : 0) |
2272 (alt ? SCI_ALT : 0) |
2273 (meta ? SCI_META : 0);
2276 void Editor::NotifyFocus(bool focus) {
2277 SCNotification scn = {};
2278 scn.nmhdr.code = focus ? SCN_FOCUSIN : SCN_FOCUSOUT;
2279 NotifyParent(scn);
2282 void Editor::SetCtrlID(int identifier) {
2283 ctrlID = identifier;
2286 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
2287 SCNotification scn = {};
2288 scn.nmhdr.code = SCN_STYLENEEDED;
2289 scn.position = endStyleNeeded;
2290 NotifyParent(scn);
2293 void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
2294 NotifyStyleToNeeded(endStyleNeeded);
2297 void Editor::NotifyLexerChanged(Document *, void *) {
2300 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
2301 errorStatus = status;
2304 void Editor::NotifyChar(int ch) {
2305 SCNotification scn = {};
2306 scn.nmhdr.code = SCN_CHARADDED;
2307 scn.ch = ch;
2308 NotifyParent(scn);
2311 void Editor::NotifySavePoint(bool isSavePoint) {
2312 SCNotification scn = {};
2313 if (isSavePoint) {
2314 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
2315 } else {
2316 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
2318 NotifyParent(scn);
2321 void Editor::NotifyModifyAttempt() {
2322 SCNotification scn = {};
2323 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
2324 NotifyParent(scn);
2327 void Editor::NotifyDoubleClick(Point pt, int modifiers) {
2328 SCNotification scn = {};
2329 scn.nmhdr.code = SCN_DOUBLECLICK;
2330 scn.line = LineFromLocation(pt);
2331 scn.position = PositionFromLocation(pt, true);
2332 scn.modifiers = modifiers;
2333 NotifyParent(scn);
2336 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
2337 NotifyDoubleClick(pt, ModifierFlags(shift, ctrl, alt));
2340 void Editor::NotifyHotSpotDoubleClicked(int position, int modifiers) {
2341 SCNotification scn = {};
2342 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
2343 scn.position = position;
2344 scn.modifiers = modifiers;
2345 NotifyParent(scn);
2348 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
2349 NotifyHotSpotDoubleClicked(position, ModifierFlags(shift, ctrl, alt));
2352 void Editor::NotifyHotSpotClicked(int position, int modifiers) {
2353 SCNotification scn = {};
2354 scn.nmhdr.code = SCN_HOTSPOTCLICK;
2355 scn.position = position;
2356 scn.modifiers = modifiers;
2357 NotifyParent(scn);
2360 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
2361 NotifyHotSpotClicked(position, ModifierFlags(shift, ctrl, alt));
2364 void Editor::NotifyHotSpotReleaseClick(int position, int modifiers) {
2365 SCNotification scn = {};
2366 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
2367 scn.position = position;
2368 scn.modifiers = modifiers;
2369 NotifyParent(scn);
2372 void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
2373 NotifyHotSpotReleaseClick(position, ModifierFlags(shift, ctrl, alt));
2376 bool Editor::NotifyUpdateUI() {
2377 if (needUpdateUI) {
2378 SCNotification scn = {};
2379 scn.nmhdr.code = SCN_UPDATEUI;
2380 scn.updated = needUpdateUI;
2381 NotifyParent(scn);
2382 needUpdateUI = 0;
2383 return true;
2385 return false;
2388 void Editor::NotifyPainted() {
2389 SCNotification scn = {};
2390 scn.nmhdr.code = SCN_PAINTED;
2391 NotifyParent(scn);
2394 void Editor::NotifyIndicatorClick(bool click, int position, int modifiers) {
2395 int mask = pdoc->decorations.AllOnFor(position);
2396 if ((click && mask) || pdoc->decorations.clickNotified) {
2397 SCNotification scn = {};
2398 pdoc->decorations.clickNotified = click;
2399 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
2400 scn.modifiers = modifiers;
2401 scn.position = position;
2402 NotifyParent(scn);
2406 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
2407 NotifyIndicatorClick(click, position, ModifierFlags(shift, ctrl, alt));
2410 bool Editor::NotifyMarginClick(Point pt, int modifiers) {
2411 int marginClicked = -1;
2412 int x = vs.textStart - vs.fixedColumnWidth;
2413 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
2414 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
2415 marginClicked = margin;
2416 x += vs.ms[margin].width;
2418 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
2419 int position = pdoc->LineStart(LineFromLocation(pt));
2420 if ((vs.ms[marginClicked].mask & SC_MASK_FOLDERS) && (foldAutomatic & SC_AUTOMATICFOLD_CLICK)) {
2421 const bool ctrl = (modifiers & SCI_CTRL) != 0;
2422 const bool shift = (modifiers & SCI_SHIFT) != 0;
2423 int lineClick = pdoc->LineFromPosition(position);
2424 if (shift && ctrl) {
2425 FoldAll(SC_FOLDACTION_TOGGLE);
2426 } else {
2427 int levelClick = pdoc->GetLevel(lineClick);
2428 if (levelClick & SC_FOLDLEVELHEADERFLAG) {
2429 if (shift) {
2430 // Ensure all children visible
2431 FoldExpand(lineClick, SC_FOLDACTION_EXPAND, levelClick);
2432 } else if (ctrl) {
2433 FoldExpand(lineClick, SC_FOLDACTION_TOGGLE, levelClick);
2434 } else {
2435 // Toggle this line
2436 FoldLine(lineClick, SC_FOLDACTION_TOGGLE);
2440 return true;
2442 SCNotification scn = {};
2443 scn.nmhdr.code = SCN_MARGINCLICK;
2444 scn.modifiers = modifiers;
2445 scn.position = position;
2446 scn.margin = marginClicked;
2447 NotifyParent(scn);
2448 return true;
2449 } else {
2450 return false;
2454 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
2455 return NotifyMarginClick(pt, ModifierFlags(shift, ctrl, alt));
2458 void Editor::NotifyNeedShown(int pos, int len) {
2459 SCNotification scn = {};
2460 scn.nmhdr.code = SCN_NEEDSHOWN;
2461 scn.position = pos;
2462 scn.length = len;
2463 NotifyParent(scn);
2466 void Editor::NotifyDwelling(Point pt, bool state) {
2467 SCNotification scn = {};
2468 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
2469 scn.position = PositionFromLocation(pt, true);
2470 scn.x = static_cast<int>(pt.x + vs.ExternalMarginWidth());
2471 scn.y = static_cast<int>(pt.y);
2472 NotifyParent(scn);
2475 void Editor::NotifyZoom() {
2476 SCNotification scn = {};
2477 scn.nmhdr.code = SCN_ZOOM;
2478 NotifyParent(scn);
2481 // Notifications from document
2482 void Editor::NotifyModifyAttempt(Document *, void *) {
2483 //Platform::DebugPrintf("** Modify Attempt\n");
2484 NotifyModifyAttempt();
2487 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
2488 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
2489 NotifySavePoint(atSavePoint);
2492 void Editor::CheckModificationForWrap(DocModification mh) {
2493 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
2494 view.llc.Invalidate(LineLayout::llCheckTextAndStyle);
2495 int lineDoc = pdoc->LineFromPosition(mh.position);
2496 int lines = Platform::Maximum(0, mh.linesAdded);
2497 if (Wrapping()) {
2498 NeedWrapping(lineDoc, lineDoc + lines + 1);
2500 RefreshStyleData();
2501 // Fix up annotation heights
2502 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
2506 // Move a position so it is still after the same character as before the insertion.
2507 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
2508 if (position > startInsertion) {
2509 return position + length;
2511 return position;
2514 // Move a position so it is still after the same character as before the deletion if that
2515 // character is still present else after the previous surviving character.
2516 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
2517 if (position > startDeletion) {
2518 int endDeletion = startDeletion + length;
2519 if (position > endDeletion) {
2520 return position - length;
2521 } else {
2522 return startDeletion;
2524 } else {
2525 return position;
2529 void Editor::NotifyModified(Document *, DocModification mh, void *) {
2530 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
2531 if (paintState == painting) {
2532 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
2534 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
2535 if (paintState == painting) {
2536 CheckForChangeOutsidePaint(
2537 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
2538 } else {
2539 // Could check that change is before last visible line.
2540 Redraw();
2543 if (mh.modificationType & SC_MOD_CHANGETABSTOPS) {
2544 Redraw();
2546 if (mh.modificationType & SC_MOD_LEXERSTATE) {
2547 if (paintState == painting) {
2548 CheckForChangeOutsidePaint(
2549 Range(mh.position, mh.position + mh.length));
2550 } else {
2551 Redraw();
2554 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
2555 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
2556 pdoc->IncrementStyleClock();
2558 if (paintState == notPainting) {
2559 if (mh.position < pdoc->LineStart(topLine)) {
2560 // Styling performed before this view
2561 Redraw();
2562 } else {
2563 InvalidateRange(mh.position, mh.position + mh.length);
2566 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
2567 view.llc.Invalidate(LineLayout::llCheckTextAndStyle);
2569 } else {
2570 // Move selection and brace highlights
2571 if (mh.modificationType & SC_MOD_INSERTTEXT) {
2572 sel.MovePositions(true, mh.position, mh.length);
2573 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
2574 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
2575 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
2576 sel.MovePositions(false, mh.position, mh.length);
2577 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
2578 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
2580 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && cs.HiddenLines()) {
2581 // Some lines are hidden so may need shown.
2582 const int lineOfPos = pdoc->LineFromPosition(mh.position);
2583 int endNeedShown = mh.position;
2584 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
2585 if (pdoc->ContainsLineEnd(mh.text, mh.length) && (mh.position != pdoc->LineStart(lineOfPos)))
2586 endNeedShown = pdoc->LineStart(lineOfPos+1);
2587 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
2588 // Extend the need shown area over any folded lines
2589 endNeedShown = mh.position + mh.length;
2590 int lineLast = pdoc->LineFromPosition(mh.position+mh.length);
2591 for (int line = lineOfPos; line <= lineLast; line++) {
2592 const int lineMaxSubord = pdoc->GetLastChild(line, -1, -1);
2593 if (lineLast < lineMaxSubord) {
2594 lineLast = lineMaxSubord;
2595 endNeedShown = pdoc->LineEnd(lineLast);
2599 NeedShown(mh.position, endNeedShown - mh.position);
2601 if (mh.linesAdded != 0) {
2602 // Update contraction state for inserted and removed lines
2603 // lineOfPos should be calculated in context of state before modification, shouldn't it
2604 int lineOfPos = pdoc->LineFromPosition(mh.position);
2605 if (mh.position > pdoc->LineStart(lineOfPos))
2606 lineOfPos++; // Affecting subsequent lines
2607 if (mh.linesAdded > 0) {
2608 cs.InsertLines(lineOfPos, mh.linesAdded);
2609 } else {
2610 cs.DeleteLines(lineOfPos, -mh.linesAdded);
2612 view.LinesAddedOrRemoved(lineOfPos, mh.linesAdded);
2614 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
2615 int lineDoc = pdoc->LineFromPosition(mh.position);
2616 if (vs.annotationVisible) {
2617 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
2618 Redraw();
2621 CheckModificationForWrap(mh);
2622 if (mh.linesAdded != 0) {
2623 // Avoid scrolling of display if change before current display
2624 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
2625 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
2626 if (newTop != topLine) {
2627 SetTopLine(newTop);
2628 SetVerticalScrollPos();
2632 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
2633 QueueIdleWork(WorkNeeded::workStyle, pdoc->Length());
2634 Redraw();
2636 } else {
2637 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
2638 QueueIdleWork(WorkNeeded::workStyle, mh.position + mh.length);
2639 InvalidateRange(mh.position, mh.position + mh.length);
2644 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
2645 SetScrollBars();
2648 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
2649 if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) {
2650 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
2651 // Fold changes can affect the drawing of following lines so redraw whole margin
2652 RedrawSelMargin(marginView.highlightDelimiter.isEnabled ? -1 : mh.line - 1, true);
2653 } else {
2654 RedrawSelMargin(mh.line);
2658 if ((mh.modificationType & SC_MOD_CHANGEFOLD) && (foldAutomatic & SC_AUTOMATICFOLD_CHANGE)) {
2659 FoldChanged(mh.line, mh.foldLevelNow, mh.foldLevelPrev);
2662 // NOW pay the piper WRT "deferred" visual updates
2663 if (IsLastStep(mh)) {
2664 SetScrollBars();
2665 Redraw();
2668 // If client wants to see this modification
2669 if (mh.modificationType & modEventMask) {
2670 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
2671 // Real modification made to text of document.
2672 NotifyChange(); // Send EN_CHANGE
2675 SCNotification scn = {};
2676 scn.nmhdr.code = SCN_MODIFIED;
2677 scn.position = mh.position;
2678 scn.modificationType = mh.modificationType;
2679 scn.text = mh.text;
2680 scn.length = mh.length;
2681 scn.linesAdded = mh.linesAdded;
2682 scn.line = mh.line;
2683 scn.foldLevelNow = mh.foldLevelNow;
2684 scn.foldLevelPrev = mh.foldLevelPrev;
2685 scn.token = mh.token;
2686 scn.annotationLinesAdded = mh.annotationLinesAdded;
2687 NotifyParent(scn);
2691 void Editor::NotifyDeleted(Document *, void *) {
2692 /* Do nothing */
2695 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2697 // Enumerates all macroable messages
2698 switch (iMessage) {
2699 case SCI_CUT:
2700 case SCI_COPY:
2701 case SCI_PASTE:
2702 case SCI_CLEAR:
2703 case SCI_REPLACESEL:
2704 case SCI_ADDTEXT:
2705 case SCI_INSERTTEXT:
2706 case SCI_APPENDTEXT:
2707 case SCI_CLEARALL:
2708 case SCI_SELECTALL:
2709 case SCI_GOTOLINE:
2710 case SCI_GOTOPOS:
2711 case SCI_SEARCHANCHOR:
2712 case SCI_SEARCHNEXT:
2713 case SCI_SEARCHPREV:
2714 case SCI_LINEDOWN:
2715 case SCI_LINEDOWNEXTEND:
2716 case SCI_PARADOWN:
2717 case SCI_PARADOWNEXTEND:
2718 case SCI_LINEUP:
2719 case SCI_LINEUPEXTEND:
2720 case SCI_PARAUP:
2721 case SCI_PARAUPEXTEND:
2722 case SCI_CHARLEFT:
2723 case SCI_CHARLEFTEXTEND:
2724 case SCI_CHARRIGHT:
2725 case SCI_CHARRIGHTEXTEND:
2726 case SCI_WORDLEFT:
2727 case SCI_WORDLEFTEXTEND:
2728 case SCI_WORDRIGHT:
2729 case SCI_WORDRIGHTEXTEND:
2730 case SCI_WORDPARTLEFT:
2731 case SCI_WORDPARTLEFTEXTEND:
2732 case SCI_WORDPARTRIGHT:
2733 case SCI_WORDPARTRIGHTEXTEND:
2734 case SCI_WORDLEFTEND:
2735 case SCI_WORDLEFTENDEXTEND:
2736 case SCI_WORDRIGHTEND:
2737 case SCI_WORDRIGHTENDEXTEND:
2738 case SCI_HOME:
2739 case SCI_HOMEEXTEND:
2740 case SCI_LINEEND:
2741 case SCI_LINEENDEXTEND:
2742 case SCI_HOMEWRAP:
2743 case SCI_HOMEWRAPEXTEND:
2744 case SCI_LINEENDWRAP:
2745 case SCI_LINEENDWRAPEXTEND:
2746 case SCI_DOCUMENTSTART:
2747 case SCI_DOCUMENTSTARTEXTEND:
2748 case SCI_DOCUMENTEND:
2749 case SCI_DOCUMENTENDEXTEND:
2750 case SCI_STUTTEREDPAGEUP:
2751 case SCI_STUTTEREDPAGEUPEXTEND:
2752 case SCI_STUTTEREDPAGEDOWN:
2753 case SCI_STUTTEREDPAGEDOWNEXTEND:
2754 case SCI_PAGEUP:
2755 case SCI_PAGEUPEXTEND:
2756 case SCI_PAGEDOWN:
2757 case SCI_PAGEDOWNEXTEND:
2758 case SCI_EDITTOGGLEOVERTYPE:
2759 case SCI_CANCEL:
2760 case SCI_DELETEBACK:
2761 case SCI_TAB:
2762 case SCI_BACKTAB:
2763 case SCI_FORMFEED:
2764 case SCI_VCHOME:
2765 case SCI_VCHOMEEXTEND:
2766 case SCI_VCHOMEWRAP:
2767 case SCI_VCHOMEWRAPEXTEND:
2768 case SCI_VCHOMEDISPLAY:
2769 case SCI_VCHOMEDISPLAYEXTEND:
2770 case SCI_DELWORDLEFT:
2771 case SCI_DELWORDRIGHT:
2772 case SCI_DELWORDRIGHTEND:
2773 case SCI_DELLINELEFT:
2774 case SCI_DELLINERIGHT:
2775 case SCI_LINECOPY:
2776 case SCI_LINECUT:
2777 case SCI_LINEDELETE:
2778 case SCI_LINETRANSPOSE:
2779 case SCI_LINEDUPLICATE:
2780 case SCI_LOWERCASE:
2781 case SCI_UPPERCASE:
2782 case SCI_LINESCROLLDOWN:
2783 case SCI_LINESCROLLUP:
2784 case SCI_DELETEBACKNOTLINE:
2785 case SCI_HOMEDISPLAY:
2786 case SCI_HOMEDISPLAYEXTEND:
2787 case SCI_LINEENDDISPLAY:
2788 case SCI_LINEENDDISPLAYEXTEND:
2789 case SCI_SETSELECTIONMODE:
2790 case SCI_LINEDOWNRECTEXTEND:
2791 case SCI_LINEUPRECTEXTEND:
2792 case SCI_CHARLEFTRECTEXTEND:
2793 case SCI_CHARRIGHTRECTEXTEND:
2794 case SCI_HOMERECTEXTEND:
2795 case SCI_VCHOMERECTEXTEND:
2796 case SCI_LINEENDRECTEXTEND:
2797 case SCI_PAGEUPRECTEXTEND:
2798 case SCI_PAGEDOWNRECTEXTEND:
2799 case SCI_SELECTIONDUPLICATE:
2800 case SCI_COPYALLOWLINE:
2801 case SCI_VERTICALCENTRECARET:
2802 case SCI_MOVESELECTEDLINESUP:
2803 case SCI_MOVESELECTEDLINESDOWN:
2804 case SCI_SCROLLTOSTART:
2805 case SCI_SCROLLTOEND:
2806 break;
2808 // Filter out all others like display changes. Also, newlines are redundant
2809 // with char insert messages.
2810 case SCI_NEWLINE:
2811 default:
2812 // printf("Filtered out %ld of macro recording\n", iMessage);
2813 return;
2816 // Send notification
2817 SCNotification scn = {};
2818 scn.nmhdr.code = SCN_MACRORECORD;
2819 scn.message = iMessage;
2820 scn.wParam = wParam;
2821 scn.lParam = lParam;
2822 NotifyParent(scn);
2825 // Something has changed that the container should know about
2826 void Editor::ContainerNeedsUpdate(int flags) {
2827 needUpdateUI |= flags;
2831 * Force scroll and keep position relative to top of window.
2833 * If stuttered = true and not already at first/last row, move to first/last row of window.
2834 * If stuttered = true and already at first/last row, scroll as normal.
2836 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
2837 int topLineNew;
2838 SelectionPosition newPos;
2840 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
2841 int topStutterLine = topLine + caretYSlop;
2842 int bottomStutterLine =
2843 pdoc->LineFromPosition(PositionFromLocation(
2844 Point::FromInts(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
2845 - caretYSlop - 1;
2847 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
2848 topLineNew = topLine;
2849 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
2850 false, false, UserVirtualSpace());
2852 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
2853 topLineNew = topLine;
2854 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
2855 false, false, UserVirtualSpace());
2857 } else {
2858 Point pt = LocationFromPosition(sel.MainCaret());
2860 topLineNew = Platform::Clamp(
2861 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
2862 newPos = SPositionFromLocation(
2863 Point::FromInts(lastXChosen - xOffset, static_cast<int>(pt.y) + direction * (vs.lineHeight * LinesToScroll())),
2864 false, false, UserVirtualSpace());
2867 if (topLineNew != topLine) {
2868 SetTopLine(topLineNew);
2869 MovePositionTo(newPos, selt);
2870 Redraw();
2871 SetVerticalScrollPos();
2872 } else {
2873 MovePositionTo(newPos, selt);
2877 void Editor::ChangeCaseOfSelection(int caseMapping) {
2878 UndoGroup ug(pdoc);
2879 for (size_t r=0; r<sel.Count(); r++) {
2880 SelectionRange current = sel.Range(r);
2881 SelectionRange currentNoVS = current;
2882 currentNoVS.ClearVirtualSpace();
2883 size_t rangeBytes = currentNoVS.Length();
2884 if (rangeBytes > 0) {
2885 std::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position());
2887 std::string sMapped = CaseMapString(sText, caseMapping);
2889 if (sMapped != sText) {
2890 size_t firstDifference = 0;
2891 while (sMapped[firstDifference] == sText[firstDifference])
2892 firstDifference++;
2893 size_t lastDifferenceText = sText.size() - 1;
2894 size_t lastDifferenceMapped = sMapped.size() - 1;
2895 while (sMapped[lastDifferenceMapped] == sText[lastDifferenceText]) {
2896 lastDifferenceText--;
2897 lastDifferenceMapped--;
2899 size_t endDifferenceText = sText.size() - 1 - lastDifferenceText;
2900 pdoc->DeleteChars(
2901 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
2902 static_cast<int>(rangeBytes - firstDifference - endDifferenceText));
2903 const int lengthChange = static_cast<int>(lastDifferenceMapped - firstDifference + 1);
2904 const int lengthInserted = pdoc->InsertString(
2905 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
2906 sMapped.c_str() + firstDifference,
2907 lengthChange);
2908 // Automatic movement changes selection so reset to exactly the same as it was.
2909 int diffSizes = static_cast<int>(sMapped.size() - sText.size()) + lengthInserted - lengthChange;
2910 if (diffSizes != 0) {
2911 if (current.anchor > current.caret)
2912 current.anchor.Add(diffSizes);
2913 else
2914 current.caret.Add(diffSizes);
2916 sel.Range(r) = current;
2922 void Editor::LineTranspose() {
2923 int line = pdoc->LineFromPosition(sel.MainCaret());
2924 if (line > 0) {
2925 UndoGroup ug(pdoc);
2927 const int startPrevious = pdoc->LineStart(line - 1);
2928 const std::string linePrevious = RangeText(startPrevious, pdoc->LineEnd(line - 1));
2930 int startCurrent = pdoc->LineStart(line);
2931 const std::string lineCurrent = RangeText(startCurrent, pdoc->LineEnd(line));
2933 pdoc->DeleteChars(startCurrent, static_cast<int>(lineCurrent.length()));
2934 pdoc->DeleteChars(startPrevious, static_cast<int>(linePrevious.length()));
2935 startCurrent -= static_cast<int>(linePrevious.length());
2937 startCurrent += pdoc->InsertString(startPrevious, lineCurrent.c_str(),
2938 static_cast<int>(lineCurrent.length()));
2939 pdoc->InsertString(startCurrent, linePrevious.c_str(),
2940 static_cast<int>(linePrevious.length()));
2941 // Move caret to start of current line
2942 MovePositionTo(SelectionPosition(startCurrent));
2946 void Editor::Duplicate(bool forLine) {
2947 if (sel.Empty()) {
2948 forLine = true;
2950 UndoGroup ug(pdoc);
2951 const char *eol = "";
2952 int eolLen = 0;
2953 if (forLine) {
2954 eol = StringFromEOLMode(pdoc->eolMode);
2955 eolLen = istrlen(eol);
2957 for (size_t r=0; r<sel.Count(); r++) {
2958 SelectionPosition start = sel.Range(r).Start();
2959 SelectionPosition end = sel.Range(r).End();
2960 if (forLine) {
2961 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
2962 start = SelectionPosition(pdoc->LineStart(line));
2963 end = SelectionPosition(pdoc->LineEnd(line));
2965 std::string text = RangeText(start.Position(), end.Position());
2966 int lengthInserted = eolLen;
2967 if (forLine)
2968 lengthInserted = pdoc->InsertString(end.Position(), eol, eolLen);
2969 pdoc->InsertString(end.Position() + lengthInserted, text.c_str(), static_cast<int>(text.length()));
2971 if (sel.Count() && sel.IsRectangular()) {
2972 SelectionPosition last = sel.Last();
2973 if (forLine) {
2974 int line = pdoc->LineFromPosition(last.Position());
2975 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
2977 if (sel.Rectangular().anchor > sel.Rectangular().caret)
2978 sel.Rectangular().anchor = last;
2979 else
2980 sel.Rectangular().caret = last;
2981 SetRectangularRange();
2985 void Editor::CancelModes() {
2986 sel.SetMoveExtends(false);
2989 void Editor::NewLine() {
2990 InvalidateWholeSelection();
2991 if (sel.IsRectangular() || !additionalSelectionTyping) {
2992 // Remove non-main ranges
2993 sel.DropAdditionalRanges();
2996 UndoGroup ug(pdoc, !sel.Empty() || (sel.Count() > 1));
2998 // Clear each range
2999 if (!sel.Empty()) {
3000 ClearSelection();
3003 // Insert each line end
3004 size_t countInsertions = 0;
3005 for (size_t r = 0; r < sel.Count(); r++) {
3006 sel.Range(r).ClearVirtualSpace();
3007 const char *eol = StringFromEOLMode(pdoc->eolMode);
3008 const int positionInsert = sel.Range(r).caret.Position();
3009 const int insertLength = pdoc->InsertString(positionInsert, eol, istrlen(eol));
3010 if (insertLength > 0) {
3011 sel.Range(r) = SelectionRange(positionInsert + insertLength);
3012 countInsertions++;
3016 // Perform notifications after all the changes as the application may change the
3017 // selections in response to the characters.
3018 for (size_t i = 0; i < countInsertions; i++) {
3019 const char *eol = StringFromEOLMode(pdoc->eolMode);
3020 while (*eol) {
3021 NotifyChar(*eol);
3022 if (recordingMacro) {
3023 char txt[2];
3024 txt[0] = *eol;
3025 txt[1] = '\0';
3026 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
3028 eol++;
3032 SetLastXChosen();
3033 SetScrollBars();
3034 EnsureCaretVisible();
3035 // Avoid blinking during rapid typing:
3036 ShowCaretAtCurrentPosition();
3039 SelectionPosition Editor::PositionUpOrDown(SelectionPosition spStart, int direction, int lastX) {
3040 const Point pt = LocationFromPosition(spStart);
3041 int skipLines = 0;
3043 if (vs.annotationVisible) {
3044 const int lineDoc = pdoc->LineFromPosition(spStart.Position());
3045 const Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
3046 const int subLine = static_cast<int>(pt.y - ptStartLine.y) / vs.lineHeight;
3048 if (direction < 0 && subLine == 0) {
3049 const int lineDisplay = cs.DisplayFromDoc(lineDoc);
3050 if (lineDisplay > 0) {
3051 skipLines = pdoc->AnnotationLines(cs.DocFromDisplay(lineDisplay - 1));
3053 } else if (direction > 0 && subLine >= (cs.GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
3054 skipLines = pdoc->AnnotationLines(lineDoc);
3058 const int newY = static_cast<int>(pt.y) + (1 + skipLines) * direction * vs.lineHeight;
3059 if (lastX < 0) {
3060 lastX = static_cast<int>(pt.x) + xOffset;
3062 SelectionPosition posNew = SPositionFromLocation(
3063 Point::FromInts(lastX - xOffset, newY), false, false, UserVirtualSpace());
3065 if (direction < 0) {
3066 // Line wrapping may lead to a location on the same line, so
3067 // seek back if that is the case.
3068 Point ptNew = LocationFromPosition(posNew.Position());
3069 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
3070 posNew.Add(-1);
3071 posNew.SetVirtualSpace(0);
3072 ptNew = LocationFromPosition(posNew.Position());
3074 } else if (direction > 0 && posNew.Position() != pdoc->Length()) {
3075 // There is an equivalent case when moving down which skips
3076 // over a line.
3077 Point ptNew = LocationFromPosition(posNew.Position());
3078 while ((posNew.Position() > spStart.Position()) && (ptNew.y > newY)) {
3079 posNew.Add(-1);
3080 posNew.SetVirtualSpace(0);
3081 ptNew = LocationFromPosition(posNew.Position());
3084 return posNew;
3087 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
3088 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
3089 if (sel.IsRectangular()) {
3090 if (selt == Selection::noSel) {
3091 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
3092 } else {
3093 caretToUse = sel.Rectangular().caret;
3096 if (selt == Selection::selRectangle) {
3097 const SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();
3098 if (!sel.IsRectangular()) {
3099 InvalidateWholeSelection();
3100 sel.DropAdditionalRanges();
3102 const SelectionPosition posNew = MovePositionSoVisible(
3103 PositionUpOrDown(caretToUse, direction, lastXChosen), direction);
3104 sel.selType = Selection::selRectangle;
3105 sel.Rectangular() = SelectionRange(posNew, rangeBase.anchor);
3106 SetRectangularRange();
3107 MovedCaret(posNew, caretToUse, true);
3108 } else {
3109 InvalidateWholeSelection();
3110 if (!additionalSelectionTyping || (sel.IsRectangular())) {
3111 sel.DropAdditionalRanges();
3113 sel.selType = Selection::selStream;
3114 for (size_t r = 0; r < sel.Count(); r++) {
3115 const int lastX = (r == sel.Main()) ? lastXChosen : -1;
3116 const SelectionPosition spCaretNow = sel.Range(r).caret;
3117 const SelectionPosition posNew = MovePositionSoVisible(
3118 PositionUpOrDown(spCaretNow, direction, lastX), direction);
3119 sel.Range(r) = selt == Selection::selStream ?
3120 SelectionRange(posNew, sel.Range(r).anchor) : SelectionRange(posNew);
3122 sel.RemoveDuplicates();
3123 MovedCaret(sel.RangeMain().caret, caretToUse, true);
3127 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
3128 int lineDoc, savedPos = sel.MainCaret();
3129 do {
3130 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
3131 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
3132 if (direction > 0) {
3133 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
3134 if (selt == Selection::noSel) {
3135 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
3137 break;
3140 } while (!cs.GetVisible(lineDoc));
3143 int Editor::StartEndDisplayLine(int pos, bool start) {
3144 RefreshStyleData();
3145 AutoSurface surface(this);
3146 int posRet = view.StartEndDisplayLine(surface, *this, pos, start, vs);
3147 if (posRet == INVALID_POSITION) {
3148 return pos;
3149 } else {
3150 return posRet;
3154 namespace {
3156 unsigned int WithExtends(unsigned int iMessage) {
3157 switch (iMessage) {
3158 case SCI_CHARLEFT: return SCI_CHARLEFTEXTEND;
3159 case SCI_CHARRIGHT: return SCI_CHARRIGHTEXTEND;
3161 case SCI_WORDLEFT: return SCI_WORDLEFTEXTEND;
3162 case SCI_WORDRIGHT: return SCI_WORDRIGHTEXTEND;
3163 case SCI_WORDLEFTEND: return SCI_WORDLEFTENDEXTEND;
3164 case SCI_WORDRIGHTEND: return SCI_WORDRIGHTENDEXTEND;
3165 case SCI_WORDPARTLEFT: return SCI_WORDPARTLEFTEXTEND;
3166 case SCI_WORDPARTRIGHT: return SCI_WORDPARTRIGHTEXTEND;
3168 case SCI_HOME: return SCI_HOMEEXTEND;
3169 case SCI_HOMEDISPLAY: return SCI_HOMEDISPLAYEXTEND;
3170 case SCI_HOMEWRAP: return SCI_HOMEWRAPEXTEND;
3171 case SCI_VCHOME: return SCI_VCHOMEEXTEND;
3172 case SCI_VCHOMEDISPLAY: return SCI_VCHOMEDISPLAYEXTEND;
3173 case SCI_VCHOMEWRAP: return SCI_VCHOMEWRAPEXTEND;
3175 case SCI_LINEEND: return SCI_LINEENDEXTEND;
3176 case SCI_LINEENDDISPLAY: return SCI_LINEENDDISPLAYEXTEND;
3177 case SCI_LINEENDWRAP: return SCI_LINEENDWRAPEXTEND;
3179 default: return iMessage;
3183 int NaturalDirection(unsigned int iMessage) {
3184 switch (iMessage) {
3185 case SCI_CHARLEFT:
3186 case SCI_CHARLEFTEXTEND:
3187 case SCI_CHARLEFTRECTEXTEND:
3188 case SCI_WORDLEFT:
3189 case SCI_WORDLEFTEXTEND:
3190 case SCI_WORDLEFTEND:
3191 case SCI_WORDLEFTENDEXTEND:
3192 case SCI_WORDPARTLEFT:
3193 case SCI_WORDPARTLEFTEXTEND:
3194 case SCI_HOME:
3195 case SCI_HOMEEXTEND:
3196 case SCI_HOMEDISPLAY:
3197 case SCI_HOMEDISPLAYEXTEND:
3198 case SCI_HOMEWRAP:
3199 case SCI_HOMEWRAPEXTEND:
3200 // VC_HOME* mostly goes back
3201 case SCI_VCHOME:
3202 case SCI_VCHOMEEXTEND:
3203 case SCI_VCHOMEDISPLAY:
3204 case SCI_VCHOMEDISPLAYEXTEND:
3205 case SCI_VCHOMEWRAP:
3206 case SCI_VCHOMEWRAPEXTEND:
3207 return -1;
3209 default:
3210 return 1;
3214 bool IsRectExtend(unsigned int iMessage) {
3215 switch (iMessage) {
3216 case SCI_CHARLEFTRECTEXTEND:
3217 case SCI_CHARRIGHTRECTEXTEND:
3218 case SCI_HOMERECTEXTEND:
3219 case SCI_VCHOMERECTEXTEND:
3220 case SCI_LINEENDRECTEXTEND:
3221 return true;
3222 default:
3223 return false;
3229 int Editor::VCHomeDisplayPosition(int position) {
3230 const int homePos = pdoc->VCHomePosition(position);
3231 const int viewLineStart = StartEndDisplayLine(position, true);
3232 if (viewLineStart > homePos)
3233 return viewLineStart;
3234 else
3235 return homePos;
3238 int Editor::VCHomeWrapPosition(int position) {
3239 const int homePos = pdoc->VCHomePosition(position);
3240 const int viewLineStart = StartEndDisplayLine(position, true);
3241 if ((viewLineStart < position) && (viewLineStart > homePos))
3242 return viewLineStart;
3243 else
3244 return homePos;
3247 int Editor::LineEndWrapPosition(int position) {
3248 const int endPos = StartEndDisplayLine(position, false);
3249 const int realEndPos = pdoc->LineEndPosition(position);
3250 if (endPos > realEndPos // if moved past visible EOLs
3251 || position >= endPos) // if at end of display line already
3252 return realEndPos;
3253 else
3254 return endPos;
3257 int Editor::HorizontalMove(unsigned int iMessage) {
3258 if (sel.MoveExtends()) {
3259 iMessage = WithExtends(iMessage);
3262 if (!multipleSelection && !sel.IsRectangular()) {
3263 // Simplify selection down to 1
3264 sel.SetSelection(sel.RangeMain());
3267 // Invalidate each of the current selections
3268 InvalidateWholeSelection();
3270 if (IsRectExtend(iMessage)) {
3271 const SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();
3272 if (!sel.IsRectangular()) {
3273 sel.DropAdditionalRanges();
3275 // Will change to rectangular if not currently rectangular
3276 SelectionPosition spCaret = rangeBase.caret;
3277 switch (iMessage) {
3278 case SCI_CHARLEFTRECTEXTEND:
3279 if (pdoc->IsLineEndPosition(spCaret.Position()) && spCaret.VirtualSpace()) {
3280 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3281 } else {
3282 spCaret = SelectionPosition(spCaret.Position() - 1);
3284 break;
3285 case SCI_CHARRIGHTRECTEXTEND:
3286 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
3287 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3288 } else {
3289 spCaret = SelectionPosition(spCaret.Position() + 1);
3291 break;
3292 case SCI_HOMERECTEXTEND:
3293 spCaret = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3294 break;
3295 case SCI_VCHOMERECTEXTEND:
3296 spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
3297 break;
3298 case SCI_LINEENDRECTEXTEND:
3299 spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3300 break;
3302 const int directionMove = (spCaret < rangeBase.caret) ? -1 : 1;
3303 spCaret = MovePositionSoVisible(spCaret, directionMove);
3304 sel.selType = Selection::selRectangle;
3305 sel.Rectangular() = SelectionRange(spCaret, rangeBase.anchor);
3306 SetRectangularRange();
3307 } else {
3308 if (sel.IsRectangular()) {
3309 // Not a rectangular extension so switch to stream.
3310 SelectionPosition selAtLimit = (NaturalDirection(iMessage) > 0) ? sel.Limits().end : sel.Limits().start;
3311 sel.selType = Selection::selStream;
3312 sel.SetSelection(SelectionRange(selAtLimit));
3314 if (!additionalSelectionTyping) {
3315 InvalidateWholeSelection();
3316 sel.DropAdditionalRanges();
3318 for (size_t r = 0; r < sel.Count(); r++) {
3319 const SelectionPosition spCaretNow = sel.Range(r).caret;
3320 SelectionPosition spCaret = spCaretNow;
3321 switch (iMessage) {
3322 case SCI_CHARLEFT:
3323 case SCI_CHARLEFTEXTEND:
3324 if (spCaret.VirtualSpace()) {
3325 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3326 } else {
3327 spCaret = SelectionPosition(spCaret.Position() - 1);
3329 break;
3330 case SCI_CHARRIGHT:
3331 case SCI_CHARRIGHTEXTEND:
3332 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(spCaret.Position())) {
3333 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3334 } else {
3335 spCaret = SelectionPosition(spCaret.Position() + 1);
3337 break;
3338 case SCI_WORDLEFT:
3339 case SCI_WORDLEFTEXTEND:
3340 spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), -1));
3341 break;
3342 case SCI_WORDRIGHT:
3343 case SCI_WORDRIGHTEXTEND:
3344 spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), 1));
3345 break;
3346 case SCI_WORDLEFTEND:
3347 case SCI_WORDLEFTENDEXTEND:
3348 spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), -1));
3349 break;
3350 case SCI_WORDRIGHTEND:
3351 case SCI_WORDRIGHTENDEXTEND:
3352 spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), 1));
3353 break;
3354 case SCI_WORDPARTLEFT:
3355 case SCI_WORDPARTLEFTEXTEND:
3356 spCaret = SelectionPosition(pdoc->WordPartLeft(spCaret.Position()));
3357 break;
3358 case SCI_WORDPARTRIGHT:
3359 case SCI_WORDPARTRIGHTEXTEND:
3360 spCaret = SelectionPosition(pdoc->WordPartRight(spCaret.Position()));
3361 break;
3362 case SCI_HOME:
3363 case SCI_HOMEEXTEND:
3364 spCaret = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3365 break;
3366 case SCI_HOMEDISPLAY:
3367 case SCI_HOMEDISPLAYEXTEND:
3368 spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), true));
3369 break;
3370 case SCI_HOMEWRAP:
3371 case SCI_HOMEWRAPEXTEND:
3372 spCaret = MovePositionSoVisible(StartEndDisplayLine(spCaret.Position(), true), -1);
3373 if (spCaretNow <= spCaret)
3374 spCaret = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3375 break;
3376 case SCI_VCHOME:
3377 case SCI_VCHOMEEXTEND:
3378 // VCHome alternates between beginning of line and beginning of text so may move back or forwards
3379 spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
3380 break;
3381 case SCI_VCHOMEDISPLAY:
3382 case SCI_VCHOMEDISPLAYEXTEND:
3383 spCaret = SelectionPosition(VCHomeDisplayPosition(spCaret.Position()));
3384 break;
3385 case SCI_VCHOMEWRAP:
3386 case SCI_VCHOMEWRAPEXTEND:
3387 spCaret = SelectionPosition(VCHomeWrapPosition(spCaret.Position()));
3388 break;
3389 case SCI_LINEEND:
3390 case SCI_LINEENDEXTEND:
3391 spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3392 break;
3393 case SCI_LINEENDDISPLAY:
3394 case SCI_LINEENDDISPLAYEXTEND:
3395 spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), false));
3396 break;
3397 case SCI_LINEENDWRAP:
3398 case SCI_LINEENDWRAPEXTEND:
3399 spCaret = SelectionPosition(LineEndWrapPosition(spCaret.Position()));
3400 break;
3402 default:
3403 PLATFORM_ASSERT(false);
3406 const int directionMove = (spCaret < spCaretNow) ? -1 : 1;
3407 spCaret = MovePositionSoVisible(spCaret, directionMove);
3409 // Handle move versus extend, and special behaviour for non-emoty left/right
3410 switch (iMessage) {
3411 case SCI_CHARLEFT:
3412 case SCI_CHARRIGHT:
3413 if (sel.Range(r).Empty()) {
3414 sel.Range(r) = SelectionRange(spCaret);
3415 } else {
3416 sel.Range(r) = SelectionRange(
3417 (iMessage == SCI_CHARLEFT) ? sel.Range(r).Start() : sel.Range(r).End());
3419 break;
3421 case SCI_WORDLEFT:
3422 case SCI_WORDRIGHT:
3423 case SCI_WORDLEFTEND:
3424 case SCI_WORDRIGHTEND:
3425 case SCI_WORDPARTLEFT:
3426 case SCI_WORDPARTRIGHT:
3427 case SCI_HOME:
3428 case SCI_HOMEDISPLAY:
3429 case SCI_HOMEWRAP:
3430 case SCI_VCHOME:
3431 case SCI_VCHOMEDISPLAY:
3432 case SCI_VCHOMEWRAP:
3433 case SCI_LINEEND:
3434 case SCI_LINEENDDISPLAY:
3435 case SCI_LINEENDWRAP:
3436 sel.Range(r) = SelectionRange(spCaret);
3437 break;
3439 case SCI_CHARLEFTEXTEND:
3440 case SCI_CHARRIGHTEXTEND:
3441 case SCI_WORDLEFTEXTEND:
3442 case SCI_WORDRIGHTEXTEND:
3443 case SCI_WORDLEFTENDEXTEND:
3444 case SCI_WORDRIGHTENDEXTEND:
3445 case SCI_WORDPARTLEFTEXTEND:
3446 case SCI_WORDPARTRIGHTEXTEND:
3447 case SCI_HOMEEXTEND:
3448 case SCI_HOMEDISPLAYEXTEND:
3449 case SCI_HOMEWRAPEXTEND:
3450 case SCI_VCHOMEEXTEND:
3451 case SCI_VCHOMEDISPLAYEXTEND:
3452 case SCI_VCHOMEWRAPEXTEND:
3453 case SCI_LINEENDEXTEND:
3454 case SCI_LINEENDDISPLAYEXTEND:
3455 case SCI_LINEENDWRAPEXTEND: {
3456 SelectionRange rangeNew = SelectionRange(spCaret, sel.Range(r).anchor);
3457 sel.TrimOtherSelections(r, SelectionRange(rangeNew));
3458 sel.Range(r) = rangeNew;
3460 break;
3462 default:
3463 PLATFORM_ASSERT(false);
3468 sel.RemoveDuplicates();
3470 MovedCaret(sel.RangeMain().caret, SelectionPosition(INVALID_POSITION), true);
3472 // Invalidate the new state of the selection
3473 InvalidateWholeSelection();
3475 SetLastXChosen();
3476 // Need the line moving and so forth from MovePositionTo
3477 return 0;
3480 int Editor::DelWordOrLine(unsigned int iMessage) {
3481 // Virtual space may be realised for SCI_DELWORDRIGHT or SCI_DELWORDRIGHTEND
3482 // which means 2 actions so wrap in an undo group.
3484 // Rightwards and leftwards deletions differ in treatment of virtual space.
3485 // Clear virtual space for leftwards, realise for rightwards.
3486 const bool leftwards = (iMessage == SCI_DELWORDLEFT) || (iMessage == SCI_DELLINELEFT);
3488 if (!additionalSelectionTyping) {
3489 InvalidateWholeSelection();
3490 sel.DropAdditionalRanges();
3493 UndoGroup ug0(pdoc, (sel.Count() > 1) || !leftwards);
3495 for (size_t r = 0; r < sel.Count(); r++) {
3496 if (leftwards) {
3497 // Delete to the left so first clear the virtual space.
3498 sel.Range(r).ClearVirtualSpace();
3499 } else {
3500 // Delete to the right so first realise the virtual space.
3501 sel.Range(r) = SelectionRange(
3502 InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
3505 Range rangeDelete;
3506 switch (iMessage) {
3507 case SCI_DELWORDLEFT:
3508 rangeDelete = Range(
3509 pdoc->NextWordStart(sel.Range(r).caret.Position(), -1),
3510 sel.Range(r).caret.Position());
3511 break;
3512 case SCI_DELWORDRIGHT:
3513 rangeDelete = Range(
3514 sel.Range(r).caret.Position(),
3515 pdoc->NextWordStart(sel.Range(r).caret.Position(), 1));
3516 break;
3517 case SCI_DELWORDRIGHTEND:
3518 rangeDelete = Range(
3519 sel.Range(r).caret.Position(),
3520 pdoc->NextWordEnd(sel.Range(r).caret.Position(), 1));
3521 break;
3522 case SCI_DELLINELEFT:
3523 rangeDelete = Range(
3524 pdoc->LineStart(pdoc->LineFromPosition(sel.Range(r).caret.Position())),
3525 sel.Range(r).caret.Position());
3526 break;
3527 case SCI_DELLINERIGHT:
3528 rangeDelete = Range(
3529 sel.Range(r).caret.Position(),
3530 pdoc->LineEnd(pdoc->LineFromPosition(sel.Range(r).caret.Position())));
3531 break;
3533 if (!RangeContainsProtected(rangeDelete.start, rangeDelete.end)) {
3534 pdoc->DeleteChars(rangeDelete.start, rangeDelete.end - rangeDelete.start);
3538 // May need something stronger here: can selections overlap at this point?
3539 sel.RemoveDuplicates();
3541 MovedCaret(sel.RangeMain().caret, SelectionPosition(INVALID_POSITION), true);
3543 // Invalidate the new state of the selection
3544 InvalidateWholeSelection();
3546 SetLastXChosen();
3547 return 0;
3550 int Editor::KeyCommand(unsigned int iMessage) {
3551 switch (iMessage) {
3552 case SCI_LINEDOWN:
3553 CursorUpOrDown(1, Selection::noSel);
3554 break;
3555 case SCI_LINEDOWNEXTEND:
3556 CursorUpOrDown(1, Selection::selStream);
3557 break;
3558 case SCI_LINEDOWNRECTEXTEND:
3559 CursorUpOrDown(1, Selection::selRectangle);
3560 break;
3561 case SCI_PARADOWN:
3562 ParaUpOrDown(1, Selection::noSel);
3563 break;
3564 case SCI_PARADOWNEXTEND:
3565 ParaUpOrDown(1, Selection::selStream);
3566 break;
3567 case SCI_LINESCROLLDOWN:
3568 ScrollTo(topLine + 1);
3569 MoveCaretInsideView(false);
3570 break;
3571 case SCI_LINEUP:
3572 CursorUpOrDown(-1, Selection::noSel);
3573 break;
3574 case SCI_LINEUPEXTEND:
3575 CursorUpOrDown(-1, Selection::selStream);
3576 break;
3577 case SCI_LINEUPRECTEXTEND:
3578 CursorUpOrDown(-1, Selection::selRectangle);
3579 break;
3580 case SCI_PARAUP:
3581 ParaUpOrDown(-1, Selection::noSel);
3582 break;
3583 case SCI_PARAUPEXTEND:
3584 ParaUpOrDown(-1, Selection::selStream);
3585 break;
3586 case SCI_LINESCROLLUP:
3587 ScrollTo(topLine - 1);
3588 MoveCaretInsideView(false);
3589 break;
3591 case SCI_CHARLEFT:
3592 case SCI_CHARLEFTEXTEND:
3593 case SCI_CHARLEFTRECTEXTEND:
3594 case SCI_CHARRIGHT:
3595 case SCI_CHARRIGHTEXTEND:
3596 case SCI_CHARRIGHTRECTEXTEND:
3597 case SCI_WORDLEFT:
3598 case SCI_WORDLEFTEXTEND:
3599 case SCI_WORDRIGHT:
3600 case SCI_WORDRIGHTEXTEND:
3601 case SCI_WORDLEFTEND:
3602 case SCI_WORDLEFTENDEXTEND:
3603 case SCI_WORDRIGHTEND:
3604 case SCI_WORDRIGHTENDEXTEND:
3605 case SCI_WORDPARTLEFT:
3606 case SCI_WORDPARTLEFTEXTEND:
3607 case SCI_WORDPARTRIGHT:
3608 case SCI_WORDPARTRIGHTEXTEND:
3609 case SCI_HOME:
3610 case SCI_HOMEEXTEND:
3611 case SCI_HOMERECTEXTEND:
3612 case SCI_HOMEDISPLAY:
3613 case SCI_HOMEDISPLAYEXTEND:
3614 case SCI_HOMEWRAP:
3615 case SCI_HOMEWRAPEXTEND:
3616 case SCI_VCHOME:
3617 case SCI_VCHOMEEXTEND:
3618 case SCI_VCHOMERECTEXTEND:
3619 case SCI_VCHOMEDISPLAY:
3620 case SCI_VCHOMEDISPLAYEXTEND:
3621 case SCI_VCHOMEWRAP:
3622 case SCI_VCHOMEWRAPEXTEND:
3623 case SCI_LINEEND:
3624 case SCI_LINEENDEXTEND:
3625 case SCI_LINEENDRECTEXTEND:
3626 case SCI_LINEENDDISPLAY:
3627 case SCI_LINEENDDISPLAYEXTEND:
3628 case SCI_LINEENDWRAP:
3629 case SCI_LINEENDWRAPEXTEND:
3630 return HorizontalMove(iMessage);
3632 case SCI_DOCUMENTSTART:
3633 MovePositionTo(0);
3634 SetLastXChosen();
3635 break;
3636 case SCI_DOCUMENTSTARTEXTEND:
3637 MovePositionTo(0, Selection::selStream);
3638 SetLastXChosen();
3639 break;
3640 case SCI_DOCUMENTEND:
3641 MovePositionTo(pdoc->Length());
3642 SetLastXChosen();
3643 break;
3644 case SCI_DOCUMENTENDEXTEND:
3645 MovePositionTo(pdoc->Length(), Selection::selStream);
3646 SetLastXChosen();
3647 break;
3648 case SCI_STUTTEREDPAGEUP:
3649 PageMove(-1, Selection::noSel, true);
3650 break;
3651 case SCI_STUTTEREDPAGEUPEXTEND:
3652 PageMove(-1, Selection::selStream, true);
3653 break;
3654 case SCI_STUTTEREDPAGEDOWN:
3655 PageMove(1, Selection::noSel, true);
3656 break;
3657 case SCI_STUTTEREDPAGEDOWNEXTEND:
3658 PageMove(1, Selection::selStream, true);
3659 break;
3660 case SCI_PAGEUP:
3661 PageMove(-1);
3662 break;
3663 case SCI_PAGEUPEXTEND:
3664 PageMove(-1, Selection::selStream);
3665 break;
3666 case SCI_PAGEUPRECTEXTEND:
3667 PageMove(-1, Selection::selRectangle);
3668 break;
3669 case SCI_PAGEDOWN:
3670 PageMove(1);
3671 break;
3672 case SCI_PAGEDOWNEXTEND:
3673 PageMove(1, Selection::selStream);
3674 break;
3675 case SCI_PAGEDOWNRECTEXTEND:
3676 PageMove(1, Selection::selRectangle);
3677 break;
3678 case SCI_EDITTOGGLEOVERTYPE:
3679 inOverstrike = !inOverstrike;
3680 ShowCaretAtCurrentPosition();
3681 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
3682 NotifyUpdateUI();
3683 break;
3684 case SCI_CANCEL: // Cancel any modes - handled in subclass
3685 // Also unselect text
3686 CancelModes();
3687 if (sel.Count() > 1) {
3688 // Drop additional selections
3689 InvalidateWholeSelection();
3690 sel.DropAdditionalRanges();
3692 break;
3693 case SCI_DELETEBACK:
3694 DelCharBack(true);
3695 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3696 SetLastXChosen();
3698 EnsureCaretVisible();
3699 break;
3700 case SCI_DELETEBACKNOTLINE:
3701 DelCharBack(false);
3702 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3703 SetLastXChosen();
3705 EnsureCaretVisible();
3706 break;
3707 case SCI_TAB:
3708 Indent(true);
3709 if (caretSticky == SC_CARETSTICKY_OFF) {
3710 SetLastXChosen();
3712 EnsureCaretVisible();
3713 ShowCaretAtCurrentPosition(); // Avoid blinking
3714 break;
3715 case SCI_BACKTAB:
3716 Indent(false);
3717 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3718 SetLastXChosen();
3720 EnsureCaretVisible();
3721 ShowCaretAtCurrentPosition(); // Avoid blinking
3722 break;
3723 case SCI_NEWLINE:
3724 NewLine();
3725 break;
3726 case SCI_FORMFEED:
3727 AddChar('\f');
3728 break;
3729 case SCI_ZOOMIN:
3730 if (vs.zoomLevel < 20) {
3731 vs.zoomLevel++;
3732 InvalidateStyleRedraw();
3733 NotifyZoom();
3735 break;
3736 case SCI_ZOOMOUT:
3737 if (vs.zoomLevel > -10) {
3738 vs.zoomLevel--;
3739 InvalidateStyleRedraw();
3740 NotifyZoom();
3742 break;
3744 case SCI_DELWORDLEFT:
3745 case SCI_DELWORDRIGHT:
3746 case SCI_DELWORDRIGHTEND:
3747 case SCI_DELLINELEFT:
3748 case SCI_DELLINERIGHT:
3749 return DelWordOrLine(iMessage);
3751 case SCI_LINECOPY: {
3752 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
3753 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
3754 CopyRangeToClipboard(pdoc->LineStart(lineStart),
3755 pdoc->LineStart(lineEnd + 1));
3757 break;
3758 case SCI_LINECUT: {
3759 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
3760 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
3761 int start = pdoc->LineStart(lineStart);
3762 int end = pdoc->LineStart(lineEnd + 1);
3763 SetSelection(start, end);
3764 Cut();
3765 SetLastXChosen();
3767 break;
3768 case SCI_LINEDELETE: {
3769 int line = pdoc->LineFromPosition(sel.MainCaret());
3770 int start = pdoc->LineStart(line);
3771 int end = pdoc->LineStart(line + 1);
3772 pdoc->DeleteChars(start, end - start);
3774 break;
3775 case SCI_LINETRANSPOSE:
3776 LineTranspose();
3777 break;
3778 case SCI_LINEDUPLICATE:
3779 Duplicate(true);
3780 break;
3781 case SCI_SELECTIONDUPLICATE:
3782 Duplicate(false);
3783 break;
3784 case SCI_LOWERCASE:
3785 ChangeCaseOfSelection(cmLower);
3786 break;
3787 case SCI_UPPERCASE:
3788 ChangeCaseOfSelection(cmUpper);
3789 break;
3790 case SCI_SCROLLTOSTART:
3791 ScrollTo(0);
3792 break;
3793 case SCI_SCROLLTOEND:
3794 ScrollTo(MaxScrollPos());
3795 break;
3797 return 0;
3800 int Editor::KeyDefault(int, int) {
3801 return 0;
3804 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
3805 DwellEnd(false);
3806 int msg = kmap.Find(key, modifiers);
3807 if (msg) {
3808 if (consumed)
3809 *consumed = true;
3810 return static_cast<int>(WndProc(msg, 0, 0));
3811 } else {
3812 if (consumed)
3813 *consumed = false;
3814 return KeyDefault(key, modifiers);
3818 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
3819 return KeyDownWithModifiers(key, ModifierFlags(shift, ctrl, alt), consumed);
3822 void Editor::Indent(bool forwards) {
3823 UndoGroup ug(pdoc);
3824 for (size_t r=0; r<sel.Count(); r++) {
3825 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
3826 int caretPosition = sel.Range(r).caret.Position();
3827 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
3828 if (lineOfAnchor == lineCurrentPos) {
3829 if (forwards) {
3830 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
3831 caretPosition = sel.Range(r).caret.Position();
3832 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
3833 pdoc->tabIndents) {
3834 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3835 int indentationStep = pdoc->IndentSize();
3836 const int posSelect = pdoc->SetLineIndentation(
3837 lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
3838 sel.Range(r) = SelectionRange(posSelect);
3839 } else {
3840 if (pdoc->useTabs) {
3841 const int lengthInserted = pdoc->InsertString(caretPosition, "\t", 1);
3842 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
3843 } else {
3844 int numSpaces = (pdoc->tabInChars) -
3845 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
3846 if (numSpaces < 1)
3847 numSpaces = pdoc->tabInChars;
3848 const std::string spaceText(numSpaces, ' ');
3849 const int lengthInserted = pdoc->InsertString(caretPosition, spaceText.c_str(),
3850 static_cast<int>(spaceText.length()));
3851 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
3854 } else {
3855 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
3856 pdoc->tabIndents) {
3857 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3858 int indentationStep = pdoc->IndentSize();
3859 const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
3860 sel.Range(r) = SelectionRange(posSelect);
3861 } else {
3862 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
3863 pdoc->tabInChars;
3864 if (newColumn < 0)
3865 newColumn = 0;
3866 int newPos = caretPosition;
3867 while (pdoc->GetColumn(newPos) > newColumn)
3868 newPos--;
3869 sel.Range(r) = SelectionRange(newPos);
3872 } else { // Multiline
3873 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
3874 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
3875 // Multiple lines selected so indent / dedent
3876 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
3877 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
3878 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
3879 lineBottomSel--; // If not selecting any characters on a line, do not indent
3880 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
3881 if (lineOfAnchor < lineCurrentPos) {
3882 if (currentPosPosOnLine == 0)
3883 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
3884 else
3885 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
3886 } else {
3887 if (anchorPosOnLine == 0)
3888 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
3889 else
3890 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
3894 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
3897 class CaseFolderASCII : public CaseFolderTable {
3898 public:
3899 CaseFolderASCII() {
3900 StandardASCII();
3902 ~CaseFolderASCII() {
3907 CaseFolder *Editor::CaseFolderForEncoding() {
3908 // Simple default that only maps ASCII upper case to lower case.
3909 return new CaseFolderASCII();
3913 * Search of a text in the document, in the given range.
3914 * @return The position of the found text, -1 if not found.
3916 long Editor::FindText(
3917 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
3918 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
3919 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
3921 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
3922 int lengthFound = istrlen(ft->lpstrText);
3923 if (!pdoc->HasCaseFolder())
3924 pdoc->SetCaseFolder(CaseFolderForEncoding());
3925 try {
3926 long pos = pdoc->FindText(
3927 static_cast<int>(ft->chrg.cpMin),
3928 static_cast<int>(ft->chrg.cpMax),
3929 ft->lpstrText,
3930 static_cast<int>(wParam),
3931 &lengthFound);
3932 if (pos != -1) {
3933 ft->chrgText.cpMin = pos;
3934 ft->chrgText.cpMax = pos + lengthFound;
3936 return static_cast<int>(pos);
3937 } catch (RegexError &) {
3938 errorStatus = SC_STATUS_WARN_REGEX;
3939 return -1;
3944 * Relocatable search support : Searches relative to current selection
3945 * point and sets the selection to the found text range with
3946 * each search.
3949 * Anchor following searches at current selection start: This allows
3950 * multiple incremental interactive searches to be macro recorded
3951 * while still setting the selection to found text so the find/select
3952 * operation is self-contained.
3954 void Editor::SearchAnchor() {
3955 searchAnchor = SelectionStart().Position();
3959 * Find text from current search anchor: Must call @c SearchAnchor first.
3960 * Used for next text and previous text requests.
3961 * @return The position of the found text, -1 if not found.
3963 long Editor::SearchText(
3964 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
3965 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
3966 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
3967 sptr_t lParam) { ///< The text to search for.
3969 const char *txt = reinterpret_cast<char *>(lParam);
3970 long pos;
3971 int lengthFound = istrlen(txt);
3972 if (!pdoc->HasCaseFolder())
3973 pdoc->SetCaseFolder(CaseFolderForEncoding());
3974 try {
3975 if (iMessage == SCI_SEARCHNEXT) {
3976 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
3977 static_cast<int>(wParam),
3978 &lengthFound);
3979 } else {
3980 pos = pdoc->FindText(searchAnchor, 0, txt,
3981 static_cast<int>(wParam),
3982 &lengthFound);
3984 } catch (RegexError &) {
3985 errorStatus = SC_STATUS_WARN_REGEX;
3986 return -1;
3988 if (pos != -1) {
3989 SetSelection(static_cast<int>(pos), static_cast<int>(pos + lengthFound));
3992 return pos;
3995 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
3996 std::string ret(s);
3997 for (size_t i=0; i<ret.size(); i++) {
3998 switch (caseMapping) {
3999 case cmUpper:
4000 if (ret[i] >= 'a' && ret[i] <= 'z')
4001 ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
4002 break;
4003 case cmLower:
4004 if (ret[i] >= 'A' && ret[i] <= 'Z')
4005 ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
4006 break;
4009 return ret;
4013 * Search for text in the target range of the document.
4014 * @return The position of the found text, -1 if not found.
4016 long Editor::SearchInTarget(const char *text, int length) {
4017 int lengthFound = length;
4019 if (!pdoc->HasCaseFolder())
4020 pdoc->SetCaseFolder(CaseFolderForEncoding());
4021 try {
4022 long pos = pdoc->FindText(targetStart, targetEnd, text,
4023 searchFlags,
4024 &lengthFound);
4025 if (pos != -1) {
4026 targetStart = static_cast<int>(pos);
4027 targetEnd = static_cast<int>(pos + lengthFound);
4029 return pos;
4030 } catch (RegexError &) {
4031 errorStatus = SC_STATUS_WARN_REGEX;
4032 return -1;
4036 void Editor::GoToLine(int lineNo) {
4037 if (lineNo > pdoc->LinesTotal())
4038 lineNo = pdoc->LinesTotal();
4039 if (lineNo < 0)
4040 lineNo = 0;
4041 SetEmptySelection(pdoc->LineStart(lineNo));
4042 ShowCaretAtCurrentPosition();
4043 EnsureCaretVisible();
4046 static bool Close(Point pt1, Point pt2, Point threshold) {
4047 if (std::abs(pt1.x - pt2.x) > threshold.x)
4048 return false;
4049 if (std::abs(pt1.y - pt2.y) > threshold.y)
4050 return false;
4051 return true;
4054 std::string Editor::RangeText(int start, int end) const {
4055 if (start < end) {
4056 int len = end - start;
4057 std::string ret(len, '\0');
4058 for (int i = 0; i < len; i++) {
4059 ret[i] = pdoc->CharAt(start + i);
4061 return ret;
4063 return std::string();
4066 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
4067 if (sel.Empty()) {
4068 if (allowLineCopy) {
4069 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
4070 int start = pdoc->LineStart(currentLine);
4071 int end = pdoc->LineEnd(currentLine);
4073 std::string text = RangeText(start, end);
4074 if (pdoc->eolMode != SC_EOL_LF)
4075 text.push_back('\r');
4076 if (pdoc->eolMode != SC_EOL_CR)
4077 text.push_back('\n');
4078 ss->Copy(text, pdoc->dbcsCodePage,
4079 vs.styles[STYLE_DEFAULT].characterSet, false, true);
4081 } else {
4082 std::string text;
4083 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
4084 if (sel.selType == Selection::selRectangle)
4085 std::sort(rangesInOrder.begin(), rangesInOrder.end());
4086 for (size_t r=0; r<rangesInOrder.size(); r++) {
4087 SelectionRange current = rangesInOrder[r];
4088 text.append(RangeText(current.Start().Position(), current.End().Position()));
4089 if (sel.selType == Selection::selRectangle) {
4090 if (pdoc->eolMode != SC_EOL_LF)
4091 text.push_back('\r');
4092 if (pdoc->eolMode != SC_EOL_CR)
4093 text.push_back('\n');
4096 ss->Copy(text, pdoc->dbcsCodePage,
4097 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
4101 void Editor::CopyRangeToClipboard(int start, int end) {
4102 start = pdoc->ClampPositionIntoDocument(start);
4103 end = pdoc->ClampPositionIntoDocument(end);
4104 SelectionText selectedText;
4105 std::string text = RangeText(start, end);
4106 selectedText.Copy(text,
4107 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
4108 CopyToClipboard(selectedText);
4111 void Editor::CopyText(int length, const char *text) {
4112 SelectionText selectedText;
4113 selectedText.Copy(std::string(text, length),
4114 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
4115 CopyToClipboard(selectedText);
4118 void Editor::SetDragPosition(SelectionPosition newPos) {
4119 if (newPos.Position() >= 0) {
4120 newPos = MovePositionOutsideChar(newPos, 1);
4121 posDrop = newPos;
4123 if (!(posDrag == newPos)) {
4124 caret.on = true;
4125 if (FineTickerAvailable()) {
4126 FineTickerCancel(tickCaret);
4127 if ((caret.active) && (caret.period > 0) && (newPos.Position() < 0))
4128 FineTickerStart(tickCaret, caret.period, caret.period/10);
4129 } else {
4130 SetTicking(true);
4132 InvalidateCaret();
4133 posDrag = newPos;
4134 InvalidateCaret();
4138 void Editor::DisplayCursor(Window::Cursor c) {
4139 if (cursorMode == SC_CURSORNORMAL)
4140 wMain.SetCursor(c);
4141 else
4142 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
4145 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
4146 int xMove = static_cast<int>(ptStart.x - ptNow.x);
4147 int yMove = static_cast<int>(ptStart.y - ptNow.y);
4148 int distanceSquared = xMove * xMove + yMove * yMove;
4149 return distanceSquared > 16;
4152 void Editor::StartDrag() {
4153 // Always handled by subclasses
4154 //SetMouseCapture(true);
4155 //DisplayCursor(Window::cursorArrow);
4158 void Editor::DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular) {
4159 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
4160 if (inDragDrop == ddDragging)
4161 dropWentOutside = false;
4163 bool positionWasInSelection = PositionInSelection(position.Position());
4165 bool positionOnEdgeOfSelection =
4166 (position == SelectionStart()) || (position == SelectionEnd());
4168 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
4169 (positionOnEdgeOfSelection && !moving)) {
4171 SelectionPosition selStart = SelectionStart();
4172 SelectionPosition selEnd = SelectionEnd();
4174 UndoGroup ug(pdoc);
4176 SelectionPosition positionAfterDeletion = position;
4177 if ((inDragDrop == ddDragging) && moving) {
4178 // Remove dragged out text
4179 if (rectangular || sel.selType == Selection::selLines) {
4180 for (size_t r=0; r<sel.Count(); r++) {
4181 if (position >= sel.Range(r).Start()) {
4182 if (position > sel.Range(r).End()) {
4183 positionAfterDeletion.Add(-sel.Range(r).Length());
4184 } else {
4185 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
4189 } else {
4190 if (position > selStart) {
4191 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
4194 ClearSelection();
4196 position = positionAfterDeletion;
4198 std::string convertedText = Document::TransformLineEnds(value, lengthValue, pdoc->eolMode);
4200 if (rectangular) {
4201 PasteRectangular(position, convertedText.c_str(), static_cast<int>(convertedText.length()));
4202 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
4203 SetEmptySelection(position);
4204 } else {
4205 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
4206 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
4207 const int lengthInserted = pdoc->InsertString(
4208 position.Position(), convertedText.c_str(), static_cast<int>(convertedText.length()));
4209 if (lengthInserted > 0) {
4210 SelectionPosition posAfterInsertion = position;
4211 posAfterInsertion.Add(lengthInserted);
4212 SetSelection(posAfterInsertion, position);
4215 } else if (inDragDrop == ddDragging) {
4216 SetEmptySelection(position);
4220 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
4221 DropAt(position, value, strlen(value), moving, rectangular);
4225 * @return true if given position is inside the selection,
4227 bool Editor::PositionInSelection(int pos) {
4228 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
4229 for (size_t r=0; r<sel.Count(); r++) {
4230 if (sel.Range(r).Contains(pos))
4231 return true;
4233 return false;
4236 bool Editor::PointInSelection(Point pt) {
4237 SelectionPosition pos = SPositionFromLocation(pt, false, true);
4238 Point ptPos = LocationFromPosition(pos);
4239 for (size_t r=0; r<sel.Count(); r++) {
4240 SelectionRange range = sel.Range(r);
4241 if (range.Contains(pos)) {
4242 bool hit = true;
4243 if (pos == range.Start()) {
4244 // see if just before selection
4245 if (pt.x < ptPos.x) {
4246 hit = false;
4249 if (pos == range.End()) {
4250 // see if just after selection
4251 if (pt.x > ptPos.x) {
4252 hit = false;
4255 if (hit)
4256 return true;
4259 return false;
4262 bool Editor::PointInSelMargin(Point pt) const {
4263 // Really means: "Point in a margin"
4264 if (vs.fixedColumnWidth > 0) { // There is a margin
4265 PRectangle rcSelMargin = GetClientRectangle();
4266 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart - vs.leftMarginWidth);
4267 rcSelMargin.left = static_cast<XYPOSITION>(vs.textStart - vs.fixedColumnWidth);
4268 return rcSelMargin.ContainsWholePixel(pt);
4269 } else {
4270 return false;
4274 Window::Cursor Editor::GetMarginCursor(Point pt) const {
4275 int x = 0;
4276 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
4277 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
4278 return static_cast<Window::Cursor>(vs.ms[margin].cursor);
4279 x += vs.ms[margin].width;
4281 return Window::cursorReverseArrow;
4284 void Editor::TrimAndSetSelection(int currentPos_, int anchor_) {
4285 sel.TrimSelection(SelectionRange(currentPos_, anchor_));
4286 SetSelection(currentPos_, anchor_);
4289 void Editor::LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine) {
4290 int selCurrentPos, selAnchorPos;
4291 if (wholeLine) {
4292 int lineCurrent_ = pdoc->LineFromPosition(lineCurrentPos_);
4293 int lineAnchor_ = pdoc->LineFromPosition(lineAnchorPos_);
4294 if (lineAnchorPos_ < lineCurrentPos_) {
4295 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
4296 selAnchorPos = pdoc->LineStart(lineAnchor_);
4297 } else if (lineAnchorPos_ > lineCurrentPos_) {
4298 selCurrentPos = pdoc->LineStart(lineCurrent_);
4299 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
4300 } else { // Same line, select it
4301 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
4302 selAnchorPos = pdoc->LineStart(lineAnchor_);
4304 } else {
4305 if (lineAnchorPos_ < lineCurrentPos_) {
4306 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
4307 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4308 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4309 } else if (lineAnchorPos_ > lineCurrentPos_) {
4310 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
4311 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4312 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
4313 } else { // Same line, select it
4314 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4315 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4316 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4319 TrimAndSetSelection(selCurrentPos, selAnchorPos);
4322 void Editor::WordSelection(int pos) {
4323 if (pos < wordSelectAnchorStartPos) {
4324 // Extend backward to the word containing pos.
4325 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
4326 // This ensures that a series of empty lines isn't counted as a single "word".
4327 if (!pdoc->IsLineEndPosition(pos))
4328 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
4329 TrimAndSetSelection(pos, wordSelectAnchorEndPos);
4330 } else if (pos > wordSelectAnchorEndPos) {
4331 // Extend forward to the word containing the character to the left of pos.
4332 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
4333 // This ensures that a series of empty lines isn't counted as a single "word".
4334 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
4335 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
4336 TrimAndSetSelection(pos, wordSelectAnchorStartPos);
4337 } else {
4338 // Select only the anchored word
4339 if (pos >= originalAnchorPos)
4340 TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
4341 else
4342 TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
4346 void Editor::DwellEnd(bool mouseMoved) {
4347 if (mouseMoved)
4348 ticksToDwell = dwellDelay;
4349 else
4350 ticksToDwell = SC_TIME_FOREVER;
4351 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
4352 dwelling = false;
4353 NotifyDwelling(ptMouseLast, dwelling);
4355 if (FineTickerAvailable()) {
4356 FineTickerCancel(tickDwell);
4357 if (mouseMoved && (dwellDelay < SC_TIME_FOREVER)) {
4358 //FineTickerStart(tickDwell, dwellDelay, dwellDelay/10);
4363 void Editor::MouseLeave() {
4364 SetHotSpotRange(NULL);
4365 if (!HaveMouseCapture()) {
4366 ptMouseLast = Point(-1,-1);
4367 DwellEnd(true);
4371 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
4372 return (!rectangular && ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0))
4373 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
4376 void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) {
4377 SetHoverIndicatorPoint(pt);
4378 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
4379 ptMouseLast = pt;
4380 const bool ctrl = (modifiers & SCI_CTRL) != 0;
4381 const bool shift = (modifiers & SCI_SHIFT) != 0;
4382 const bool alt = (modifiers & SCI_ALT) != 0;
4383 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
4384 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4385 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4386 newCharPos = MovePositionOutsideChar(newCharPos, -1);
4387 inDragDrop = ddNone;
4388 sel.SetMoveExtends(false);
4390 if (NotifyMarginClick(pt, modifiers))
4391 return;
4393 NotifyIndicatorClick(true, newPos.Position(), modifiers);
4395 bool inSelMargin = PointInSelMargin(pt);
4396 // In margin ctrl+(double)click should always select everything
4397 if (ctrl && inSelMargin) {
4398 SelectAll();
4399 lastClickTime = curTime;
4400 lastClick = pt;
4401 return;
4403 if (shift && !inSelMargin) {
4404 SetSelection(newPos);
4406 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick, doubleClickCloseThreshold)) {
4407 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
4408 SetMouseCapture(true);
4409 if (FineTickerAvailable()) {
4410 FineTickerStart(tickScroll, 100, 10);
4412 if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))
4413 SetEmptySelection(newPos.Position());
4414 bool doubleClick = false;
4415 // Stop mouse button bounce changing selection type
4416 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
4417 if (inSelMargin) {
4418 // Inside margin selection type should be either selSubLine or selWholeLine.
4419 if (selectionType == selSubLine) {
4420 // If it is selSubLine, we're inside a *double* click and word wrap is enabled,
4421 // so we switch to selWholeLine in order to select whole line.
4422 selectionType = selWholeLine;
4423 } else if (selectionType != selSubLine && selectionType != selWholeLine) {
4424 // If it is neither, reset selection type to line selection.
4425 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4427 } else {
4428 if (selectionType == selChar) {
4429 selectionType = selWord;
4430 doubleClick = true;
4431 } else if (selectionType == selWord) {
4432 // Since we ended up here, we're inside a *triple* click, which should always select
4433 // whole line regardless of word wrap being enabled or not.
4434 selectionType = selWholeLine;
4435 } else {
4436 selectionType = selChar;
4437 originalAnchorPos = sel.MainCaret();
4442 if (selectionType == selWord) {
4443 int charPos = originalAnchorPos;
4444 if (sel.MainCaret() == originalAnchorPos) {
4445 charPos = PositionFromLocation(pt, false, true);
4446 charPos = MovePositionOutsideChar(charPos, -1);
4449 int startWord, endWord;
4450 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
4451 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
4452 endWord = pdoc->ExtendWordSelect(charPos, 1);
4453 } else {
4454 // Selecting backwards, or anchor beyond last character on line. In these cases,
4455 // we select the word containing the character to the *left* of the anchor.
4456 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
4457 startWord = pdoc->ExtendWordSelect(charPos, -1);
4458 endWord = pdoc->ExtendWordSelect(startWord, 1);
4459 } else {
4460 // Anchor at start of line; select nothing to begin with.
4461 startWord = charPos;
4462 endWord = charPos;
4466 wordSelectAnchorStartPos = startWord;
4467 wordSelectAnchorEndPos = endWord;
4468 wordSelectInitialCaretPos = sel.MainCaret();
4469 WordSelection(wordSelectInitialCaretPos);
4470 } else if (selectionType == selSubLine || selectionType == selWholeLine) {
4471 lineAnchorPos = newPos.Position();
4472 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
4473 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
4474 } else {
4475 SetEmptySelection(sel.MainCaret());
4477 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
4478 if (doubleClick) {
4479 NotifyDoubleClick(pt, modifiers);
4480 if (PositionIsHotspot(newCharPos.Position()))
4481 NotifyHotSpotDoubleClicked(newCharPos.Position(), modifiers);
4483 } else { // Single click
4484 if (inSelMargin) {
4485 sel.selType = Selection::selStream;
4486 if (!shift) {
4487 // Single click in margin: select whole line or only subline if word wrap is enabled
4488 lineAnchorPos = newPos.Position();
4489 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4490 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
4491 } else {
4492 // Single shift+click in margin: select from line anchor to clicked line
4493 if (sel.MainAnchor() > sel.MainCaret())
4494 lineAnchorPos = sel.MainAnchor() - 1;
4495 else
4496 lineAnchorPos = sel.MainAnchor();
4497 // Reset selection type if there is an empty selection.
4498 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
4499 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
4500 // This ensures that we continue selecting in the same selection mode.
4501 if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
4502 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4503 LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
4506 SetDragPosition(SelectionPosition(invalidPosition));
4507 SetMouseCapture(true);
4508 if (FineTickerAvailable()) {
4509 FineTickerStart(tickScroll, 100, 10);
4511 } else {
4512 if (PointIsHotspot(pt)) {
4513 NotifyHotSpotClicked(newCharPos.Position(), modifiers);
4514 hotSpotClickPos = newCharPos.Position();
4516 if (!shift) {
4517 if (PointInSelection(pt) && !SelectionEmpty())
4518 inDragDrop = ddInitial;
4519 else
4520 inDragDrop = ddNone;
4522 SetMouseCapture(true);
4523 if (FineTickerAvailable()) {
4524 FineTickerStart(tickScroll, 100, 10);
4526 if (inDragDrop != ddInitial) {
4527 SetDragPosition(SelectionPosition(invalidPosition));
4528 if (!shift) {
4529 if (ctrl && multipleSelection) {
4530 SelectionRange range(newPos);
4531 sel.TentativeSelection(range);
4532 InvalidateSelection(range, true);
4533 } else {
4534 InvalidateSelection(SelectionRange(newPos), true);
4535 if (sel.Count() > 1)
4536 Redraw();
4537 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
4538 sel.Clear();
4539 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
4540 SetSelection(newPos, newPos);
4543 SelectionPosition anchorCurrent = newPos;
4544 if (shift)
4545 anchorCurrent = sel.IsRectangular() ?
4546 sel.Rectangular().anchor : sel.RangeMain().anchor;
4547 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
4548 selectionType = selChar;
4549 originalAnchorPos = sel.MainCaret();
4550 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
4551 SetRectangularRange();
4555 lastClickTime = curTime;
4556 lastClick = pt;
4557 lastXChosen = static_cast<int>(pt.x) + xOffset;
4558 ShowCaretAtCurrentPosition();
4561 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
4562 return ButtonDownWithModifiers(pt, curTime, ModifierFlags(shift, ctrl, alt));
4565 bool Editor::PositionIsHotspot(int position) const {
4566 return vs.styles[pdoc->StyleIndexAt(position)].hotspot;
4569 bool Editor::PointIsHotspot(Point pt) {
4570 int pos = PositionFromLocation(pt, true, true);
4571 if (pos == INVALID_POSITION)
4572 return false;
4573 return PositionIsHotspot(pos);
4576 void Editor::SetHoverIndicatorPosition(int position) {
4577 int hoverIndicatorPosPrev = hoverIndicatorPos;
4578 hoverIndicatorPos = INVALID_POSITION;
4579 if (vs.indicatorsDynamic == 0)
4580 return;
4581 if (position != INVALID_POSITION) {
4582 for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
4583 if (vs.indicators[deco->indicator].IsDynamic()) {
4584 if (pdoc->decorations.ValueAt(deco->indicator, position)) {
4585 hoverIndicatorPos = position;
4590 if (hoverIndicatorPosPrev != hoverIndicatorPos) {
4591 Redraw();
4595 void Editor::SetHoverIndicatorPoint(Point pt) {
4596 if (vs.indicatorsDynamic == 0) {
4597 SetHoverIndicatorPosition(INVALID_POSITION);
4598 } else {
4599 SetHoverIndicatorPosition(PositionFromLocation(pt, true, true));
4603 void Editor::SetHotSpotRange(Point *pt) {
4604 if (pt) {
4605 int pos = PositionFromLocation(*pt, false, true);
4607 // If we don't limit this to word characters then the
4608 // range can encompass more than the run range and then
4609 // the underline will not be drawn properly.
4610 Range hsNew;
4611 hsNew.start = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
4612 hsNew.end = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
4614 // Only invalidate the range if the hotspot range has changed...
4615 if (!(hsNew == hotspot)) {
4616 if (hotspot.Valid()) {
4617 InvalidateRange(hotspot.start, hotspot.end);
4619 hotspot = hsNew;
4620 InvalidateRange(hotspot.start, hotspot.end);
4622 } else {
4623 if (hotspot.Valid()) {
4624 InvalidateRange(hotspot.start, hotspot.end);
4626 hotspot = Range(invalidPosition);
4630 Range Editor::GetHotSpotRange() const {
4631 return hotspot;
4634 void Editor::ButtonMoveWithModifiers(Point pt, int modifiers) {
4635 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
4636 DwellEnd(true);
4639 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
4640 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4641 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
4643 if (inDragDrop == ddInitial) {
4644 if (DragThreshold(ptMouseLast, pt)) {
4645 SetMouseCapture(false);
4646 if (FineTickerAvailable()) {
4647 FineTickerCancel(tickScroll);
4649 SetDragPosition(movePos);
4650 CopySelectionRange(&drag);
4651 StartDrag();
4653 return;
4656 ptMouseLast = pt;
4657 PRectangle rcClient = GetClientRectangle();
4658 Point ptOrigin = GetVisibleOriginInMain();
4659 rcClient.Move(0, -ptOrigin.y);
4660 if (FineTickerAvailable() && (dwellDelay < SC_TIME_FOREVER) && rcClient.Contains(pt)) {
4661 FineTickerStart(tickDwell, dwellDelay, dwellDelay/10);
4663 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
4664 if (HaveMouseCapture()) {
4666 // Slow down autoscrolling/selection
4667 autoScrollTimer.ticksToWait -= timer.tickSize;
4668 if (autoScrollTimer.ticksToWait > 0)
4669 return;
4670 autoScrollTimer.ticksToWait = autoScrollDelay;
4672 // Adjust selection
4673 if (posDrag.IsValid()) {
4674 SetDragPosition(movePos);
4675 } else {
4676 if (selectionType == selChar) {
4677 if (sel.selType == Selection::selStream && (modifiers & SCI_ALT) && mouseSelectionRectangularSwitch) {
4678 sel.selType = Selection::selRectangle;
4680 if (sel.IsRectangular()) {
4681 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
4682 SetSelection(movePos, sel.RangeMain().anchor);
4683 } else if (sel.Count() > 1) {
4684 InvalidateSelection(sel.RangeMain(), false);
4685 SelectionRange range(movePos, sel.RangeMain().anchor);
4686 sel.TentativeSelection(range);
4687 InvalidateSelection(range, true);
4688 } else {
4689 SetSelection(movePos, sel.RangeMain().anchor);
4691 } else if (selectionType == selWord) {
4692 // Continue selecting by word
4693 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
4694 // No need to do anything. Previously this case was lumped
4695 // in with "Moved forward", but that can be harmful in this
4696 // case: a handler for the NotifyDoubleClick re-adjusts
4697 // the selection for a fancier definition of "word" (for
4698 // example, in Perl it is useful to include the leading
4699 // '$', '%' or '@' on variables for word selection). In this
4700 // the ButtonMove() called via Tick() for auto-scrolling
4701 // could result in the fancier word selection adjustment
4702 // being unmade.
4703 } else {
4704 wordSelectInitialCaretPos = -1;
4705 WordSelection(movePos.Position());
4707 } else {
4708 // Continue selecting by line
4709 LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
4713 // Autoscroll
4714 int lineMove = DisplayFromPosition(movePos.Position());
4715 if (pt.y > rcClient.bottom) {
4716 ScrollTo(lineMove - LinesOnScreen() + 1);
4717 Redraw();
4718 } else if (pt.y < rcClient.top) {
4719 ScrollTo(lineMove);
4720 Redraw();
4722 EnsureCaretVisible(false, false, true);
4724 if (hotspot.Valid() && !PointIsHotspot(pt))
4725 SetHotSpotRange(NULL);
4727 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,true) != hotSpotClickPos) {
4728 if (inDragDrop == ddNone) {
4729 DisplayCursor(Window::cursorText);
4731 hotSpotClickPos = INVALID_POSITION;
4734 } else {
4735 if (vs.fixedColumnWidth > 0) { // There is a margin
4736 if (PointInSelMargin(pt)) {
4737 DisplayCursor(GetMarginCursor(pt));
4738 SetHotSpotRange(NULL);
4739 return; // No need to test for selection
4742 // Display regular (drag) cursor over selection
4743 if (PointInSelection(pt) && !SelectionEmpty()) {
4744 DisplayCursor(Window::cursorArrow);
4745 } else {
4746 SetHoverIndicatorPoint(pt);
4747 if (PointIsHotspot(pt)) {
4748 DisplayCursor(Window::cursorHand);
4749 SetHotSpotRange(&pt);
4750 } else {
4751 if (hoverIndicatorPos != invalidPosition)
4752 DisplayCursor(Window::cursorHand);
4753 else
4754 DisplayCursor(Window::cursorText);
4755 SetHotSpotRange(NULL);
4761 void Editor::ButtonMove(Point pt) {
4762 ButtonMoveWithModifiers(pt, 0);
4765 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
4766 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
4767 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
4768 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4769 if (hoverIndicatorPos != INVALID_POSITION)
4770 InvalidateRange(newPos.Position(), newPos.Position() + 1);
4771 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4772 if (inDragDrop == ddInitial) {
4773 inDragDrop = ddNone;
4774 SetEmptySelection(newPos);
4775 selectionType = selChar;
4776 originalAnchorPos = sel.MainCaret();
4778 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
4779 hotSpotClickPos = INVALID_POSITION;
4780 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4781 newCharPos = MovePositionOutsideChar(newCharPos, -1);
4782 NotifyHotSpotReleaseClick(newCharPos.Position(), ctrl ? SCI_CTRL : 0);
4784 if (HaveMouseCapture()) {
4785 if (PointInSelMargin(pt)) {
4786 DisplayCursor(GetMarginCursor(pt));
4787 } else {
4788 DisplayCursor(Window::cursorText);
4789 SetHotSpotRange(NULL);
4791 ptMouseLast = pt;
4792 SetMouseCapture(false);
4793 if (FineTickerAvailable()) {
4794 FineTickerCancel(tickScroll);
4796 NotifyIndicatorClick(false, newPos.Position(), 0);
4797 if (inDragDrop == ddDragging) {
4798 SelectionPosition selStart = SelectionStart();
4799 SelectionPosition selEnd = SelectionEnd();
4800 if (selStart < selEnd) {
4801 if (drag.Length()) {
4802 const int length = static_cast<int>(drag.Length());
4803 if (ctrl) {
4804 const int lengthInserted = pdoc->InsertString(
4805 newPos.Position(), drag.Data(), length);
4806 if (lengthInserted > 0) {
4807 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4809 } else if (newPos < selStart) {
4810 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
4811 const int lengthInserted = pdoc->InsertString(
4812 newPos.Position(), drag.Data(), length);
4813 if (lengthInserted > 0) {
4814 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4816 } else if (newPos > selEnd) {
4817 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
4818 newPos.Add(-static_cast<int>(drag.Length()));
4819 const int lengthInserted = pdoc->InsertString(
4820 newPos.Position(), drag.Data(), length);
4821 if (lengthInserted > 0) {
4822 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4824 } else {
4825 SetEmptySelection(newPos.Position());
4827 drag.Clear();
4829 selectionType = selChar;
4831 } else {
4832 if (selectionType == selChar) {
4833 if (sel.Count() > 1) {
4834 sel.RangeMain() =
4835 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
4836 InvalidateWholeSelection();
4837 } else {
4838 SetSelection(newPos, sel.RangeMain().anchor);
4841 sel.CommitTentative();
4843 SetRectangularRange();
4844 lastClickTime = curTime;
4845 lastClick = pt;
4846 lastXChosen = static_cast<int>(pt.x) + xOffset;
4847 if (sel.selType == Selection::selStream) {
4848 SetLastXChosen();
4850 inDragDrop = ddNone;
4851 EnsureCaretVisible(false);
4855 // Called frequently to perform background UI including
4856 // caret blinking and automatic scrolling.
4857 void Editor::Tick() {
4858 if (HaveMouseCapture()) {
4859 // Auto scroll
4860 ButtonMove(ptMouseLast);
4862 if (caret.period > 0) {
4863 timer.ticksToWait -= timer.tickSize;
4864 if (timer.ticksToWait <= 0) {
4865 caret.on = !caret.on;
4866 timer.ticksToWait = caret.period;
4867 if (caret.active) {
4868 InvalidateCaret();
4872 if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) {
4873 scrollWidth = view.lineWidthMaxSeen;
4874 SetScrollBars();
4876 if ((dwellDelay < SC_TIME_FOREVER) &&
4877 (ticksToDwell > 0) &&
4878 (!HaveMouseCapture()) &&
4879 (ptMouseLast.y >= 0)) {
4880 ticksToDwell -= timer.tickSize;
4881 if (ticksToDwell <= 0) {
4882 dwelling = true;
4883 NotifyDwelling(ptMouseLast, dwelling);
4888 bool Editor::Idle() {
4889 bool needWrap = Wrapping() && wrapPending.NeedsWrap();
4891 if (needWrap) {
4892 // Wrap lines during idle.
4893 WrapLines(wsIdle);
4894 // No more wrapping
4895 needWrap = wrapPending.NeedsWrap();
4896 } else if (needIdleStyling) {
4897 IdleStyling();
4900 // Add more idle things to do here, but make sure idleDone is
4901 // set correctly before the function returns. returning
4902 // false will stop calling this idle function until SetIdle() is
4903 // called again.
4905 const bool idleDone = !needWrap && !needIdleStyling; // && thatDone && theOtherThingDone...
4907 return !idleDone;
4910 void Editor::SetTicking(bool) {
4911 // SetTicking is deprecated. In the past it was pure virtual and was overridden in each
4912 // derived platform class but fine grained timers should now be implemented.
4913 // Either way, execution should not arrive here so assert failure.
4914 assert(false);
4917 void Editor::TickFor(TickReason reason) {
4918 switch (reason) {
4919 case tickCaret:
4920 caret.on = !caret.on;
4921 if (caret.active) {
4922 InvalidateCaret();
4924 break;
4925 case tickScroll:
4926 // Auto scroll
4927 ButtonMove(ptMouseLast);
4928 break;
4929 case tickWiden:
4930 SetScrollBars();
4931 FineTickerCancel(tickWiden);
4932 break;
4933 case tickDwell:
4934 if ((!HaveMouseCapture()) &&
4935 (ptMouseLast.y >= 0)) {
4936 dwelling = true;
4937 NotifyDwelling(ptMouseLast, dwelling);
4939 FineTickerCancel(tickDwell);
4940 break;
4941 default:
4942 // tickPlatform handled by subclass
4943 break;
4947 bool Editor::FineTickerAvailable() {
4948 return false;
4951 // FineTickerStart is be overridden by subclasses that support fine ticking so
4952 // this method should never be called.
4953 bool Editor::FineTickerRunning(TickReason) {
4954 assert(false);
4955 return false;
4958 // FineTickerStart is be overridden by subclasses that support fine ticking so
4959 // this method should never be called.
4960 void Editor::FineTickerStart(TickReason, int, int) {
4961 assert(false);
4964 // FineTickerCancel is be overridden by subclasses that support fine ticking so
4965 // this method should never be called.
4966 void Editor::FineTickerCancel(TickReason) {
4967 assert(false);
4970 void Editor::SetFocusState(bool focusState) {
4971 hasFocus = focusState;
4972 NotifyFocus(hasFocus);
4973 if (!hasFocus) {
4974 CancelModes();
4976 ShowCaretAtCurrentPosition();
4979 int Editor::PositionAfterArea(PRectangle rcArea) const {
4980 // The start of the document line after the display line after the area
4981 // This often means that the line after a modification is restyled which helps
4982 // detect multiline comment additions and heals single line comments
4983 int lineAfter = TopLineOfMain() + static_cast<int>(rcArea.bottom - 1) / vs.lineHeight + 1;
4984 if (lineAfter < cs.LinesDisplayed())
4985 return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
4986 else
4987 return pdoc->Length();
4990 // Style to a position within the view. If this causes a change at end of last line then
4991 // affects later lines so style all the viewed text.
4992 void Editor::StyleToPositionInView(Position pos) {
4993 int endWindow = PositionAfterArea(GetClientDrawingRectangle());
4994 if (pos > endWindow)
4995 pos = endWindow;
4996 const int styleAtEnd = pdoc->StyleIndexAt(pos-1);
4997 pdoc->EnsureStyledTo(pos);
4998 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleIndexAt(pos-1))) {
4999 // Style at end of line changed so is multi-line change like starting a comment
5000 // so require rest of window to be styled.
5001 DiscardOverdraw(); // Prepared bitmaps may be invalid
5002 // DiscardOverdraw may have truncated client drawing area so recalculate endWindow
5003 endWindow = PositionAfterArea(GetClientDrawingRectangle());
5004 pdoc->EnsureStyledTo(endWindow);
5008 int Editor::PositionAfterMaxStyling(int posMax, bool scrolling) const {
5009 if ((idleStyling == SC_IDLESTYLING_NONE) || (idleStyling == SC_IDLESTYLING_AFTERVISIBLE)) {
5010 // Both states do not limit styling
5011 return posMax;
5014 // Try to keep time taken by styling reasonable so interaction remains smooth.
5015 // When scrolling, allow less time to ensure responsive
5016 const double secondsAllowed = scrolling ? 0.005 : 0.02;
5018 const int linesToStyle = Platform::Clamp(static_cast<int>(secondsAllowed / pdoc->durationStyleOneLine),
5019 10, 0x10000);
5020 const int stylingMaxLine = std::min(
5021 static_cast<int>(pdoc->LineFromPosition(pdoc->GetEndStyled()) + linesToStyle),
5022 pdoc->LinesTotal());
5023 return std::min(static_cast<int>(pdoc->LineStart(stylingMaxLine)), posMax);
5026 void Editor::StartIdleStyling(bool truncatedLastStyling) {
5027 if ((idleStyling == SC_IDLESTYLING_ALL) || (idleStyling == SC_IDLESTYLING_AFTERVISIBLE)) {
5028 if (pdoc->GetEndStyled() < pdoc->Length()) {
5029 // Style remainder of document in idle time
5030 needIdleStyling = true;
5032 } else if (truncatedLastStyling) {
5033 needIdleStyling = true;
5036 if (needIdleStyling) {
5037 SetIdle(true);
5041 // Style for an area but bound the amount of styling to remain responsive
5042 void Editor::StyleAreaBounded(PRectangle rcArea, bool scrolling) {
5043 const int posAfterArea = PositionAfterArea(rcArea);
5044 const int posAfterMax = PositionAfterMaxStyling(posAfterArea, scrolling);
5045 if (posAfterMax < posAfterArea) {
5046 // Idle styling may be performed before current visible area
5047 // Style a bit now then style further in idle time
5048 pdoc->StyleToAdjustingLineDuration(posAfterMax);
5049 } else {
5050 // Can style all wanted now.
5051 StyleToPositionInView(posAfterArea);
5053 StartIdleStyling(posAfterMax < posAfterArea);
5056 void Editor::IdleStyling() {
5057 const int posAfterArea = PositionAfterArea(GetClientRectangle());
5058 const int endGoal = (idleStyling >= SC_IDLESTYLING_AFTERVISIBLE) ?
5059 pdoc->Length() : posAfterArea;
5060 const int posAfterMax = PositionAfterMaxStyling(endGoal, false);
5061 pdoc->StyleToAdjustingLineDuration(posAfterMax);
5062 if (pdoc->GetEndStyled() >= endGoal) {
5063 needIdleStyling = false;
5067 void Editor::IdleWork() {
5068 // Style the line after the modification as this allows modifications that change just the
5069 // line of the modification to heal instead of propagating to the rest of the window.
5070 if (workNeeded.items & WorkNeeded::workStyle) {
5071 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2));
5073 NotifyUpdateUI();
5074 workNeeded.Reset();
5077 void Editor::QueueIdleWork(WorkNeeded::workItems items, int upTo) {
5078 workNeeded.Need(items, upTo);
5081 bool Editor::PaintContains(PRectangle rc) {
5082 if (rc.Empty()) {
5083 return true;
5084 } else {
5085 return rcPaint.Contains(rc);
5089 bool Editor::PaintContainsMargin() {
5090 if (wMargin.GetID()) {
5091 // With separate margin view, paint of text view
5092 // never contains margin.
5093 return false;
5095 PRectangle rcSelMargin = GetClientRectangle();
5096 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart);
5097 return PaintContains(rcSelMargin);
5100 void Editor::CheckForChangeOutsidePaint(Range r) {
5101 if (paintState == painting && !paintingAllText) {
5102 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
5103 if (!r.Valid())
5104 return;
5106 PRectangle rcRange = RectangleFromRange(r, 0);
5107 PRectangle rcText = GetTextRectangle();
5108 if (rcRange.top < rcText.top) {
5109 rcRange.top = rcText.top;
5111 if (rcRange.bottom > rcText.bottom) {
5112 rcRange.bottom = rcText.bottom;
5115 if (!PaintContains(rcRange)) {
5116 AbandonPaint();
5117 paintAbandonedByStyling = true;
5122 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
5123 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
5124 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
5125 CheckForChangeOutsidePaint(Range(braces[0]));
5126 CheckForChangeOutsidePaint(Range(pos0));
5127 braces[0] = pos0;
5129 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
5130 CheckForChangeOutsidePaint(Range(braces[1]));
5131 CheckForChangeOutsidePaint(Range(pos1));
5132 braces[1] = pos1;
5134 bracesMatchStyle = matchStyle;
5135 if (paintState == notPainting) {
5136 Redraw();
5141 void Editor::SetAnnotationHeights(int start, int end) {
5142 if (vs.annotationVisible) {
5143 RefreshStyleData();
5144 bool changedHeight = false;
5145 for (int line=start; line<end && line<pdoc->LinesTotal(); line++) {
5146 int linesWrapped = 1;
5147 if (Wrapping()) {
5148 AutoSurface surface(this);
5149 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
5150 if (surface && ll) {
5151 view.LayoutLine(*this, line, surface, vs, ll, wrapWidth);
5152 linesWrapped = ll->lines;
5155 if (cs.SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
5156 changedHeight = true;
5158 if (changedHeight) {
5159 Redraw();
5164 void Editor::SetDocPointer(Document *document) {
5165 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
5166 pdoc->RemoveWatcher(this, 0);
5167 pdoc->Release();
5168 if (document == NULL) {
5169 pdoc = new Document();
5170 } else {
5171 pdoc = document;
5173 pdoc->AddRef();
5175 // Ensure all positions within document
5176 sel.Clear();
5177 targetStart = 0;
5178 targetEnd = 0;
5180 braces[0] = invalidPosition;
5181 braces[1] = invalidPosition;
5183 vs.ReleaseAllExtendedStyles();
5185 SetRepresentations();
5187 // Reset the contraction state to fully shown.
5188 cs.Clear();
5189 cs.InsertLines(0, pdoc->LinesTotal() - 1);
5190 SetAnnotationHeights(0, pdoc->LinesTotal());
5191 view.llc.Deallocate();
5192 NeedWrapping();
5194 hotspot = Range(invalidPosition);
5195 hoverIndicatorPos = invalidPosition;
5197 view.ClearAllTabstops();
5199 pdoc->AddWatcher(this, 0);
5200 SetScrollBars();
5201 Redraw();
5204 void Editor::SetAnnotationVisible(int visible) {
5205 if (vs.annotationVisible != visible) {
5206 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
5207 vs.annotationVisible = visible;
5208 if (changedFromOrToHidden) {
5209 int dir = vs.annotationVisible ? 1 : -1;
5210 for (int line=0; line<pdoc->LinesTotal(); line++) {
5211 int annotationLines = pdoc->AnnotationLines(line);
5212 if (annotationLines > 0) {
5213 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
5217 Redraw();
5222 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
5224 int Editor::ExpandLine(int line) {
5225 int lineMaxSubord = pdoc->GetLastChild(line);
5226 line++;
5227 while (line <= lineMaxSubord) {
5228 cs.SetVisible(line, line, true);
5229 int level = pdoc->GetLevel(line);
5230 if (level & SC_FOLDLEVELHEADERFLAG) {
5231 if (cs.GetExpanded(line)) {
5232 line = ExpandLine(line);
5233 } else {
5234 line = pdoc->GetLastChild(line);
5237 line++;
5239 return lineMaxSubord;
5242 void Editor::SetFoldExpanded(int lineDoc, bool expanded) {
5243 if (cs.SetExpanded(lineDoc, expanded)) {
5244 RedrawSelMargin();
5248 void Editor::FoldLine(int line, int action) {
5249 if (line >= 0) {
5250 if (action == SC_FOLDACTION_TOGGLE) {
5251 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
5252 line = pdoc->GetFoldParent(line);
5253 if (line < 0)
5254 return;
5256 action = (cs.GetExpanded(line)) ? SC_FOLDACTION_CONTRACT : SC_FOLDACTION_EXPAND;
5259 if (action == SC_FOLDACTION_CONTRACT) {
5260 int lineMaxSubord = pdoc->GetLastChild(line);
5261 if (lineMaxSubord > line) {
5262 cs.SetExpanded(line, 0);
5263 cs.SetVisible(line + 1, lineMaxSubord, false);
5265 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
5266 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
5267 // This does not re-expand the fold
5268 EnsureCaretVisible();
5272 } else {
5273 if (!(cs.GetVisible(line))) {
5274 EnsureLineVisible(line, false);
5275 GoToLine(line);
5277 cs.SetExpanded(line, 1);
5278 ExpandLine(line);
5281 SetScrollBars();
5282 Redraw();
5286 void Editor::FoldExpand(int line, int action, int level) {
5287 bool expanding = action == SC_FOLDACTION_EXPAND;
5288 if (action == SC_FOLDACTION_TOGGLE) {
5289 expanding = !cs.GetExpanded(line);
5291 SetFoldExpanded(line, expanding);
5292 if (expanding && (cs.HiddenLines() == 0))
5293 // Nothing to do
5294 return;
5295 int lineMaxSubord = pdoc->GetLastChild(line, level & SC_FOLDLEVELNUMBERMASK);
5296 line++;
5297 cs.SetVisible(line, lineMaxSubord, expanding);
5298 while (line <= lineMaxSubord) {
5299 int levelLine = pdoc->GetLevel(line);
5300 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
5301 SetFoldExpanded(line, expanding);
5303 line++;
5305 SetScrollBars();
5306 Redraw();
5309 int Editor::ContractedFoldNext(int lineStart) const {
5310 for (int line = lineStart; line<pdoc->LinesTotal();) {
5311 if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
5312 return line;
5313 line = cs.ContractedNext(line+1);
5314 if (line < 0)
5315 return -1;
5318 return -1;
5322 * Recurse up from this line to find any folds that prevent this line from being visible
5323 * and unfold them all.
5325 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
5327 // In case in need of wrapping to ensure DisplayFromDoc works.
5328 if (lineDoc >= wrapPending.start)
5329 WrapLines(wsAll);
5331 if (!cs.GetVisible(lineDoc)) {
5332 // Back up to find a non-blank line
5333 int lookLine = lineDoc;
5334 int lookLineLevel = pdoc->GetLevel(lookLine);
5335 while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
5336 lookLineLevel = pdoc->GetLevel(--lookLine);
5338 int lineParent = pdoc->GetFoldParent(lookLine);
5339 if (lineParent < 0) {
5340 // Backed up to a top level line, so try to find parent of initial line
5341 lineParent = pdoc->GetFoldParent(lineDoc);
5343 if (lineParent >= 0) {
5344 if (lineDoc != lineParent)
5345 EnsureLineVisible(lineParent, enforcePolicy);
5346 if (!cs.GetExpanded(lineParent)) {
5347 cs.SetExpanded(lineParent, 1);
5348 ExpandLine(lineParent);
5351 SetScrollBars();
5352 Redraw();
5354 if (enforcePolicy) {
5355 int lineDisplay = cs.DisplayFromDoc(lineDoc);
5356 if (visiblePolicy & VISIBLE_SLOP) {
5357 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
5358 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
5359 SetVerticalScrollPos();
5360 Redraw();
5361 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
5362 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
5363 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
5364 SetVerticalScrollPos();
5365 Redraw();
5367 } else {
5368 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
5369 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
5370 SetVerticalScrollPos();
5371 Redraw();
5377 void Editor::FoldAll(int action) {
5378 pdoc->EnsureStyledTo(pdoc->Length());
5379 int maxLine = pdoc->LinesTotal();
5380 bool expanding = action == SC_FOLDACTION_EXPAND;
5381 if (action == SC_FOLDACTION_TOGGLE) {
5382 // Discover current state
5383 for (int lineSeek = 0; lineSeek < maxLine; lineSeek++) {
5384 if (pdoc->GetLevel(lineSeek) & SC_FOLDLEVELHEADERFLAG) {
5385 expanding = !cs.GetExpanded(lineSeek);
5386 break;
5390 if (expanding) {
5391 cs.SetVisible(0, maxLine-1, true);
5392 for (int line = 0; line < maxLine; line++) {
5393 int levelLine = pdoc->GetLevel(line);
5394 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
5395 SetFoldExpanded(line, true);
5398 } else {
5399 for (int line = 0; line < maxLine; line++) {
5400 int level = pdoc->GetLevel(line);
5401 if ((level & SC_FOLDLEVELHEADERFLAG) &&
5402 (SC_FOLDLEVELBASE == (level & SC_FOLDLEVELNUMBERMASK))) {
5403 SetFoldExpanded(line, false);
5404 int lineMaxSubord = pdoc->GetLastChild(line, -1);
5405 if (lineMaxSubord > line) {
5406 cs.SetVisible(line + 1, lineMaxSubord, false);
5411 SetScrollBars();
5412 Redraw();
5415 void Editor::FoldChanged(int line, int levelNow, int levelPrev) {
5416 if (levelNow & SC_FOLDLEVELHEADERFLAG) {
5417 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
5418 // Adding a fold point.
5419 if (cs.SetExpanded(line, true)) {
5420 RedrawSelMargin();
5422 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
5424 } else if (levelPrev & SC_FOLDLEVELHEADERFLAG) {
5425 if (!cs.GetExpanded(line)) {
5426 // Removing the fold from one that has been contracted so should expand
5427 // otherwise lines are left invisible with no way to make them visible
5428 if (cs.SetExpanded(line, true)) {
5429 RedrawSelMargin();
5431 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
5434 if (!(levelNow & SC_FOLDLEVELWHITEFLAG) &&
5435 ((levelPrev & SC_FOLDLEVELNUMBERMASK) > (levelNow & SC_FOLDLEVELNUMBERMASK))) {
5436 if (cs.HiddenLines()) {
5437 // See if should still be hidden
5438 int parentLine = pdoc->GetFoldParent(line);
5439 if ((parentLine < 0) || (cs.GetExpanded(parentLine) && cs.GetVisible(parentLine))) {
5440 cs.SetVisible(line, line, true);
5441 SetScrollBars();
5442 Redraw();
5448 void Editor::NeedShown(int pos, int len) {
5449 if (foldAutomatic & SC_AUTOMATICFOLD_SHOW) {
5450 int lineStart = pdoc->LineFromPosition(pos);
5451 int lineEnd = pdoc->LineFromPosition(pos+len);
5452 for (int line = lineStart; line <= lineEnd; line++) {
5453 EnsureLineVisible(line, false);
5455 } else {
5456 NotifyNeedShown(pos, len);
5460 int Editor::GetTag(char *tagValue, int tagNumber) {
5461 const char *text = 0;
5462 int length = 0;
5463 if ((tagNumber >= 1) && (tagNumber <= 9)) {
5464 char name[3] = "\\?";
5465 name[1] = static_cast<char>(tagNumber + '0');
5466 length = 2;
5467 text = pdoc->SubstituteByPosition(name, &length);
5469 if (tagValue) {
5470 if (text)
5471 memcpy(tagValue, text, length + 1);
5472 else
5473 *tagValue = '\0';
5475 return length;
5478 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
5479 UndoGroup ug(pdoc);
5480 if (length == -1)
5481 length = istrlen(text);
5482 if (replacePatterns) {
5483 text = pdoc->SubstituteByPosition(text, &length);
5484 if (!text) {
5485 return 0;
5488 if (targetStart != targetEnd)
5489 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
5490 targetEnd = targetStart;
5491 const int lengthInserted = pdoc->InsertString(targetStart, text, length);
5492 targetEnd = targetStart + lengthInserted;
5493 return length;
5496 bool Editor::IsUnicodeMode() const {
5497 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
5500 int Editor::CodePage() const {
5501 if (pdoc)
5502 return pdoc->dbcsCodePage;
5503 else
5504 return 0;
5507 int Editor::WrapCount(int line) {
5508 AutoSurface surface(this);
5509 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
5511 if (surface && ll) {
5512 view.LayoutLine(*this, line, surface, vs, ll, wrapWidth);
5513 return ll->lines;
5514 } else {
5515 return 1;
5519 void Editor::AddStyledText(char *buffer, int appendLength) {
5520 // The buffer consists of alternating character bytes and style bytes
5521 int textLength = appendLength / 2;
5522 std::string text(textLength, '\0');
5523 int i;
5524 for (i = 0; i < textLength; i++) {
5525 text[i] = buffer[i*2];
5527 const int lengthInserted = pdoc->InsertString(CurrentPosition(), text.c_str(), textLength);
5528 for (i = 0; i < textLength; i++) {
5529 text[i] = buffer[i*2+1];
5531 pdoc->StartStyling(CurrentPosition(), static_cast<unsigned char>(0xff));
5532 pdoc->SetStyles(textLength, text.c_str());
5533 SetEmptySelection(sel.MainCaret() + lengthInserted);
5536 static bool ValidMargin(uptr_t wParam) {
5537 return wParam <= SC_MAX_MARGIN;
5540 static char *CharPtrFromSPtr(sptr_t lParam) {
5541 return reinterpret_cast<char *>(lParam);
5544 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5545 vs.EnsureStyle(wParam);
5546 switch (iMessage) {
5547 case SCI_STYLESETFORE:
5548 vs.styles[wParam].fore = ColourDesired(static_cast<long>(lParam));
5549 break;
5550 case SCI_STYLESETBACK:
5551 vs.styles[wParam].back = ColourDesired(static_cast<long>(lParam));
5552 break;
5553 case SCI_STYLESETBOLD:
5554 vs.styles[wParam].weight = lParam != 0 ? SC_WEIGHT_BOLD : SC_WEIGHT_NORMAL;
5555 break;
5556 case SCI_STYLESETWEIGHT:
5557 vs.styles[wParam].weight = static_cast<int>(lParam);
5558 break;
5559 case SCI_STYLESETITALIC:
5560 vs.styles[wParam].italic = lParam != 0;
5561 break;
5562 case SCI_STYLESETEOLFILLED:
5563 vs.styles[wParam].eolFilled = lParam != 0;
5564 break;
5565 case SCI_STYLESETSIZE:
5566 vs.styles[wParam].size = static_cast<int>(lParam * SC_FONT_SIZE_MULTIPLIER);
5567 break;
5568 case SCI_STYLESETSIZEFRACTIONAL:
5569 vs.styles[wParam].size = static_cast<int>(lParam);
5570 break;
5571 case SCI_STYLESETFONT:
5572 if (lParam != 0) {
5573 vs.SetStyleFontName(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
5575 break;
5576 case SCI_STYLESETUNDERLINE:
5577 vs.styles[wParam].underline = lParam != 0;
5578 break;
5579 case SCI_STYLESETCASE:
5580 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
5581 break;
5582 case SCI_STYLESETCHARACTERSET:
5583 vs.styles[wParam].characterSet = static_cast<int>(lParam);
5584 pdoc->SetCaseFolder(NULL);
5585 break;
5586 case SCI_STYLESETVISIBLE:
5587 vs.styles[wParam].visible = lParam != 0;
5588 break;
5589 case SCI_STYLESETCHANGEABLE:
5590 vs.styles[wParam].changeable = lParam != 0;
5591 break;
5592 case SCI_STYLESETHOTSPOT:
5593 vs.styles[wParam].hotspot = lParam != 0;
5594 break;
5596 InvalidateStyleRedraw();
5599 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5600 vs.EnsureStyle(wParam);
5601 switch (iMessage) {
5602 case SCI_STYLEGETFORE:
5603 return vs.styles[wParam].fore.AsLong();
5604 case SCI_STYLEGETBACK:
5605 return vs.styles[wParam].back.AsLong();
5606 case SCI_STYLEGETBOLD:
5607 return vs.styles[wParam].weight > SC_WEIGHT_NORMAL;
5608 case SCI_STYLEGETWEIGHT:
5609 return vs.styles[wParam].weight;
5610 case SCI_STYLEGETITALIC:
5611 return vs.styles[wParam].italic ? 1 : 0;
5612 case SCI_STYLEGETEOLFILLED:
5613 return vs.styles[wParam].eolFilled ? 1 : 0;
5614 case SCI_STYLEGETSIZE:
5615 return vs.styles[wParam].size / SC_FONT_SIZE_MULTIPLIER;
5616 case SCI_STYLEGETSIZEFRACTIONAL:
5617 return vs.styles[wParam].size;
5618 case SCI_STYLEGETFONT:
5619 return StringResult(lParam, vs.styles[wParam].fontName);
5620 case SCI_STYLEGETUNDERLINE:
5621 return vs.styles[wParam].underline ? 1 : 0;
5622 case SCI_STYLEGETCASE:
5623 return static_cast<int>(vs.styles[wParam].caseForce);
5624 case SCI_STYLEGETCHARACTERSET:
5625 return vs.styles[wParam].characterSet;
5626 case SCI_STYLEGETVISIBLE:
5627 return vs.styles[wParam].visible ? 1 : 0;
5628 case SCI_STYLEGETCHANGEABLE:
5629 return vs.styles[wParam].changeable ? 1 : 0;
5630 case SCI_STYLEGETHOTSPOT:
5631 return vs.styles[wParam].hotspot ? 1 : 0;
5633 return 0;
5636 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
5637 const size_t len = val ? strlen(val) : 0;
5638 if (lParam) {
5639 char *ptr = CharPtrFromSPtr(lParam);
5640 if (val)
5641 memcpy(ptr, val, len+1);
5642 else
5643 *ptr = 0;
5645 return len; // Not including NUL
5648 sptr_t Editor::BytesResult(sptr_t lParam, const unsigned char *val, size_t len) {
5649 // No NUL termination: len is number of valid/displayed bytes
5650 if ((lParam) && (len > 0)) {
5651 char *ptr = CharPtrFromSPtr(lParam);
5652 if (val)
5653 memcpy(ptr, val, len);
5654 else
5655 *ptr = 0;
5657 return val ? len : 0;
5660 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5661 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
5663 // Optional macro recording hook
5664 if (recordingMacro)
5665 NotifyMacroRecord(iMessage, wParam, lParam);
5667 switch (iMessage) {
5669 case SCI_GETTEXT: {
5670 if (lParam == 0)
5671 return pdoc->Length() + 1;
5672 if (wParam == 0)
5673 return 0;
5674 char *ptr = CharPtrFromSPtr(lParam);
5675 unsigned int iChar = 0;
5676 for (; iChar < wParam - 1; iChar++)
5677 ptr[iChar] = pdoc->CharAt(iChar);
5678 ptr[iChar] = '\0';
5679 return iChar;
5682 case SCI_SETTEXT: {
5683 if (lParam == 0)
5684 return 0;
5685 UndoGroup ug(pdoc);
5686 pdoc->DeleteChars(0, pdoc->Length());
5687 SetEmptySelection(0);
5688 const char *text = CharPtrFromSPtr(lParam);
5689 pdoc->InsertString(0, text, istrlen(text));
5690 return 1;
5693 case SCI_GETTEXTLENGTH:
5694 return pdoc->Length();
5696 case SCI_CUT:
5697 Cut();
5698 SetLastXChosen();
5699 break;
5701 case SCI_COPY:
5702 Copy();
5703 break;
5705 case SCI_COPYALLOWLINE:
5706 CopyAllowLine();
5707 break;
5709 case SCI_VERTICALCENTRECARET:
5710 VerticalCentreCaret();
5711 break;
5713 case SCI_MOVESELECTEDLINESUP:
5714 MoveSelectedLinesUp();
5715 break;
5717 case SCI_MOVESELECTEDLINESDOWN:
5718 MoveSelectedLinesDown();
5719 break;
5721 case SCI_COPYRANGE:
5722 CopyRangeToClipboard(static_cast<int>(wParam), static_cast<int>(lParam));
5723 break;
5725 case SCI_COPYTEXT:
5726 CopyText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
5727 break;
5729 case SCI_PASTE:
5730 Paste();
5731 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5732 SetLastXChosen();
5734 EnsureCaretVisible();
5735 break;
5737 case SCI_CLEAR:
5738 Clear();
5739 SetLastXChosen();
5740 EnsureCaretVisible();
5741 break;
5743 case SCI_UNDO:
5744 Undo();
5745 SetLastXChosen();
5746 break;
5748 case SCI_CANUNDO:
5749 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
5751 case SCI_EMPTYUNDOBUFFER:
5752 pdoc->DeleteUndoHistory();
5753 return 0;
5755 case SCI_GETFIRSTVISIBLELINE:
5756 return topLine;
5758 case SCI_SETFIRSTVISIBLELINE:
5759 ScrollTo(static_cast<int>(wParam));
5760 break;
5762 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
5763 int lineStart = pdoc->LineStart(static_cast<int>(wParam));
5764 int lineEnd = pdoc->LineStart(static_cast<int>(wParam + 1));
5765 if (lParam == 0) {
5766 return lineEnd - lineStart;
5768 char *ptr = CharPtrFromSPtr(lParam);
5769 int iPlace = 0;
5770 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
5771 ptr[iPlace++] = pdoc->CharAt(iChar);
5773 return iPlace;
5776 case SCI_GETLINECOUNT:
5777 if (pdoc->LinesTotal() == 0)
5778 return 1;
5779 else
5780 return pdoc->LinesTotal();
5782 case SCI_GETMODIFY:
5783 return !pdoc->IsSavePoint();
5785 case SCI_SETSEL: {
5786 int nStart = static_cast<int>(wParam);
5787 int nEnd = static_cast<int>(lParam);
5788 if (nEnd < 0)
5789 nEnd = pdoc->Length();
5790 if (nStart < 0)
5791 nStart = nEnd; // Remove selection
5792 InvalidateSelection(SelectionRange(nStart, nEnd));
5793 sel.Clear();
5794 sel.selType = Selection::selStream;
5795 SetSelection(nEnd, nStart);
5796 EnsureCaretVisible();
5798 break;
5800 case SCI_GETSELTEXT: {
5801 SelectionText selectedText;
5802 CopySelectionRange(&selectedText);
5803 if (lParam == 0) {
5804 return selectedText.LengthWithTerminator();
5805 } else {
5806 char *ptr = CharPtrFromSPtr(lParam);
5807 unsigned int iChar = 0;
5808 if (selectedText.Length()) {
5809 for (; iChar < selectedText.LengthWithTerminator(); iChar++)
5810 ptr[iChar] = selectedText.Data()[iChar];
5811 } else {
5812 ptr[0] = '\0';
5814 return iChar;
5818 case SCI_LINEFROMPOSITION:
5819 if (static_cast<int>(wParam) < 0)
5820 return 0;
5821 return pdoc->LineFromPosition(static_cast<int>(wParam));
5823 case SCI_POSITIONFROMLINE:
5824 if (static_cast<int>(wParam) < 0)
5825 wParam = pdoc->LineFromPosition(SelectionStart().Position());
5826 if (wParam == 0)
5827 return 0; // Even if there is no text, there is a first line that starts at 0
5828 if (static_cast<int>(wParam) > pdoc->LinesTotal())
5829 return -1;
5830 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
5831 // return -1;
5832 return pdoc->LineStart(static_cast<int>(wParam));
5834 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
5835 case SCI_LINELENGTH:
5836 if ((static_cast<int>(wParam) < 0) ||
5837 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
5838 return 0;
5839 return pdoc->LineStart(static_cast<int>(wParam) + 1) - pdoc->LineStart(static_cast<int>(wParam));
5841 case SCI_REPLACESEL: {
5842 if (lParam == 0)
5843 return 0;
5844 UndoGroup ug(pdoc);
5845 ClearSelection();
5846 char *replacement = CharPtrFromSPtr(lParam);
5847 const int lengthInserted = pdoc->InsertString(
5848 sel.MainCaret(), replacement, istrlen(replacement));
5849 SetEmptySelection(sel.MainCaret() + lengthInserted);
5850 EnsureCaretVisible();
5852 break;
5854 case SCI_SETTARGETSTART:
5855 targetStart = static_cast<int>(wParam);
5856 break;
5858 case SCI_GETTARGETSTART:
5859 return targetStart;
5861 case SCI_SETTARGETEND:
5862 targetEnd = static_cast<int>(wParam);
5863 break;
5865 case SCI_GETTARGETEND:
5866 return targetEnd;
5868 case SCI_SETTARGETRANGE:
5869 targetStart = static_cast<int>(wParam);
5870 targetEnd = static_cast<int>(lParam);
5871 break;
5873 case SCI_TARGETWHOLEDOCUMENT:
5874 targetStart = 0;
5875 targetEnd = pdoc->Length();
5876 break;
5878 case SCI_TARGETFROMSELECTION:
5879 if (sel.MainCaret() < sel.MainAnchor()) {
5880 targetStart = sel.MainCaret();
5881 targetEnd = sel.MainAnchor();
5882 } else {
5883 targetStart = sel.MainAnchor();
5884 targetEnd = sel.MainCaret();
5886 break;
5888 case SCI_GETTARGETTEXT: {
5889 std::string text = RangeText(targetStart, targetEnd);
5890 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(text.c_str()), text.length());
5893 case SCI_REPLACETARGET:
5894 PLATFORM_ASSERT(lParam);
5895 return ReplaceTarget(false, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5897 case SCI_REPLACETARGETRE:
5898 PLATFORM_ASSERT(lParam);
5899 return ReplaceTarget(true, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5901 case SCI_SEARCHINTARGET:
5902 PLATFORM_ASSERT(lParam);
5903 return SearchInTarget(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5905 case SCI_SETSEARCHFLAGS:
5906 searchFlags = static_cast<int>(wParam);
5907 break;
5909 case SCI_GETSEARCHFLAGS:
5910 return searchFlags;
5912 case SCI_GETTAG:
5913 return GetTag(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5915 case SCI_POSITIONBEFORE:
5916 return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) - 1, -1, true);
5918 case SCI_POSITIONAFTER:
5919 return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) + 1, 1, true);
5921 case SCI_POSITIONRELATIVE:
5922 return Platform::Clamp(pdoc->GetRelativePosition(static_cast<int>(wParam), static_cast<int>(lParam)), 0, pdoc->Length());
5924 case SCI_LINESCROLL:
5925 ScrollTo(topLine + static_cast<int>(lParam));
5926 HorizontalScrollTo(xOffset + static_cast<int>(wParam)* static_cast<int>(vs.spaceWidth));
5927 return 1;
5929 case SCI_SETXOFFSET:
5930 xOffset = static_cast<int>(wParam);
5931 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
5932 SetHorizontalScrollPos();
5933 Redraw();
5934 break;
5936 case SCI_GETXOFFSET:
5937 return xOffset;
5939 case SCI_CHOOSECARETX:
5940 SetLastXChosen();
5941 break;
5943 case SCI_SCROLLCARET:
5944 EnsureCaretVisible();
5945 break;
5947 case SCI_SETREADONLY:
5948 pdoc->SetReadOnly(wParam != 0);
5949 return 1;
5951 case SCI_GETREADONLY:
5952 return pdoc->IsReadOnly();
5954 case SCI_CANPASTE:
5955 return CanPaste();
5957 case SCI_POINTXFROMPOSITION:
5958 if (lParam < 0) {
5959 return 0;
5960 } else {
5961 Point pt = LocationFromPosition(static_cast<int>(lParam));
5962 // Convert to view-relative
5963 return static_cast<int>(pt.x) - vs.textStart + vs.fixedColumnWidth;
5966 case SCI_POINTYFROMPOSITION:
5967 if (lParam < 0) {
5968 return 0;
5969 } else {
5970 Point pt = LocationFromPosition(static_cast<int>(lParam));
5971 return static_cast<int>(pt.y);
5974 case SCI_FINDTEXT:
5975 return FindText(wParam, lParam);
5977 case SCI_GETTEXTRANGE: {
5978 if (lParam == 0)
5979 return 0;
5980 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
5981 int cpMax = static_cast<int>(tr->chrg.cpMax);
5982 if (cpMax == -1)
5983 cpMax = pdoc->Length();
5984 PLATFORM_ASSERT(cpMax <= pdoc->Length());
5985 int len = static_cast<int>(cpMax - tr->chrg.cpMin); // No -1 as cpMin and cpMax are referring to inter character positions
5986 pdoc->GetCharRange(tr->lpstrText, static_cast<int>(tr->chrg.cpMin), len);
5987 // Spec says copied text is terminated with a NUL
5988 tr->lpstrText[len] = '\0';
5989 return len; // Not including NUL
5992 case SCI_HIDESELECTION:
5993 view.hideSelection = wParam != 0;
5994 Redraw();
5995 break;
5997 case SCI_FORMATRANGE:
5998 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
6000 case SCI_GETMARGINLEFT:
6001 return vs.leftMarginWidth;
6003 case SCI_GETMARGINRIGHT:
6004 return vs.rightMarginWidth;
6006 case SCI_SETMARGINLEFT:
6007 lastXChosen += static_cast<int>(lParam) - vs.leftMarginWidth;
6008 vs.leftMarginWidth = static_cast<int>(lParam);
6009 InvalidateStyleRedraw();
6010 break;
6012 case SCI_SETMARGINRIGHT:
6013 vs.rightMarginWidth = static_cast<int>(lParam);
6014 InvalidateStyleRedraw();
6015 break;
6017 // Control specific mesages
6019 case SCI_ADDTEXT: {
6020 if (lParam == 0)
6021 return 0;
6022 const int lengthInserted = pdoc->InsertString(
6023 CurrentPosition(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
6024 SetEmptySelection(sel.MainCaret() + lengthInserted);
6025 return 0;
6028 case SCI_ADDSTYLEDTEXT:
6029 if (lParam)
6030 AddStyledText(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
6031 return 0;
6033 case SCI_INSERTTEXT: {
6034 if (lParam == 0)
6035 return 0;
6036 int insertPos = static_cast<int>(wParam);
6037 if (static_cast<int>(wParam) == -1)
6038 insertPos = CurrentPosition();
6039 int newCurrent = CurrentPosition();
6040 char *sz = CharPtrFromSPtr(lParam);
6041 const int lengthInserted = pdoc->InsertString(insertPos, sz, istrlen(sz));
6042 if (newCurrent > insertPos)
6043 newCurrent += lengthInserted;
6044 SetEmptySelection(newCurrent);
6045 return 0;
6048 case SCI_CHANGEINSERTION:
6049 PLATFORM_ASSERT(lParam);
6050 pdoc->ChangeInsertion(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
6051 return 0;
6053 case SCI_APPENDTEXT:
6054 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
6055 return 0;
6057 case SCI_CLEARALL:
6058 ClearAll();
6059 return 0;
6061 case SCI_DELETERANGE:
6062 pdoc->DeleteChars(static_cast<int>(wParam), static_cast<int>(lParam));
6063 return 0;
6065 case SCI_CLEARDOCUMENTSTYLE:
6066 ClearDocumentStyle();
6067 return 0;
6069 case SCI_SETUNDOCOLLECTION:
6070 pdoc->SetUndoCollection(wParam != 0);
6071 return 0;
6073 case SCI_GETUNDOCOLLECTION:
6074 return pdoc->IsCollectingUndo();
6076 case SCI_BEGINUNDOACTION:
6077 pdoc->BeginUndoAction();
6078 return 0;
6080 case SCI_ENDUNDOACTION:
6081 pdoc->EndUndoAction();
6082 return 0;
6084 case SCI_GETCARETPERIOD:
6085 return caret.period;
6087 case SCI_SETCARETPERIOD:
6088 CaretSetPeriod(static_cast<int>(wParam));
6089 break;
6091 case SCI_GETWORDCHARS:
6092 return pdoc->GetCharsOfClass(CharClassify::ccWord, reinterpret_cast<unsigned char *>(lParam));
6094 case SCI_SETWORDCHARS: {
6095 pdoc->SetDefaultCharClasses(false);
6096 if (lParam == 0)
6097 return 0;
6098 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
6100 break;
6102 case SCI_GETWHITESPACECHARS:
6103 return pdoc->GetCharsOfClass(CharClassify::ccSpace, reinterpret_cast<unsigned char *>(lParam));
6105 case SCI_SETWHITESPACECHARS: {
6106 if (lParam == 0)
6107 return 0;
6108 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
6110 break;
6112 case SCI_GETPUNCTUATIONCHARS:
6113 return pdoc->GetCharsOfClass(CharClassify::ccPunctuation, reinterpret_cast<unsigned char *>(lParam));
6115 case SCI_SETPUNCTUATIONCHARS: {
6116 if (lParam == 0)
6117 return 0;
6118 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccPunctuation);
6120 break;
6122 case SCI_SETCHARSDEFAULT:
6123 pdoc->SetDefaultCharClasses(true);
6124 break;
6126 case SCI_GETLENGTH:
6127 return pdoc->Length();
6129 case SCI_ALLOCATE:
6130 pdoc->Allocate(static_cast<int>(wParam));
6131 break;
6133 case SCI_GETCHARAT:
6134 return pdoc->CharAt(static_cast<int>(wParam));
6136 case SCI_SETCURRENTPOS:
6137 if (sel.IsRectangular()) {
6138 sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
6139 SetRectangularRange();
6140 Redraw();
6141 } else {
6142 SetSelection(static_cast<int>(wParam), sel.MainAnchor());
6144 break;
6146 case SCI_GETCURRENTPOS:
6147 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
6149 case SCI_SETANCHOR:
6150 if (sel.IsRectangular()) {
6151 sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
6152 SetRectangularRange();
6153 Redraw();
6154 } else {
6155 SetSelection(sel.MainCaret(), static_cast<int>(wParam));
6157 break;
6159 case SCI_GETANCHOR:
6160 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
6162 case SCI_SETSELECTIONSTART:
6163 SetSelection(Platform::Maximum(sel.MainCaret(), static_cast<int>(wParam)), static_cast<int>(wParam));
6164 break;
6166 case SCI_GETSELECTIONSTART:
6167 return sel.LimitsForRectangularElseMain().start.Position();
6169 case SCI_SETSELECTIONEND:
6170 SetSelection(static_cast<int>(wParam), Platform::Minimum(sel.MainAnchor(), static_cast<int>(wParam)));
6171 break;
6173 case SCI_GETSELECTIONEND:
6174 return sel.LimitsForRectangularElseMain().end.Position();
6176 case SCI_SETEMPTYSELECTION:
6177 SetEmptySelection(static_cast<int>(wParam));
6178 break;
6180 case SCI_SETPRINTMAGNIFICATION:
6181 view.printParameters.magnification = static_cast<int>(wParam);
6182 break;
6184 case SCI_GETPRINTMAGNIFICATION:
6185 return view.printParameters.magnification;
6187 case SCI_SETPRINTCOLOURMODE:
6188 view.printParameters.colourMode = static_cast<int>(wParam);
6189 break;
6191 case SCI_GETPRINTCOLOURMODE:
6192 return view.printParameters.colourMode;
6194 case SCI_SETPRINTWRAPMODE:
6195 view.printParameters.wrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
6196 break;
6198 case SCI_GETPRINTWRAPMODE:
6199 return view.printParameters.wrapState;
6201 case SCI_GETSTYLEAT:
6202 if (static_cast<int>(wParam) >= pdoc->Length())
6203 return 0;
6204 else
6205 return pdoc->StyleAt(static_cast<int>(wParam));
6207 case SCI_REDO:
6208 Redo();
6209 break;
6211 case SCI_SELECTALL:
6212 SelectAll();
6213 break;
6215 case SCI_SETSAVEPOINT:
6216 pdoc->SetSavePoint();
6217 break;
6219 case SCI_GETSTYLEDTEXT: {
6220 if (lParam == 0)
6221 return 0;
6222 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
6223 int iPlace = 0;
6224 for (long iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
6225 tr->lpstrText[iPlace++] = pdoc->CharAt(static_cast<int>(iChar));
6226 tr->lpstrText[iPlace++] = pdoc->StyleAt(static_cast<int>(iChar));
6228 tr->lpstrText[iPlace] = '\0';
6229 tr->lpstrText[iPlace + 1] = '\0';
6230 return iPlace;
6233 case SCI_CANREDO:
6234 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
6236 case SCI_MARKERLINEFROMHANDLE:
6237 return pdoc->LineFromHandle(static_cast<int>(wParam));
6239 case SCI_MARKERDELETEHANDLE:
6240 pdoc->DeleteMarkFromHandle(static_cast<int>(wParam));
6241 break;
6243 case SCI_GETVIEWWS:
6244 return vs.viewWhitespace;
6246 case SCI_SETVIEWWS:
6247 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
6248 Redraw();
6249 break;
6251 case SCI_GETWHITESPACESIZE:
6252 return vs.whitespaceSize;
6254 case SCI_SETWHITESPACESIZE:
6255 vs.whitespaceSize = static_cast<int>(wParam);
6256 Redraw();
6257 break;
6259 case SCI_POSITIONFROMPOINT:
6260 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6261 false, false);
6263 case SCI_POSITIONFROMPOINTCLOSE:
6264 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6265 true, false);
6267 case SCI_CHARPOSITIONFROMPOINT:
6268 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6269 false, true);
6271 case SCI_CHARPOSITIONFROMPOINTCLOSE:
6272 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6273 true, true);
6275 case SCI_GOTOLINE:
6276 GoToLine(static_cast<int>(wParam));
6277 break;
6279 case SCI_GOTOPOS:
6280 SetEmptySelection(static_cast<int>(wParam));
6281 EnsureCaretVisible();
6282 break;
6284 case SCI_GETCURLINE: {
6285 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
6286 int lineStart = pdoc->LineStart(lineCurrentPos);
6287 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
6288 if (lParam == 0) {
6289 return 1 + lineEnd - lineStart;
6291 PLATFORM_ASSERT(wParam > 0);
6292 char *ptr = CharPtrFromSPtr(lParam);
6293 unsigned int iPlace = 0;
6294 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
6295 ptr[iPlace++] = pdoc->CharAt(iChar);
6297 ptr[iPlace] = '\0';
6298 return sel.MainCaret() - lineStart;
6301 case SCI_GETENDSTYLED:
6302 return pdoc->GetEndStyled();
6304 case SCI_GETEOLMODE:
6305 return pdoc->eolMode;
6307 case SCI_SETEOLMODE:
6308 pdoc->eolMode = static_cast<int>(wParam);
6309 break;
6311 case SCI_SETLINEENDTYPESALLOWED:
6312 if (pdoc->SetLineEndTypesAllowed(static_cast<int>(wParam))) {
6313 cs.Clear();
6314 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6315 SetAnnotationHeights(0, pdoc->LinesTotal());
6316 InvalidateStyleRedraw();
6318 break;
6320 case SCI_GETLINEENDTYPESALLOWED:
6321 return pdoc->GetLineEndTypesAllowed();
6323 case SCI_GETLINEENDTYPESACTIVE:
6324 return pdoc->GetLineEndTypesActive();
6326 case SCI_STARTSTYLING:
6327 pdoc->StartStyling(static_cast<int>(wParam), static_cast<char>(lParam));
6328 break;
6330 case SCI_SETSTYLING:
6331 if (static_cast<int>(wParam) < 0)
6332 errorStatus = SC_STATUS_FAILURE;
6333 else
6334 pdoc->SetStyleFor(static_cast<int>(wParam), static_cast<char>(lParam));
6335 break;
6337 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
6338 if (lParam == 0)
6339 return 0;
6340 pdoc->SetStyles(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
6341 break;
6343 case SCI_SETBUFFEREDDRAW:
6344 view.bufferedDraw = wParam != 0;
6345 break;
6347 case SCI_GETBUFFEREDDRAW:
6348 return view.bufferedDraw;
6350 case SCI_GETTWOPHASEDRAW:
6351 return view.phasesDraw == EditView::phasesTwo;
6353 case SCI_SETTWOPHASEDRAW:
6354 if (view.SetTwoPhaseDraw(wParam != 0))
6355 InvalidateStyleRedraw();
6356 break;
6358 case SCI_GETPHASESDRAW:
6359 return view.phasesDraw;
6361 case SCI_SETPHASESDRAW:
6362 if (view.SetPhasesDraw(static_cast<int>(wParam)))
6363 InvalidateStyleRedraw();
6364 break;
6366 case SCI_SETFONTQUALITY:
6367 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
6368 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
6369 InvalidateStyleRedraw();
6370 break;
6372 case SCI_GETFONTQUALITY:
6373 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
6375 case SCI_SETTABWIDTH:
6376 if (wParam > 0) {
6377 pdoc->tabInChars = static_cast<int>(wParam);
6378 if (pdoc->indentInChars == 0)
6379 pdoc->actualIndentInChars = pdoc->tabInChars;
6381 InvalidateStyleRedraw();
6382 break;
6384 case SCI_GETTABWIDTH:
6385 return pdoc->tabInChars;
6387 case SCI_CLEARTABSTOPS:
6388 if (view.ClearTabstops(static_cast<int>(wParam))) {
6389 DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, 0, static_cast<int>(wParam));
6390 NotifyModified(pdoc, mh, NULL);
6392 break;
6394 case SCI_ADDTABSTOP:
6395 if (view.AddTabstop(static_cast<int>(wParam), static_cast<int>(lParam))) {
6396 DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, 0, static_cast<int>(wParam));
6397 NotifyModified(pdoc, mh, NULL);
6399 break;
6401 case SCI_GETNEXTTABSTOP:
6402 return view.GetNextTabstop(static_cast<int>(wParam), static_cast<int>(lParam));
6404 case SCI_SETINDENT:
6405 pdoc->indentInChars = static_cast<int>(wParam);
6406 if (pdoc->indentInChars != 0)
6407 pdoc->actualIndentInChars = pdoc->indentInChars;
6408 else
6409 pdoc->actualIndentInChars = pdoc->tabInChars;
6410 InvalidateStyleRedraw();
6411 break;
6413 case SCI_GETINDENT:
6414 return pdoc->indentInChars;
6416 case SCI_SETUSETABS:
6417 pdoc->useTabs = wParam != 0;
6418 InvalidateStyleRedraw();
6419 break;
6421 case SCI_GETUSETABS:
6422 return pdoc->useTabs;
6424 case SCI_SETLINEINDENTATION:
6425 pdoc->SetLineIndentation(static_cast<int>(wParam), static_cast<int>(lParam));
6426 break;
6428 case SCI_GETLINEINDENTATION:
6429 return pdoc->GetLineIndentation(static_cast<int>(wParam));
6431 case SCI_GETLINEINDENTPOSITION:
6432 return pdoc->GetLineIndentPosition(static_cast<int>(wParam));
6434 case SCI_SETTABINDENTS:
6435 pdoc->tabIndents = wParam != 0;
6436 break;
6438 case SCI_GETTABINDENTS:
6439 return pdoc->tabIndents;
6441 case SCI_SETBACKSPACEUNINDENTS:
6442 pdoc->backspaceUnindents = wParam != 0;
6443 break;
6445 case SCI_GETBACKSPACEUNINDENTS:
6446 return pdoc->backspaceUnindents;
6448 case SCI_SETMOUSEDWELLTIME:
6449 dwellDelay = static_cast<int>(wParam);
6450 ticksToDwell = dwellDelay;
6451 break;
6453 case SCI_GETMOUSEDWELLTIME:
6454 return dwellDelay;
6456 case SCI_WORDSTARTPOSITION:
6457 return pdoc->ExtendWordSelect(static_cast<int>(wParam), -1, lParam != 0);
6459 case SCI_WORDENDPOSITION:
6460 return pdoc->ExtendWordSelect(static_cast<int>(wParam), 1, lParam != 0);
6462 case SCI_ISRANGEWORD:
6463 return pdoc->IsWordAt(static_cast<int>(wParam), static_cast<int>(lParam));
6465 case SCI_SETIDLESTYLING:
6466 idleStyling = static_cast<int>(wParam);
6467 break;
6469 case SCI_GETIDLESTYLING:
6470 return idleStyling;
6472 case SCI_SETWRAPMODE:
6473 if (vs.SetWrapState(static_cast<int>(wParam))) {
6474 xOffset = 0;
6475 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
6476 InvalidateStyleRedraw();
6477 ReconfigureScrollBars();
6479 break;
6481 case SCI_GETWRAPMODE:
6482 return vs.wrapState;
6484 case SCI_SETWRAPVISUALFLAGS:
6485 if (vs.SetWrapVisualFlags(static_cast<int>(wParam))) {
6486 InvalidateStyleRedraw();
6487 ReconfigureScrollBars();
6489 break;
6491 case SCI_GETWRAPVISUALFLAGS:
6492 return vs.wrapVisualFlags;
6494 case SCI_SETWRAPVISUALFLAGSLOCATION:
6495 if (vs.SetWrapVisualFlagsLocation(static_cast<int>(wParam))) {
6496 InvalidateStyleRedraw();
6498 break;
6500 case SCI_GETWRAPVISUALFLAGSLOCATION:
6501 return vs.wrapVisualFlagsLocation;
6503 case SCI_SETWRAPSTARTINDENT:
6504 if (vs.SetWrapVisualStartIndent(static_cast<int>(wParam))) {
6505 InvalidateStyleRedraw();
6506 ReconfigureScrollBars();
6508 break;
6510 case SCI_GETWRAPSTARTINDENT:
6511 return vs.wrapVisualStartIndent;
6513 case SCI_SETWRAPINDENTMODE:
6514 if (vs.SetWrapIndentMode(static_cast<int>(wParam))) {
6515 InvalidateStyleRedraw();
6516 ReconfigureScrollBars();
6518 break;
6520 case SCI_GETWRAPINDENTMODE:
6521 return vs.wrapIndentMode;
6523 case SCI_SETLAYOUTCACHE:
6524 view.llc.SetLevel(static_cast<int>(wParam));
6525 break;
6527 case SCI_GETLAYOUTCACHE:
6528 return view.llc.GetLevel();
6530 case SCI_SETPOSITIONCACHE:
6531 view.posCache.SetSize(wParam);
6532 break;
6534 case SCI_GETPOSITIONCACHE:
6535 return view.posCache.GetSize();
6537 case SCI_SETSCROLLWIDTH:
6538 PLATFORM_ASSERT(wParam > 0);
6539 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
6540 view.lineWidthMaxSeen = 0;
6541 scrollWidth = static_cast<int>(wParam);
6542 SetScrollBars();
6544 break;
6546 case SCI_GETSCROLLWIDTH:
6547 return scrollWidth;
6549 case SCI_SETSCROLLWIDTHTRACKING:
6550 trackLineWidth = wParam != 0;
6551 break;
6553 case SCI_GETSCROLLWIDTHTRACKING:
6554 return trackLineWidth;
6556 case SCI_LINESJOIN:
6557 LinesJoin();
6558 break;
6560 case SCI_LINESSPLIT:
6561 LinesSplit(static_cast<int>(wParam));
6562 break;
6564 case SCI_TEXTWIDTH:
6565 PLATFORM_ASSERT(wParam < vs.styles.size());
6566 PLATFORM_ASSERT(lParam);
6567 return TextWidth(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
6569 case SCI_TEXTHEIGHT:
6570 RefreshStyleData();
6571 return vs.lineHeight;
6573 case SCI_SETENDATLASTLINE:
6574 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
6575 if (endAtLastLine != (wParam != 0)) {
6576 endAtLastLine = wParam != 0;
6577 SetScrollBars();
6579 break;
6581 case SCI_GETENDATLASTLINE:
6582 return endAtLastLine;
6584 case SCI_SETCARETSTICKY:
6585 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
6586 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
6587 caretSticky = static_cast<int>(wParam);
6589 break;
6591 case SCI_GETCARETSTICKY:
6592 return caretSticky;
6594 case SCI_TOGGLECARETSTICKY:
6595 caretSticky = !caretSticky;
6596 break;
6598 case SCI_GETCOLUMN:
6599 return pdoc->GetColumn(static_cast<int>(wParam));
6601 case SCI_FINDCOLUMN:
6602 return pdoc->FindColumn(static_cast<int>(wParam), static_cast<int>(lParam));
6604 case SCI_SETHSCROLLBAR :
6605 if (horizontalScrollBarVisible != (wParam != 0)) {
6606 horizontalScrollBarVisible = wParam != 0;
6607 SetScrollBars();
6608 ReconfigureScrollBars();
6610 break;
6612 case SCI_GETHSCROLLBAR:
6613 return horizontalScrollBarVisible;
6615 case SCI_SETVSCROLLBAR:
6616 if (verticalScrollBarVisible != (wParam != 0)) {
6617 verticalScrollBarVisible = wParam != 0;
6618 SetScrollBars();
6619 ReconfigureScrollBars();
6620 if (verticalScrollBarVisible)
6621 SetVerticalScrollPos();
6623 break;
6625 case SCI_GETVSCROLLBAR:
6626 return verticalScrollBarVisible;
6628 case SCI_SETINDENTATIONGUIDES:
6629 vs.viewIndentationGuides = IndentView(wParam);
6630 Redraw();
6631 break;
6633 case SCI_GETINDENTATIONGUIDES:
6634 return vs.viewIndentationGuides;
6636 case SCI_SETHIGHLIGHTGUIDE:
6637 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
6638 highlightGuideColumn = static_cast<int>(wParam);
6639 Redraw();
6641 break;
6643 case SCI_GETHIGHLIGHTGUIDE:
6644 return highlightGuideColumn;
6646 case SCI_GETLINEENDPOSITION:
6647 return pdoc->LineEnd(static_cast<int>(wParam));
6649 case SCI_SETCODEPAGE:
6650 if (ValidCodePage(static_cast<int>(wParam))) {
6651 if (pdoc->SetDBCSCodePage(static_cast<int>(wParam))) {
6652 cs.Clear();
6653 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6654 SetAnnotationHeights(0, pdoc->LinesTotal());
6655 InvalidateStyleRedraw();
6656 SetRepresentations();
6659 break;
6661 case SCI_GETCODEPAGE:
6662 return pdoc->dbcsCodePage;
6664 case SCI_SETIMEINTERACTION:
6665 imeInteraction = static_cast<EditModel::IMEInteraction>(wParam);
6666 break;
6668 case SCI_GETIMEINTERACTION:
6669 return imeInteraction;
6671 #ifdef INCLUDE_DEPRECATED_FEATURES
6672 case SCI_SETUSEPALETTE:
6673 InvalidateStyleRedraw();
6674 break;
6676 case SCI_GETUSEPALETTE:
6677 return 0;
6678 #endif
6680 // Marker definition and setting
6681 case SCI_MARKERDEFINE:
6682 if (wParam <= MARKER_MAX) {
6683 vs.markers[wParam].markType = static_cast<int>(lParam);
6684 vs.CalcLargestMarkerHeight();
6686 InvalidateStyleData();
6687 RedrawSelMargin();
6688 break;
6690 case SCI_MARKERSYMBOLDEFINED:
6691 if (wParam <= MARKER_MAX)
6692 return vs.markers[wParam].markType;
6693 else
6694 return 0;
6696 case SCI_MARKERSETFORE:
6697 if (wParam <= MARKER_MAX)
6698 vs.markers[wParam].fore = ColourDesired(static_cast<long>(lParam));
6699 InvalidateStyleData();
6700 RedrawSelMargin();
6701 break;
6702 case SCI_MARKERSETBACKSELECTED:
6703 if (wParam <= MARKER_MAX)
6704 vs.markers[wParam].backSelected = ColourDesired(static_cast<long>(lParam));
6705 InvalidateStyleData();
6706 RedrawSelMargin();
6707 break;
6708 case SCI_MARKERENABLEHIGHLIGHT:
6709 marginView.highlightDelimiter.isEnabled = wParam == 1;
6710 RedrawSelMargin();
6711 break;
6712 case SCI_MARKERSETBACK:
6713 if (wParam <= MARKER_MAX)
6714 vs.markers[wParam].back = ColourDesired(static_cast<long>(lParam));
6715 InvalidateStyleData();
6716 RedrawSelMargin();
6717 break;
6718 case SCI_MARKERSETALPHA:
6719 if (wParam <= MARKER_MAX)
6720 vs.markers[wParam].alpha = static_cast<int>(lParam);
6721 InvalidateStyleRedraw();
6722 break;
6723 case SCI_MARKERADD: {
6724 int markerID = pdoc->AddMark(static_cast<int>(wParam), static_cast<int>(lParam));
6725 return markerID;
6727 case SCI_MARKERADDSET:
6728 if (lParam != 0)
6729 pdoc->AddMarkSet(static_cast<int>(wParam), static_cast<int>(lParam));
6730 break;
6732 case SCI_MARKERDELETE:
6733 pdoc->DeleteMark(static_cast<int>(wParam), static_cast<int>(lParam));
6734 break;
6736 case SCI_MARKERDELETEALL:
6737 pdoc->DeleteAllMarks(static_cast<int>(wParam));
6738 break;
6740 case SCI_MARKERGET:
6741 return pdoc->GetMark(static_cast<int>(wParam));
6743 case SCI_MARKERNEXT:
6744 return pdoc->MarkerNext(static_cast<int>(wParam), static_cast<int>(lParam));
6746 case SCI_MARKERPREVIOUS: {
6747 for (int iLine = static_cast<int>(wParam); iLine >= 0; iLine--) {
6748 if ((pdoc->GetMark(iLine) & lParam) != 0)
6749 return iLine;
6752 return -1;
6754 case SCI_MARKERDEFINEPIXMAP:
6755 if (wParam <= MARKER_MAX) {
6756 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
6757 vs.CalcLargestMarkerHeight();
6759 InvalidateStyleData();
6760 RedrawSelMargin();
6761 break;
6763 case SCI_RGBAIMAGESETWIDTH:
6764 sizeRGBAImage.x = static_cast<XYPOSITION>(wParam);
6765 break;
6767 case SCI_RGBAIMAGESETHEIGHT:
6768 sizeRGBAImage.y = static_cast<XYPOSITION>(wParam);
6769 break;
6771 case SCI_RGBAIMAGESETSCALE:
6772 scaleRGBAImage = static_cast<float>(wParam);
6773 break;
6775 case SCI_MARKERDEFINERGBAIMAGE:
6776 if (wParam <= MARKER_MAX) {
6777 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0f, reinterpret_cast<unsigned char *>(lParam));
6778 vs.CalcLargestMarkerHeight();
6780 InvalidateStyleData();
6781 RedrawSelMargin();
6782 break;
6784 case SCI_SETMARGINTYPEN:
6785 if (ValidMargin(wParam)) {
6786 vs.ms[wParam].style = static_cast<int>(lParam);
6787 InvalidateStyleRedraw();
6789 break;
6791 case SCI_GETMARGINTYPEN:
6792 if (ValidMargin(wParam))
6793 return vs.ms[wParam].style;
6794 else
6795 return 0;
6797 case SCI_SETMARGINWIDTHN:
6798 if (ValidMargin(wParam)) {
6799 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
6800 if (vs.ms[wParam].width != lParam) {
6801 lastXChosen += static_cast<int>(lParam) - vs.ms[wParam].width;
6802 vs.ms[wParam].width = static_cast<int>(lParam);
6803 InvalidateStyleRedraw();
6806 break;
6808 case SCI_GETMARGINWIDTHN:
6809 if (ValidMargin(wParam))
6810 return vs.ms[wParam].width;
6811 else
6812 return 0;
6814 case SCI_SETMARGINMASKN:
6815 if (ValidMargin(wParam)) {
6816 vs.ms[wParam].mask = static_cast<int>(lParam);
6817 InvalidateStyleRedraw();
6819 break;
6821 case SCI_GETMARGINMASKN:
6822 if (ValidMargin(wParam))
6823 return vs.ms[wParam].mask;
6824 else
6825 return 0;
6827 case SCI_SETMARGINSENSITIVEN:
6828 if (ValidMargin(wParam)) {
6829 vs.ms[wParam].sensitive = lParam != 0;
6830 InvalidateStyleRedraw();
6832 break;
6834 case SCI_GETMARGINSENSITIVEN:
6835 if (ValidMargin(wParam))
6836 return vs.ms[wParam].sensitive ? 1 : 0;
6837 else
6838 return 0;
6840 case SCI_SETMARGINCURSORN:
6841 if (ValidMargin(wParam))
6842 vs.ms[wParam].cursor = static_cast<int>(lParam);
6843 break;
6845 case SCI_GETMARGINCURSORN:
6846 if (ValidMargin(wParam))
6847 return vs.ms[wParam].cursor;
6848 else
6849 return 0;
6851 case SCI_STYLECLEARALL:
6852 vs.ClearStyles();
6853 InvalidateStyleRedraw();
6854 break;
6856 case SCI_STYLESETFORE:
6857 case SCI_STYLESETBACK:
6858 case SCI_STYLESETBOLD:
6859 case SCI_STYLESETWEIGHT:
6860 case SCI_STYLESETITALIC:
6861 case SCI_STYLESETEOLFILLED:
6862 case SCI_STYLESETSIZE:
6863 case SCI_STYLESETSIZEFRACTIONAL:
6864 case SCI_STYLESETFONT:
6865 case SCI_STYLESETUNDERLINE:
6866 case SCI_STYLESETCASE:
6867 case SCI_STYLESETCHARACTERSET:
6868 case SCI_STYLESETVISIBLE:
6869 case SCI_STYLESETCHANGEABLE:
6870 case SCI_STYLESETHOTSPOT:
6871 StyleSetMessage(iMessage, wParam, lParam);
6872 break;
6874 case SCI_STYLEGETFORE:
6875 case SCI_STYLEGETBACK:
6876 case SCI_STYLEGETBOLD:
6877 case SCI_STYLEGETWEIGHT:
6878 case SCI_STYLEGETITALIC:
6879 case SCI_STYLEGETEOLFILLED:
6880 case SCI_STYLEGETSIZE:
6881 case SCI_STYLEGETSIZEFRACTIONAL:
6882 case SCI_STYLEGETFONT:
6883 case SCI_STYLEGETUNDERLINE:
6884 case SCI_STYLEGETCASE:
6885 case SCI_STYLEGETCHARACTERSET:
6886 case SCI_STYLEGETVISIBLE:
6887 case SCI_STYLEGETCHANGEABLE:
6888 case SCI_STYLEGETHOTSPOT:
6889 return StyleGetMessage(iMessage, wParam, lParam);
6891 case SCI_STYLERESETDEFAULT:
6892 vs.ResetDefaultStyle();
6893 InvalidateStyleRedraw();
6894 break;
6895 case SCI_SETSTYLEBITS:
6896 vs.EnsureStyle(0xff);
6897 break;
6899 case SCI_GETSTYLEBITS:
6900 return 8;
6902 case SCI_SETLINESTATE:
6903 return pdoc->SetLineState(static_cast<int>(wParam), static_cast<int>(lParam));
6905 case SCI_GETLINESTATE:
6906 return pdoc->GetLineState(static_cast<int>(wParam));
6908 case SCI_GETMAXLINESTATE:
6909 return pdoc->GetMaxLineState();
6911 case SCI_GETCARETLINEVISIBLE:
6912 return vs.showCaretLineBackground;
6913 case SCI_SETCARETLINEVISIBLE:
6914 vs.showCaretLineBackground = wParam != 0;
6915 InvalidateStyleRedraw();
6916 break;
6917 case SCI_GETCARETLINEVISIBLEALWAYS:
6918 return vs.alwaysShowCaretLineBackground;
6919 case SCI_SETCARETLINEVISIBLEALWAYS:
6920 vs.alwaysShowCaretLineBackground = wParam != 0;
6921 InvalidateStyleRedraw();
6922 break;
6924 case SCI_GETCARETLINEBACK:
6925 return vs.caretLineBackground.AsLong();
6926 case SCI_SETCARETLINEBACK:
6927 vs.caretLineBackground = static_cast<int>(wParam);
6928 InvalidateStyleRedraw();
6929 break;
6930 case SCI_GETCARETLINEBACKALPHA:
6931 return vs.caretLineAlpha;
6932 case SCI_SETCARETLINEBACKALPHA:
6933 vs.caretLineAlpha = static_cast<int>(wParam);
6934 InvalidateStyleRedraw();
6935 break;
6937 // Folding messages
6939 case SCI_VISIBLEFROMDOCLINE:
6940 return cs.DisplayFromDoc(static_cast<int>(wParam));
6942 case SCI_DOCLINEFROMVISIBLE:
6943 return cs.DocFromDisplay(static_cast<int>(wParam));
6945 case SCI_WRAPCOUNT:
6946 return WrapCount(static_cast<int>(wParam));
6948 case SCI_SETFOLDLEVEL: {
6949 int prev = pdoc->SetLevel(static_cast<int>(wParam), static_cast<int>(lParam));
6950 if (prev != static_cast<int>(lParam))
6951 RedrawSelMargin();
6952 return prev;
6955 case SCI_GETFOLDLEVEL:
6956 return pdoc->GetLevel(static_cast<int>(wParam));
6958 case SCI_GETLASTCHILD:
6959 return pdoc->GetLastChild(static_cast<int>(wParam), static_cast<int>(lParam));
6961 case SCI_GETFOLDPARENT:
6962 return pdoc->GetFoldParent(static_cast<int>(wParam));
6964 case SCI_SHOWLINES:
6965 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), true);
6966 SetScrollBars();
6967 Redraw();
6968 break;
6970 case SCI_HIDELINES:
6971 if (wParam > 0)
6972 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), false);
6973 SetScrollBars();
6974 Redraw();
6975 break;
6977 case SCI_GETLINEVISIBLE:
6978 return cs.GetVisible(static_cast<int>(wParam));
6980 case SCI_GETALLLINESVISIBLE:
6981 return cs.HiddenLines() ? 0 : 1;
6983 case SCI_SETFOLDEXPANDED:
6984 SetFoldExpanded(static_cast<int>(wParam), lParam != 0);
6985 break;
6987 case SCI_GETFOLDEXPANDED:
6988 return cs.GetExpanded(static_cast<int>(wParam));
6990 case SCI_SETAUTOMATICFOLD:
6991 foldAutomatic = static_cast<int>(wParam);
6992 break;
6994 case SCI_GETAUTOMATICFOLD:
6995 return foldAutomatic;
6997 case SCI_SETFOLDFLAGS:
6998 foldFlags = static_cast<int>(wParam);
6999 Redraw();
7000 break;
7002 case SCI_TOGGLEFOLD:
7003 FoldLine(static_cast<int>(wParam), SC_FOLDACTION_TOGGLE);
7004 break;
7006 case SCI_FOLDLINE:
7007 FoldLine(static_cast<int>(wParam), static_cast<int>(lParam));
7008 break;
7010 case SCI_FOLDCHILDREN:
7011 FoldExpand(static_cast<int>(wParam), static_cast<int>(lParam), pdoc->GetLevel(static_cast<int>(wParam)));
7012 break;
7014 case SCI_FOLDALL:
7015 FoldAll(static_cast<int>(wParam));
7016 break;
7018 case SCI_EXPANDCHILDREN:
7019 FoldExpand(static_cast<int>(wParam), SC_FOLDACTION_EXPAND, static_cast<int>(lParam));
7020 break;
7022 case SCI_CONTRACTEDFOLDNEXT:
7023 return ContractedFoldNext(static_cast<int>(wParam));
7025 case SCI_ENSUREVISIBLE:
7026 EnsureLineVisible(static_cast<int>(wParam), false);
7027 break;
7029 case SCI_ENSUREVISIBLEENFORCEPOLICY:
7030 EnsureLineVisible(static_cast<int>(wParam), true);
7031 break;
7033 case SCI_SCROLLRANGE:
7034 ScrollRange(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
7035 break;
7037 case SCI_SEARCHANCHOR:
7038 SearchAnchor();
7039 break;
7041 case SCI_SEARCHNEXT:
7042 case SCI_SEARCHPREV:
7043 return SearchText(iMessage, wParam, lParam);
7045 case SCI_SETXCARETPOLICY:
7046 caretXPolicy = static_cast<int>(wParam);
7047 caretXSlop = static_cast<int>(lParam);
7048 break;
7050 case SCI_SETYCARETPOLICY:
7051 caretYPolicy = static_cast<int>(wParam);
7052 caretYSlop = static_cast<int>(lParam);
7053 break;
7055 case SCI_SETVISIBLEPOLICY:
7056 visiblePolicy = static_cast<int>(wParam);
7057 visibleSlop = static_cast<int>(lParam);
7058 break;
7060 case SCI_LINESONSCREEN:
7061 return LinesOnScreen();
7063 case SCI_SETSELFORE:
7064 vs.selColours.fore = ColourOptional(wParam, lParam);
7065 vs.selAdditionalForeground = ColourDesired(static_cast<long>(lParam));
7066 InvalidateStyleRedraw();
7067 break;
7069 case SCI_SETSELBACK:
7070 vs.selColours.back = ColourOptional(wParam, lParam);
7071 vs.selAdditionalBackground = ColourDesired(static_cast<long>(lParam));
7072 InvalidateStyleRedraw();
7073 break;
7075 case SCI_SETSELALPHA:
7076 vs.selAlpha = static_cast<int>(wParam);
7077 vs.selAdditionalAlpha = static_cast<int>(wParam);
7078 InvalidateStyleRedraw();
7079 break;
7081 case SCI_GETSELALPHA:
7082 return vs.selAlpha;
7084 case SCI_GETSELEOLFILLED:
7085 return vs.selEOLFilled;
7087 case SCI_SETSELEOLFILLED:
7088 vs.selEOLFilled = wParam != 0;
7089 InvalidateStyleRedraw();
7090 break;
7092 case SCI_SETWHITESPACEFORE:
7093 vs.whitespaceColours.fore = ColourOptional(wParam, lParam);
7094 InvalidateStyleRedraw();
7095 break;
7097 case SCI_SETWHITESPACEBACK:
7098 vs.whitespaceColours.back = ColourOptional(wParam, lParam);
7099 InvalidateStyleRedraw();
7100 break;
7102 case SCI_SETCARETFORE:
7103 vs.caretcolour = ColourDesired(static_cast<long>(wParam));
7104 InvalidateStyleRedraw();
7105 break;
7107 case SCI_GETCARETFORE:
7108 return vs.caretcolour.AsLong();
7110 case SCI_SETCARETSTYLE:
7111 if (wParam <= CARETSTYLE_BLOCK)
7112 vs.caretStyle = static_cast<int>(wParam);
7113 else
7114 /* Default to the line caret */
7115 vs.caretStyle = CARETSTYLE_LINE;
7116 InvalidateStyleRedraw();
7117 break;
7119 case SCI_GETCARETSTYLE:
7120 return vs.caretStyle;
7122 case SCI_SETCARETWIDTH:
7123 if (static_cast<int>(wParam) <= 0)
7124 vs.caretWidth = 0;
7125 else if (wParam >= 3)
7126 vs.caretWidth = 3;
7127 else
7128 vs.caretWidth = static_cast<int>(wParam);
7129 InvalidateStyleRedraw();
7130 break;
7132 case SCI_GETCARETWIDTH:
7133 return vs.caretWidth;
7135 case SCI_ASSIGNCMDKEY:
7136 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
7137 Platform::HighShortFromLong(static_cast<long>(wParam)), static_cast<unsigned int>(lParam));
7138 break;
7140 case SCI_CLEARCMDKEY:
7141 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
7142 Platform::HighShortFromLong(static_cast<long>(wParam)), SCI_NULL);
7143 break;
7145 case SCI_CLEARALLCMDKEYS:
7146 kmap.Clear();
7147 break;
7149 case SCI_INDICSETSTYLE:
7150 if (wParam <= INDIC_MAX) {
7151 vs.indicators[wParam].sacNormal.style = static_cast<int>(lParam);
7152 vs.indicators[wParam].sacHover.style = static_cast<int>(lParam);
7153 InvalidateStyleRedraw();
7155 break;
7157 case SCI_INDICGETSTYLE:
7158 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacNormal.style : 0;
7160 case SCI_INDICSETFORE:
7161 if (wParam <= INDIC_MAX) {
7162 vs.indicators[wParam].sacNormal.fore = ColourDesired(static_cast<long>(lParam));
7163 vs.indicators[wParam].sacHover.fore = ColourDesired(static_cast<long>(lParam));
7164 InvalidateStyleRedraw();
7166 break;
7168 case SCI_INDICGETFORE:
7169 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacNormal.fore.AsLong() : 0;
7171 case SCI_INDICSETHOVERSTYLE:
7172 if (wParam <= INDIC_MAX) {
7173 vs.indicators[wParam].sacHover.style = static_cast<int>(lParam);
7174 InvalidateStyleRedraw();
7176 break;
7178 case SCI_INDICGETHOVERSTYLE:
7179 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacHover.style : 0;
7181 case SCI_INDICSETHOVERFORE:
7182 if (wParam <= INDIC_MAX) {
7183 vs.indicators[wParam].sacHover.fore = ColourDesired(static_cast<long>(lParam));
7184 InvalidateStyleRedraw();
7186 break;
7188 case SCI_INDICGETHOVERFORE:
7189 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacHover.fore.AsLong() : 0;
7191 case SCI_INDICSETFLAGS:
7192 if (wParam <= INDIC_MAX) {
7193 vs.indicators[wParam].SetFlags(static_cast<int>(lParam));
7194 InvalidateStyleRedraw();
7196 break;
7198 case SCI_INDICGETFLAGS:
7199 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].Flags() : 0;
7201 case SCI_INDICSETUNDER:
7202 if (wParam <= INDIC_MAX) {
7203 vs.indicators[wParam].under = lParam != 0;
7204 InvalidateStyleRedraw();
7206 break;
7208 case SCI_INDICGETUNDER:
7209 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
7211 case SCI_INDICSETALPHA:
7212 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
7213 vs.indicators[wParam].fillAlpha = static_cast<int>(lParam);
7214 InvalidateStyleRedraw();
7216 break;
7218 case SCI_INDICGETALPHA:
7219 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
7221 case SCI_INDICSETOUTLINEALPHA:
7222 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
7223 vs.indicators[wParam].outlineAlpha = static_cast<int>(lParam);
7224 InvalidateStyleRedraw();
7226 break;
7228 case SCI_INDICGETOUTLINEALPHA:
7229 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
7231 case SCI_SETINDICATORCURRENT:
7232 pdoc->decorations.SetCurrentIndicator(static_cast<int>(wParam));
7233 break;
7234 case SCI_GETINDICATORCURRENT:
7235 return pdoc->decorations.GetCurrentIndicator();
7236 case SCI_SETINDICATORVALUE:
7237 pdoc->decorations.SetCurrentValue(static_cast<int>(wParam));
7238 break;
7239 case SCI_GETINDICATORVALUE:
7240 return pdoc->decorations.GetCurrentValue();
7242 case SCI_INDICATORFILLRANGE:
7243 pdoc->DecorationFillRange(static_cast<int>(wParam), pdoc->decorations.GetCurrentValue(), static_cast<int>(lParam));
7244 break;
7246 case SCI_INDICATORCLEARRANGE:
7247 pdoc->DecorationFillRange(static_cast<int>(wParam), 0, static_cast<int>(lParam));
7248 break;
7250 case SCI_INDICATORALLONFOR:
7251 return pdoc->decorations.AllOnFor(static_cast<int>(wParam));
7253 case SCI_INDICATORVALUEAT:
7254 return pdoc->decorations.ValueAt(static_cast<int>(wParam), static_cast<int>(lParam));
7256 case SCI_INDICATORSTART:
7257 return pdoc->decorations.Start(static_cast<int>(wParam), static_cast<int>(lParam));
7259 case SCI_INDICATOREND:
7260 return pdoc->decorations.End(static_cast<int>(wParam), static_cast<int>(lParam));
7262 case SCI_LINEDOWN:
7263 case SCI_LINEDOWNEXTEND:
7264 case SCI_PARADOWN:
7265 case SCI_PARADOWNEXTEND:
7266 case SCI_LINEUP:
7267 case SCI_LINEUPEXTEND:
7268 case SCI_PARAUP:
7269 case SCI_PARAUPEXTEND:
7270 case SCI_CHARLEFT:
7271 case SCI_CHARLEFTEXTEND:
7272 case SCI_CHARRIGHT:
7273 case SCI_CHARRIGHTEXTEND:
7274 case SCI_WORDLEFT:
7275 case SCI_WORDLEFTEXTEND:
7276 case SCI_WORDRIGHT:
7277 case SCI_WORDRIGHTEXTEND:
7278 case SCI_WORDLEFTEND:
7279 case SCI_WORDLEFTENDEXTEND:
7280 case SCI_WORDRIGHTEND:
7281 case SCI_WORDRIGHTENDEXTEND:
7282 case SCI_HOME:
7283 case SCI_HOMEEXTEND:
7284 case SCI_LINEEND:
7285 case SCI_LINEENDEXTEND:
7286 case SCI_HOMEWRAP:
7287 case SCI_HOMEWRAPEXTEND:
7288 case SCI_LINEENDWRAP:
7289 case SCI_LINEENDWRAPEXTEND:
7290 case SCI_DOCUMENTSTART:
7291 case SCI_DOCUMENTSTARTEXTEND:
7292 case SCI_DOCUMENTEND:
7293 case SCI_DOCUMENTENDEXTEND:
7294 case SCI_SCROLLTOSTART:
7295 case SCI_SCROLLTOEND:
7297 case SCI_STUTTEREDPAGEUP:
7298 case SCI_STUTTEREDPAGEUPEXTEND:
7299 case SCI_STUTTEREDPAGEDOWN:
7300 case SCI_STUTTEREDPAGEDOWNEXTEND:
7302 case SCI_PAGEUP:
7303 case SCI_PAGEUPEXTEND:
7304 case SCI_PAGEDOWN:
7305 case SCI_PAGEDOWNEXTEND:
7306 case SCI_EDITTOGGLEOVERTYPE:
7307 case SCI_CANCEL:
7308 case SCI_DELETEBACK:
7309 case SCI_TAB:
7310 case SCI_BACKTAB:
7311 case SCI_NEWLINE:
7312 case SCI_FORMFEED:
7313 case SCI_VCHOME:
7314 case SCI_VCHOMEEXTEND:
7315 case SCI_VCHOMEWRAP:
7316 case SCI_VCHOMEWRAPEXTEND:
7317 case SCI_VCHOMEDISPLAY:
7318 case SCI_VCHOMEDISPLAYEXTEND:
7319 case SCI_ZOOMIN:
7320 case SCI_ZOOMOUT:
7321 case SCI_DELWORDLEFT:
7322 case SCI_DELWORDRIGHT:
7323 case SCI_DELWORDRIGHTEND:
7324 case SCI_DELLINELEFT:
7325 case SCI_DELLINERIGHT:
7326 case SCI_LINECOPY:
7327 case SCI_LINECUT:
7328 case SCI_LINEDELETE:
7329 case SCI_LINETRANSPOSE:
7330 case SCI_LINEDUPLICATE:
7331 case SCI_LOWERCASE:
7332 case SCI_UPPERCASE:
7333 case SCI_LINESCROLLDOWN:
7334 case SCI_LINESCROLLUP:
7335 case SCI_WORDPARTLEFT:
7336 case SCI_WORDPARTLEFTEXTEND:
7337 case SCI_WORDPARTRIGHT:
7338 case SCI_WORDPARTRIGHTEXTEND:
7339 case SCI_DELETEBACKNOTLINE:
7340 case SCI_HOMEDISPLAY:
7341 case SCI_HOMEDISPLAYEXTEND:
7342 case SCI_LINEENDDISPLAY:
7343 case SCI_LINEENDDISPLAYEXTEND:
7344 case SCI_LINEDOWNRECTEXTEND:
7345 case SCI_LINEUPRECTEXTEND:
7346 case SCI_CHARLEFTRECTEXTEND:
7347 case SCI_CHARRIGHTRECTEXTEND:
7348 case SCI_HOMERECTEXTEND:
7349 case SCI_VCHOMERECTEXTEND:
7350 case SCI_LINEENDRECTEXTEND:
7351 case SCI_PAGEUPRECTEXTEND:
7352 case SCI_PAGEDOWNRECTEXTEND:
7353 case SCI_SELECTIONDUPLICATE:
7354 return KeyCommand(iMessage);
7356 case SCI_BRACEHIGHLIGHT:
7357 SetBraceHighlight(static_cast<int>(wParam), static_cast<int>(lParam), STYLE_BRACELIGHT);
7358 break;
7360 case SCI_BRACEHIGHLIGHTINDICATOR:
7361 if (lParam >= 0 && lParam <= INDIC_MAX) {
7362 vs.braceHighlightIndicatorSet = wParam != 0;
7363 vs.braceHighlightIndicator = static_cast<int>(lParam);
7365 break;
7367 case SCI_BRACEBADLIGHT:
7368 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
7369 break;
7371 case SCI_BRACEBADLIGHTINDICATOR:
7372 if (lParam >= 0 && lParam <= INDIC_MAX) {
7373 vs.braceBadLightIndicatorSet = wParam != 0;
7374 vs.braceBadLightIndicator = static_cast<int>(lParam);
7376 break;
7378 case SCI_BRACEMATCH:
7379 // wParam is position of char to find brace for,
7380 // lParam is maximum amount of text to restyle to find it
7381 return pdoc->BraceMatch(static_cast<int>(wParam), static_cast<int>(lParam));
7383 case SCI_GETVIEWEOL:
7384 return vs.viewEOL;
7386 case SCI_SETVIEWEOL:
7387 vs.viewEOL = wParam != 0;
7388 InvalidateStyleRedraw();
7389 break;
7391 case SCI_SETZOOM:
7392 vs.zoomLevel = static_cast<int>(wParam);
7393 InvalidateStyleRedraw();
7394 NotifyZoom();
7395 break;
7397 case SCI_GETZOOM:
7398 return vs.zoomLevel;
7400 case SCI_GETEDGECOLUMN:
7401 return vs.theEdge;
7403 case SCI_SETEDGECOLUMN:
7404 vs.theEdge = static_cast<int>(wParam);
7405 InvalidateStyleRedraw();
7406 break;
7408 case SCI_GETEDGEMODE:
7409 return vs.edgeState;
7411 case SCI_SETEDGEMODE:
7412 vs.edgeState = static_cast<int>(wParam);
7413 InvalidateStyleRedraw();
7414 break;
7416 case SCI_GETEDGECOLOUR:
7417 return vs.edgecolour.AsLong();
7419 case SCI_SETEDGECOLOUR:
7420 vs.edgecolour = ColourDesired(static_cast<long>(wParam));
7421 InvalidateStyleRedraw();
7422 break;
7424 case SCI_GETDOCPOINTER:
7425 return reinterpret_cast<sptr_t>(pdoc);
7427 case SCI_SETDOCPOINTER:
7428 CancelModes();
7429 SetDocPointer(reinterpret_cast<Document *>(lParam));
7430 return 0;
7432 case SCI_CREATEDOCUMENT: {
7433 Document *doc = new Document();
7434 doc->AddRef();
7435 return reinterpret_cast<sptr_t>(doc);
7438 case SCI_ADDREFDOCUMENT:
7439 (reinterpret_cast<Document *>(lParam))->AddRef();
7440 break;
7442 case SCI_RELEASEDOCUMENT:
7443 (reinterpret_cast<Document *>(lParam))->Release();
7444 break;
7446 case SCI_CREATELOADER: {
7447 Document *doc = new Document();
7448 doc->AddRef();
7449 doc->Allocate(static_cast<int>(wParam));
7450 doc->SetUndoCollection(false);
7451 return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
7454 case SCI_SETMODEVENTMASK:
7455 modEventMask = static_cast<int>(wParam);
7456 return 0;
7458 case SCI_GETMODEVENTMASK:
7459 return modEventMask;
7461 case SCI_CONVERTEOLS:
7462 pdoc->ConvertLineEnds(static_cast<int>(wParam));
7463 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
7464 return 0;
7466 case SCI_SETLENGTHFORENCODE:
7467 lengthForEncode = static_cast<int>(wParam);
7468 return 0;
7470 case SCI_SELECTIONISRECTANGLE:
7471 return sel.selType == Selection::selRectangle ? 1 : 0;
7473 case SCI_SETSELECTIONMODE: {
7474 switch (wParam) {
7475 case SC_SEL_STREAM:
7476 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7477 sel.selType = Selection::selStream;
7478 break;
7479 case SC_SEL_RECTANGLE:
7480 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
7481 sel.selType = Selection::selRectangle;
7482 break;
7483 case SC_SEL_LINES:
7484 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
7485 sel.selType = Selection::selLines;
7486 break;
7487 case SC_SEL_THIN:
7488 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
7489 sel.selType = Selection::selThin;
7490 break;
7491 default:
7492 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7493 sel.selType = Selection::selStream;
7495 InvalidateWholeSelection();
7496 break;
7498 case SCI_GETSELECTIONMODE:
7499 switch (sel.selType) {
7500 case Selection::selStream:
7501 return SC_SEL_STREAM;
7502 case Selection::selRectangle:
7503 return SC_SEL_RECTANGLE;
7504 case Selection::selLines:
7505 return SC_SEL_LINES;
7506 case Selection::selThin:
7507 return SC_SEL_THIN;
7508 default: // ?!
7509 return SC_SEL_STREAM;
7511 case SCI_GETLINESELSTARTPOSITION:
7512 case SCI_GETLINESELENDPOSITION: {
7513 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(static_cast<int>(wParam))),
7514 SelectionPosition(pdoc->LineEnd(static_cast<int>(wParam))));
7515 for (size_t r=0; r<sel.Count(); r++) {
7516 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
7517 if (portion.start.IsValid()) {
7518 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
7521 return INVALID_POSITION;
7524 case SCI_SETOVERTYPE:
7525 inOverstrike = wParam != 0;
7526 break;
7528 case SCI_GETOVERTYPE:
7529 return inOverstrike ? 1 : 0;
7531 case SCI_SETFOCUS:
7532 SetFocusState(wParam != 0);
7533 break;
7535 case SCI_GETFOCUS:
7536 return hasFocus;
7538 case SCI_SETSTATUS:
7539 errorStatus = static_cast<int>(wParam);
7540 break;
7542 case SCI_GETSTATUS:
7543 return errorStatus;
7545 case SCI_SETMOUSEDOWNCAPTURES:
7546 mouseDownCaptures = wParam != 0;
7547 break;
7549 case SCI_GETMOUSEDOWNCAPTURES:
7550 return mouseDownCaptures;
7552 case SCI_SETCURSOR:
7553 cursorMode = static_cast<int>(wParam);
7554 DisplayCursor(Window::cursorText);
7555 break;
7557 case SCI_GETCURSOR:
7558 return cursorMode;
7560 case SCI_SETCONTROLCHARSYMBOL:
7561 vs.controlCharSymbol = static_cast<int>(wParam);
7562 InvalidateStyleRedraw();
7563 break;
7565 case SCI_GETCONTROLCHARSYMBOL:
7566 return vs.controlCharSymbol;
7568 case SCI_SETREPRESENTATION:
7569 reprs.SetRepresentation(reinterpret_cast<const char *>(wParam), CharPtrFromSPtr(lParam));
7570 break;
7572 case SCI_GETREPRESENTATION: {
7573 const Representation *repr = reprs.RepresentationFromCharacter(
7574 reinterpret_cast<const char *>(wParam), UTF8MaxBytes);
7575 if (repr) {
7576 return StringResult(lParam, repr->stringRep.c_str());
7578 return 0;
7581 case SCI_CLEARREPRESENTATION:
7582 reprs.ClearRepresentation(reinterpret_cast<const char *>(wParam));
7583 break;
7585 case SCI_STARTRECORD:
7586 recordingMacro = true;
7587 return 0;
7589 case SCI_STOPRECORD:
7590 recordingMacro = false;
7591 return 0;
7593 case SCI_MOVECARETINSIDEVIEW:
7594 MoveCaretInsideView();
7595 break;
7597 case SCI_SETFOLDMARGINCOLOUR:
7598 vs.foldmarginColour = ColourOptional(wParam, lParam);
7599 InvalidateStyleRedraw();
7600 break;
7602 case SCI_SETFOLDMARGINHICOLOUR:
7603 vs.foldmarginHighlightColour = ColourOptional(wParam, lParam);
7604 InvalidateStyleRedraw();
7605 break;
7607 case SCI_SETHOTSPOTACTIVEFORE:
7608 vs.hotspotColours.fore = ColourOptional(wParam, lParam);
7609 InvalidateStyleRedraw();
7610 break;
7612 case SCI_GETHOTSPOTACTIVEFORE:
7613 return vs.hotspotColours.fore.AsLong();
7615 case SCI_SETHOTSPOTACTIVEBACK:
7616 vs.hotspotColours.back = ColourOptional(wParam, lParam);
7617 InvalidateStyleRedraw();
7618 break;
7620 case SCI_GETHOTSPOTACTIVEBACK:
7621 return vs.hotspotColours.back.AsLong();
7623 case SCI_SETHOTSPOTACTIVEUNDERLINE:
7624 vs.hotspotUnderline = wParam != 0;
7625 InvalidateStyleRedraw();
7626 break;
7628 case SCI_GETHOTSPOTACTIVEUNDERLINE:
7629 return vs.hotspotUnderline ? 1 : 0;
7631 case SCI_SETHOTSPOTSINGLELINE:
7632 vs.hotspotSingleLine = wParam != 0;
7633 InvalidateStyleRedraw();
7634 break;
7636 case SCI_GETHOTSPOTSINGLELINE:
7637 return vs.hotspotSingleLine ? 1 : 0;
7639 case SCI_SETPASTECONVERTENDINGS:
7640 convertPastes = wParam != 0;
7641 break;
7643 case SCI_GETPASTECONVERTENDINGS:
7644 return convertPastes ? 1 : 0;
7646 case SCI_GETCHARACTERPOINTER:
7647 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
7649 case SCI_GETRANGEPOINTER:
7650 return reinterpret_cast<sptr_t>(pdoc->RangePointer(static_cast<int>(wParam), static_cast<int>(lParam)));
7652 case SCI_GETGAPPOSITION:
7653 return pdoc->GapPosition();
7655 case SCI_SETEXTRAASCENT:
7656 vs.extraAscent = static_cast<int>(wParam);
7657 InvalidateStyleRedraw();
7658 break;
7660 case SCI_GETEXTRAASCENT:
7661 return vs.extraAscent;
7663 case SCI_SETEXTRADESCENT:
7664 vs.extraDescent = static_cast<int>(wParam);
7665 InvalidateStyleRedraw();
7666 break;
7668 case SCI_GETEXTRADESCENT:
7669 return vs.extraDescent;
7671 case SCI_MARGINSETSTYLEOFFSET:
7672 vs.marginStyleOffset = static_cast<int>(wParam);
7673 InvalidateStyleRedraw();
7674 break;
7676 case SCI_MARGINGETSTYLEOFFSET:
7677 return vs.marginStyleOffset;
7679 case SCI_SETMARGINOPTIONS:
7680 marginOptions = static_cast<int>(wParam);
7681 break;
7683 case SCI_GETMARGINOPTIONS:
7684 return marginOptions;
7686 case SCI_MARGINSETTEXT:
7687 pdoc->MarginSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7688 break;
7690 case SCI_MARGINGETTEXT: {
7691 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7692 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
7695 case SCI_MARGINSETSTYLE:
7696 pdoc->MarginSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
7697 break;
7699 case SCI_MARGINGETSTYLE: {
7700 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7701 return st.style;
7704 case SCI_MARGINSETSTYLES:
7705 pdoc->MarginSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
7706 break;
7708 case SCI_MARGINGETSTYLES: {
7709 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7710 return BytesResult(lParam, st.styles, st.length);
7713 case SCI_MARGINTEXTCLEARALL:
7714 pdoc->MarginClearAll();
7715 break;
7717 case SCI_ANNOTATIONSETTEXT:
7718 pdoc->AnnotationSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7719 break;
7721 case SCI_ANNOTATIONGETTEXT: {
7722 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7723 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
7726 case SCI_ANNOTATIONGETSTYLE: {
7727 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7728 return st.style;
7731 case SCI_ANNOTATIONSETSTYLE:
7732 pdoc->AnnotationSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
7733 break;
7735 case SCI_ANNOTATIONSETSTYLES:
7736 pdoc->AnnotationSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
7737 break;
7739 case SCI_ANNOTATIONGETSTYLES: {
7740 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7741 return BytesResult(lParam, st.styles, st.length);
7744 case SCI_ANNOTATIONGETLINES:
7745 return pdoc->AnnotationLines(static_cast<int>(wParam));
7747 case SCI_ANNOTATIONCLEARALL:
7748 pdoc->AnnotationClearAll();
7749 break;
7751 case SCI_ANNOTATIONSETVISIBLE:
7752 SetAnnotationVisible(static_cast<int>(wParam));
7753 break;
7755 case SCI_ANNOTATIONGETVISIBLE:
7756 return vs.annotationVisible;
7758 case SCI_ANNOTATIONSETSTYLEOFFSET:
7759 vs.annotationStyleOffset = static_cast<int>(wParam);
7760 InvalidateStyleRedraw();
7761 break;
7763 case SCI_ANNOTATIONGETSTYLEOFFSET:
7764 return vs.annotationStyleOffset;
7766 case SCI_RELEASEALLEXTENDEDSTYLES:
7767 vs.ReleaseAllExtendedStyles();
7768 break;
7770 case SCI_ALLOCATEEXTENDEDSTYLES:
7771 return vs.AllocateExtendedStyles(static_cast<int>(wParam));
7773 case SCI_ADDUNDOACTION:
7774 pdoc->AddUndoAction(static_cast<int>(wParam), lParam & UNDO_MAY_COALESCE);
7775 break;
7777 case SCI_SETMOUSESELECTIONRECTANGULARSWITCH:
7778 mouseSelectionRectangularSwitch = wParam != 0;
7779 break;
7781 case SCI_GETMOUSESELECTIONRECTANGULARSWITCH:
7782 return mouseSelectionRectangularSwitch;
7784 case SCI_SETMULTIPLESELECTION:
7785 multipleSelection = wParam != 0;
7786 InvalidateCaret();
7787 break;
7789 case SCI_GETMULTIPLESELECTION:
7790 return multipleSelection;
7792 case SCI_SETADDITIONALSELECTIONTYPING:
7793 additionalSelectionTyping = wParam != 0;
7794 InvalidateCaret();
7795 break;
7797 case SCI_GETADDITIONALSELECTIONTYPING:
7798 return additionalSelectionTyping;
7800 case SCI_SETMULTIPASTE:
7801 multiPasteMode = static_cast<int>(wParam);
7802 break;
7804 case SCI_GETMULTIPASTE:
7805 return multiPasteMode;
7807 case SCI_SETADDITIONALCARETSBLINK:
7808 view.additionalCaretsBlink = wParam != 0;
7809 InvalidateCaret();
7810 break;
7812 case SCI_GETADDITIONALCARETSBLINK:
7813 return view.additionalCaretsBlink;
7815 case SCI_SETADDITIONALCARETSVISIBLE:
7816 view.additionalCaretsVisible = wParam != 0;
7817 InvalidateCaret();
7818 break;
7820 case SCI_GETADDITIONALCARETSVISIBLE:
7821 return view.additionalCaretsVisible;
7823 case SCI_GETSELECTIONS:
7824 return sel.Count();
7826 case SCI_GETSELECTIONEMPTY:
7827 return sel.Empty();
7829 case SCI_CLEARSELECTIONS:
7830 sel.Clear();
7831 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7832 Redraw();
7833 break;
7835 case SCI_SETSELECTION:
7836 sel.SetSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
7837 Redraw();
7838 break;
7840 case SCI_ADDSELECTION:
7841 sel.AddSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
7842 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7843 Redraw();
7844 break;
7846 case SCI_DROPSELECTIONN:
7847 sel.DropSelection(static_cast<int>(wParam));
7848 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7849 Redraw();
7850 break;
7852 case SCI_SETMAINSELECTION:
7853 sel.SetMain(static_cast<int>(wParam));
7854 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7855 Redraw();
7856 break;
7858 case SCI_GETMAINSELECTION:
7859 return sel.Main();
7861 case SCI_SETSELECTIONNCARET:
7862 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
7863 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7864 Redraw();
7865 break;
7867 case SCI_GETSELECTIONNCARET:
7868 return sel.Range(wParam).caret.Position();
7870 case SCI_SETSELECTIONNANCHOR:
7871 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
7872 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7873 Redraw();
7874 break;
7875 case SCI_GETSELECTIONNANCHOR:
7876 return sel.Range(wParam).anchor.Position();
7878 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
7879 sel.Range(wParam).caret.SetVirtualSpace(static_cast<int>(lParam));
7880 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7881 Redraw();
7882 break;
7884 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
7885 return sel.Range(wParam).caret.VirtualSpace();
7887 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
7888 sel.Range(wParam).anchor.SetVirtualSpace(static_cast<int>(lParam));
7889 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7890 Redraw();
7891 break;
7893 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
7894 return sel.Range(wParam).anchor.VirtualSpace();
7896 case SCI_SETSELECTIONNSTART:
7897 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
7898 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7899 Redraw();
7900 break;
7902 case SCI_GETSELECTIONNSTART:
7903 return sel.Range(wParam).Start().Position();
7905 case SCI_SETSELECTIONNEND:
7906 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
7907 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7908 Redraw();
7909 break;
7911 case SCI_GETSELECTIONNEND:
7912 return sel.Range(wParam).End().Position();
7914 case SCI_SETRECTANGULARSELECTIONCARET:
7915 if (!sel.IsRectangular())
7916 sel.Clear();
7917 sel.selType = Selection::selRectangle;
7918 sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
7919 SetRectangularRange();
7920 Redraw();
7921 break;
7923 case SCI_GETRECTANGULARSELECTIONCARET:
7924 return sel.Rectangular().caret.Position();
7926 case SCI_SETRECTANGULARSELECTIONANCHOR:
7927 if (!sel.IsRectangular())
7928 sel.Clear();
7929 sel.selType = Selection::selRectangle;
7930 sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
7931 SetRectangularRange();
7932 Redraw();
7933 break;
7935 case SCI_GETRECTANGULARSELECTIONANCHOR:
7936 return sel.Rectangular().anchor.Position();
7938 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
7939 if (!sel.IsRectangular())
7940 sel.Clear();
7941 sel.selType = Selection::selRectangle;
7942 sel.Rectangular().caret.SetVirtualSpace(static_cast<int>(wParam));
7943 SetRectangularRange();
7944 Redraw();
7945 break;
7947 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
7948 return sel.Rectangular().caret.VirtualSpace();
7950 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
7951 if (!sel.IsRectangular())
7952 sel.Clear();
7953 sel.selType = Selection::selRectangle;
7954 sel.Rectangular().anchor.SetVirtualSpace(static_cast<int>(wParam));
7955 SetRectangularRange();
7956 Redraw();
7957 break;
7959 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
7960 return sel.Rectangular().anchor.VirtualSpace();
7962 case SCI_SETVIRTUALSPACEOPTIONS:
7963 virtualSpaceOptions = static_cast<int>(wParam);
7964 break;
7966 case SCI_GETVIRTUALSPACEOPTIONS:
7967 return virtualSpaceOptions;
7969 case SCI_SETADDITIONALSELFORE:
7970 vs.selAdditionalForeground = ColourDesired(static_cast<long>(wParam));
7971 InvalidateStyleRedraw();
7972 break;
7974 case SCI_SETADDITIONALSELBACK:
7975 vs.selAdditionalBackground = ColourDesired(static_cast<long>(wParam));
7976 InvalidateStyleRedraw();
7977 break;
7979 case SCI_SETADDITIONALSELALPHA:
7980 vs.selAdditionalAlpha = static_cast<int>(wParam);
7981 InvalidateStyleRedraw();
7982 break;
7984 case SCI_GETADDITIONALSELALPHA:
7985 return vs.selAdditionalAlpha;
7987 case SCI_SETADDITIONALCARETFORE:
7988 vs.additionalCaretColour = ColourDesired(static_cast<long>(wParam));
7989 InvalidateStyleRedraw();
7990 break;
7992 case SCI_GETADDITIONALCARETFORE:
7993 return vs.additionalCaretColour.AsLong();
7995 case SCI_ROTATESELECTION:
7996 sel.RotateMain();
7997 InvalidateWholeSelection();
7998 break;
8000 case SCI_SWAPMAINANCHORCARET:
8001 InvalidateSelection(sel.RangeMain());
8002 sel.RangeMain().Swap();
8003 break;
8005 case SCI_MULTIPLESELECTADDNEXT:
8006 MultipleSelectAdd(addOne);
8007 break;
8009 case SCI_MULTIPLESELECTADDEACH:
8010 MultipleSelectAdd(addEach);
8011 break;
8013 case SCI_CHANGELEXERSTATE:
8014 pdoc->ChangeLexerState(static_cast<int>(wParam), static_cast<int>(lParam));
8015 break;
8017 case SCI_SETIDENTIFIER:
8018 SetCtrlID(static_cast<int>(wParam));
8019 break;
8021 case SCI_GETIDENTIFIER:
8022 return GetCtrlID();
8024 case SCI_SETTECHNOLOGY:
8025 // No action by default
8026 break;
8028 case SCI_GETTECHNOLOGY:
8029 return technology;
8031 case SCI_COUNTCHARACTERS:
8032 return pdoc->CountCharacters(static_cast<int>(wParam), static_cast<int>(lParam));
8034 default:
8035 return DefWndProc(iMessage, wParam, lParam);
8037 //Platform::DebugPrintf("end wnd proc\n");
8038 return 0l;