Applied backgroundcolors.patch
[TortoiseGit.git] / ext / scintilla / src / Editor.cxx
blobb92789b58b072cf30a2c14e0b707faaadbfe62ab
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 view.editor = this;
105 ctrlID = 0;
107 stylesValid = false;
108 technology = SC_TECHNOLOGY_DEFAULT;
109 scaleRGBAImage = 100.0f;
111 cursorMode = SC_CURSORNORMAL;
113 hasFocus = false;
114 errorStatus = 0;
115 mouseDownCaptures = true;
117 lastClickTime = 0;
118 doubleClickCloseThreshold = Point(3, 3);
119 dwellDelay = SC_TIME_FOREVER;
120 ticksToDwell = SC_TIME_FOREVER;
121 dwelling = false;
122 ptMouseLast.x = 0;
123 ptMouseLast.y = 0;
124 inDragDrop = ddNone;
125 dropWentOutside = false;
126 posDrop = SelectionPosition(invalidPosition);
127 hotSpotClickPos = INVALID_POSITION;
128 selectionType = selChar;
130 lastXChosen = 0;
131 lineAnchorPos = 0;
132 originalAnchorPos = 0;
133 wordSelectAnchorStartPos = 0;
134 wordSelectAnchorEndPos = 0;
135 wordSelectInitialCaretPos = -1;
137 caretXPolicy = CARET_SLOP | CARET_EVEN;
138 caretXSlop = 50;
140 caretYPolicy = CARET_EVEN;
141 caretYSlop = 0;
143 visiblePolicy = 0;
144 visibleSlop = 0;
146 searchAnchor = 0;
148 xCaretMargin = 50;
149 horizontalScrollBarVisible = true;
150 scrollWidth = 2000;
151 verticalScrollBarVisible = true;
152 endAtLastLine = true;
153 caretSticky = SC_CARETSTICKY_OFF;
154 marginOptions = SC_MARGINOPTION_NONE;
155 mouseSelectionRectangularSwitch = false;
156 multipleSelection = false;
157 additionalSelectionTyping = false;
158 multiPasteMode = SC_MULTIPASTE_ONCE;
159 virtualSpaceOptions = SCVS_NONE;
161 targetStart = 0;
162 targetEnd = 0;
163 searchFlags = 0;
165 topLine = 0;
166 posTopLine = 0;
168 lengthForEncode = -1;
170 needUpdateUI = 0;
171 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
173 paintState = notPainting;
174 paintAbandonedByStyling = false;
175 paintingAllText = false;
176 willRedrawAll = false;
177 idleStyling = SC_IDLESTYLING_NONE;
178 needIdleStyling = false;
180 modEventMask = SC_MODEVENTMASKALL;
182 pdoc->AddWatcher(this, 0);
184 recordingMacro = false;
185 foldAutomatic = 0;
187 convertPastes = true;
189 SetRepresentations();
192 Editor::~Editor() {
193 pdoc->RemoveWatcher(this, 0);
194 DropGraphics(true);
197 void Editor::Finalise() {
198 SetIdle(false);
199 CancelModes();
202 void Editor::SetRepresentations() {
203 reprs.Clear();
205 // C0 control set
206 const char *reps[] = {
207 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
208 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
209 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
210 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
212 for (size_t j=0; j < ELEMENTS(reps); j++) {
213 char c[2] = { static_cast<char>(j), 0 };
214 reprs.SetRepresentation(c, reps[j]);
217 // C1 control set
218 // As well as Unicode mode, ISO-8859-1 should use these
219 if (IsUnicodeMode()) {
220 const char *repsC1[] = {
221 "PAD", "HOP", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
222 "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
223 "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
224 "SOS", "SGCI", "SCI", "CSI", "ST", "OSC", "PM", "APC"
226 for (size_t j=0; j < ELEMENTS(repsC1); j++) {
227 char c1[3] = { '\xc2', static_cast<char>(0x80+j), 0 };
228 reprs.SetRepresentation(c1, repsC1[j]);
230 reprs.SetRepresentation("\xe2\x80\xa8", "LS");
231 reprs.SetRepresentation("\xe2\x80\xa9", "PS");
234 // UTF-8 invalid bytes
235 if (IsUnicodeMode()) {
236 for (int k=0x80; k < 0x100; k++) {
237 char hiByte[2] = { static_cast<char>(k), 0 };
238 char hexits[4];
239 sprintf(hexits, "x%2X", k);
240 reprs.SetRepresentation(hiByte, hexits);
245 void Editor::DropGraphics(bool freeObjects) {
246 marginView.DropGraphics(freeObjects);
247 view.DropGraphics(freeObjects);
250 void Editor::AllocateGraphics() {
251 marginView.AllocateGraphics(vs);
252 view.AllocateGraphics(vs);
255 void Editor::InvalidateStyleData() {
256 stylesValid = false;
257 vs.technology = technology;
258 DropGraphics(false);
259 AllocateGraphics();
260 view.llc.Invalidate(LineLayout::llInvalid);
261 view.posCache.Clear();
264 void Editor::InvalidateStyleRedraw() {
265 NeedWrapping();
266 InvalidateStyleData();
267 Redraw();
270 void Editor::RefreshStyleData() {
271 if (!stylesValid) {
272 stylesValid = true;
273 AutoSurface surface(this);
274 if (surface) {
275 vs.Refresh(*surface, pdoc->tabInChars);
277 SetScrollBars();
278 SetRectangularRange();
282 Point Editor::GetVisibleOriginInMain() const {
283 return Point(0,0);
286 Point Editor::DocumentPointFromView(Point ptView) const {
287 Point ptDocument = ptView;
288 if (wMargin.GetID()) {
289 Point ptOrigin = GetVisibleOriginInMain();
290 ptDocument.x += ptOrigin.x;
291 ptDocument.y += ptOrigin.y;
292 } else {
293 ptDocument.x += xOffset;
294 ptDocument.y += topLine * vs.lineHeight;
296 return ptDocument;
299 int Editor::TopLineOfMain() const {
300 if (wMargin.GetID())
301 return 0;
302 else
303 return topLine;
306 PRectangle Editor::GetClientRectangle() const {
307 Window &win = const_cast<Window &>(wMain);
308 return win.GetClientPosition();
311 PRectangle Editor::GetClientDrawingRectangle() {
312 return GetClientRectangle();
315 PRectangle Editor::GetTextRectangle() const {
316 PRectangle rc = GetClientRectangle();
317 rc.left += vs.textStart;
318 rc.right -= vs.rightMarginWidth;
319 return rc;
322 int Editor::LinesOnScreen() const {
323 PRectangle rcClient = GetClientRectangle();
324 int htClient = static_cast<int>(rcClient.bottom - rcClient.top);
325 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
326 return htClient / vs.lineHeight;
329 int Editor::LinesToScroll() const {
330 int retVal = LinesOnScreen() - 1;
331 if (retVal < 1)
332 return 1;
333 else
334 return retVal;
337 int Editor::MaxScrollPos() const {
338 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
339 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
340 int retVal = cs.LinesDisplayed();
341 if (endAtLastLine) {
342 retVal -= LinesOnScreen();
343 } else {
344 retVal--;
346 if (retVal < 0) {
347 return 0;
348 } else {
349 return retVal;
353 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
354 if (sp.Position() < 0) {
355 return SelectionPosition(0);
356 } else if (sp.Position() > pdoc->Length()) {
357 return SelectionPosition(pdoc->Length());
358 } else {
359 // If not at end of line then set offset to 0
360 if (!pdoc->IsLineEndPosition(sp.Position()))
361 sp.SetVirtualSpace(0);
362 return sp;
366 Point Editor::LocationFromPosition(SelectionPosition pos) {
367 RefreshStyleData();
368 AutoSurface surface(this);
369 return view.LocationFromPosition(surface, *this, pos, topLine, vs);
372 Point Editor::LocationFromPosition(int pos) {
373 return LocationFromPosition(SelectionPosition(pos));
376 int Editor::XFromPosition(int pos) {
377 Point pt = LocationFromPosition(pos);
378 return static_cast<int>(pt.x) - vs.textStart + xOffset;
381 int Editor::XFromPosition(SelectionPosition sp) {
382 Point pt = LocationFromPosition(sp);
383 return static_cast<int>(pt.x) - vs.textStart + xOffset;
386 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
387 RefreshStyleData();
388 AutoSurface surface(this);
390 if (canReturnInvalid) {
391 PRectangle rcClient = GetTextRectangle();
392 // May be in scroll view coordinates so translate back to main view
393 Point ptOrigin = GetVisibleOriginInMain();
394 rcClient.Move(-ptOrigin.x, -ptOrigin.y);
395 if (!rcClient.Contains(pt))
396 return SelectionPosition(INVALID_POSITION);
397 if (pt.x < vs.textStart)
398 return SelectionPosition(INVALID_POSITION);
399 if (pt.y < 0)
400 return SelectionPosition(INVALID_POSITION);
402 pt = DocumentPointFromView(pt);
403 return view.SPositionFromLocation(surface, *this, pt, canReturnInvalid, charPosition, virtualSpace, vs);
406 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
407 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
411 * Find the document position corresponding to an x coordinate on a particular document line.
412 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
413 * This method is used for rectangular selections and does not work on wrapped lines.
415 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
416 RefreshStyleData();
417 if (lineDoc >= pdoc->LinesTotal())
418 return SelectionPosition(pdoc->Length());
419 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
420 AutoSurface surface(this);
421 return view.SPositionFromLineX(surface, *this, lineDoc, x, vs);
424 int Editor::PositionFromLineX(int lineDoc, int x) {
425 return SPositionFromLineX(lineDoc, x).Position();
428 int Editor::LineFromLocation(Point pt) const {
429 return cs.DocFromDisplay(static_cast<int>(pt.y) / vs.lineHeight + topLine);
432 void Editor::SetTopLine(int topLineNew) {
433 if ((topLine != topLineNew) && (topLineNew >= 0)) {
434 topLine = topLineNew;
435 ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
437 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
441 * If painting then abandon the painting because a wider redraw is needed.
442 * @return true if calling code should stop drawing.
444 bool Editor::AbandonPaint() {
445 if ((paintState == painting) && !paintingAllText) {
446 paintState = paintAbandoned;
448 return paintState == paintAbandoned;
451 void Editor::RedrawRect(PRectangle rc) {
452 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
454 // Clip the redraw rectangle into the client area
455 PRectangle rcClient = GetClientRectangle();
456 if (rc.top < rcClient.top)
457 rc.top = rcClient.top;
458 if (rc.bottom > rcClient.bottom)
459 rc.bottom = rcClient.bottom;
460 if (rc.left < rcClient.left)
461 rc.left = rcClient.left;
462 if (rc.right > rcClient.right)
463 rc.right = rcClient.right;
465 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
466 wMain.InvalidateRectangle(rc);
470 void Editor::DiscardOverdraw() {
471 // Overridden on platforms that may draw outside visible area.
474 void Editor::Redraw() {
475 //Platform::DebugPrintf("Redraw all\n");
476 PRectangle rcClient = GetClientRectangle();
477 wMain.InvalidateRectangle(rcClient);
478 if (wMargin.GetID())
479 wMargin.InvalidateAll();
480 //wMain.InvalidateAll();
483 void Editor::RedrawSelMargin(int line, bool allAfter) {
484 const bool markersInText = vs.maskInLine || vs.maskDrawInText;
485 if (!wMargin.GetID() || markersInText) { // May affect text area so may need to abandon and retry
486 if (AbandonPaint()) {
487 return;
490 if (wMargin.GetID() && markersInText) {
491 Redraw();
492 return;
494 PRectangle rcMarkers = GetClientRectangle();
495 if (!markersInText) {
496 // Normal case: just draw the margin
497 rcMarkers.right = rcMarkers.left + vs.fixedColumnWidth;
499 if (line != -1) {
500 PRectangle rcLine = RectangleFromRange(Range(pdoc->LineStart(line)), 0);
502 // Inflate line rectangle if there are image markers with height larger than line height
503 if (vs.largestMarkerHeight > vs.lineHeight) {
504 int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
505 rcLine.top -= delta;
506 rcLine.bottom += delta;
507 if (rcLine.top < rcMarkers.top)
508 rcLine.top = rcMarkers.top;
509 if (rcLine.bottom > rcMarkers.bottom)
510 rcLine.bottom = rcMarkers.bottom;
513 rcMarkers.top = rcLine.top;
514 if (!allAfter)
515 rcMarkers.bottom = rcLine.bottom;
516 if (rcMarkers.Empty())
517 return;
519 if (wMargin.GetID()) {
520 Point ptOrigin = GetVisibleOriginInMain();
521 rcMarkers.Move(-ptOrigin.x, -ptOrigin.y);
522 wMargin.InvalidateRectangle(rcMarkers);
523 } else {
524 wMain.InvalidateRectangle(rcMarkers);
528 PRectangle Editor::RectangleFromRange(Range r, int overlap) {
529 const int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(r.First()));
530 const int maxLine = cs.DisplayLastFromDoc(pdoc->LineFromPosition(r.Last()));
531 const PRectangle rcClientDrawing = GetClientDrawingRectangle();
532 PRectangle rc;
533 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
534 rc.left = static_cast<XYPOSITION>(vs.textStart - leftTextOverlap);
535 rc.top = static_cast<XYPOSITION>((minLine - TopLineOfMain()) * vs.lineHeight - overlap);
536 if (rc.top < rcClientDrawing.top)
537 rc.top = rcClientDrawing.top;
538 // Extend to right of prepared area if any to prevent artifacts from caret line highlight
539 rc.right = rcClientDrawing.right;
540 rc.bottom = static_cast<XYPOSITION>((maxLine - TopLineOfMain() + 1) * vs.lineHeight + overlap);
542 return rc;
545 void Editor::InvalidateRange(int start, int end) {
546 RedrawRect(RectangleFromRange(Range(start, end), view.LinesOverlap() ? vs.lineOverlap : 0));
549 int Editor::CurrentPosition() const {
550 return sel.MainCaret();
553 bool Editor::SelectionEmpty() const {
554 return sel.Empty();
557 SelectionPosition Editor::SelectionStart() {
558 return sel.RangeMain().Start();
561 SelectionPosition Editor::SelectionEnd() {
562 return sel.RangeMain().End();
565 void Editor::SetRectangularRange() {
566 if (sel.IsRectangular()) {
567 int xAnchor = XFromPosition(sel.Rectangular().anchor);
568 int xCaret = XFromPosition(sel.Rectangular().caret);
569 if (sel.selType == Selection::selThin) {
570 xCaret = xAnchor;
572 int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
573 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
574 int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
575 for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
576 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
577 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
578 range.ClearVirtualSpace();
579 if (line == lineAnchorRect)
580 sel.SetSelection(range);
581 else
582 sel.AddSelectionWithoutTrim(range);
587 void Editor::ThinRectangularRange() {
588 if (sel.IsRectangular()) {
589 sel.selType = Selection::selThin;
590 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
591 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
592 } else {
593 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
595 SetRectangularRange();
599 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
600 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
601 invalidateWholeSelection = true;
603 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
604 // +1 for lastAffected ensures caret repainted
605 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
606 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
607 if (invalidateWholeSelection) {
608 for (size_t r=0; r<sel.Count(); r++) {
609 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
610 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
611 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
612 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
615 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
616 InvalidateRange(firstAffected, lastAffected);
619 void Editor::InvalidateWholeSelection() {
620 InvalidateSelection(sel.RangeMain(), true);
623 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
624 currentPos_ = ClampPositionIntoDocument(currentPos_);
625 anchor_ = ClampPositionIntoDocument(anchor_);
626 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
627 /* For Line selection - ensure the anchor and caret are always
628 at the beginning and end of the region lines. */
629 if (sel.selType == Selection::selLines) {
630 if (currentPos_ > anchor_) {
631 anchor_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
632 currentPos_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
633 } else {
634 currentPos_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
635 anchor_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
638 SelectionRange rangeNew(currentPos_, anchor_);
639 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
640 InvalidateSelection(rangeNew);
642 sel.RangeMain() = rangeNew;
643 SetRectangularRange();
644 ClaimSelection();
645 SetHoverIndicatorPosition(sel.MainCaret());
647 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
648 RedrawSelMargin();
650 QueueIdleWork(WorkNeeded::workUpdateUI);
653 void Editor::SetSelection(int currentPos_, int anchor_) {
654 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
657 // Just move the caret on the main selection
658 void Editor::SetSelection(SelectionPosition currentPos_) {
659 currentPos_ = ClampPositionIntoDocument(currentPos_);
660 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
661 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
662 InvalidateSelection(SelectionRange(currentPos_));
664 if (sel.IsRectangular()) {
665 sel.Rectangular() =
666 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
667 SetRectangularRange();
668 } else {
669 sel.RangeMain() =
670 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
672 ClaimSelection();
673 SetHoverIndicatorPosition(sel.MainCaret());
675 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
676 RedrawSelMargin();
678 QueueIdleWork(WorkNeeded::workUpdateUI);
681 void Editor::SetSelection(int currentPos_) {
682 SetSelection(SelectionPosition(currentPos_));
685 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
686 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
687 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
688 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
689 InvalidateSelection(rangeNew);
691 sel.Clear();
692 sel.RangeMain() = rangeNew;
693 SetRectangularRange();
694 ClaimSelection();
695 SetHoverIndicatorPosition(sel.MainCaret());
697 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
698 RedrawSelMargin();
700 QueueIdleWork(WorkNeeded::workUpdateUI);
703 void Editor::SetEmptySelection(int currentPos_) {
704 SetEmptySelection(SelectionPosition(currentPos_));
707 void Editor::MultipleSelectAdd(AddNumber addNumber) {
708 if (SelectionEmpty() || !multipleSelection) {
709 // Select word at caret
710 const int startWord = pdoc->ExtendWordSelect(sel.MainCaret(), -1, true);
711 const int endWord = pdoc->ExtendWordSelect(startWord, 1, true);
712 TrimAndSetSelection(endWord, startWord);
714 } else {
716 if (!pdoc->HasCaseFolder())
717 pdoc->SetCaseFolder(CaseFolderForEncoding());
719 const Range rangeMainSelection(sel.RangeMain().Start().Position(), sel.RangeMain().End().Position());
720 const std::string selectedText = RangeText(rangeMainSelection.start, rangeMainSelection.end);
722 const Range rangeTarget(targetStart, targetEnd);
723 std::vector<Range> searchRanges;
724 // Search should be over the target range excluding the current selection so
725 // may need to search 2 ranges, after the selection then before the selection.
726 if (rangeTarget.Overlaps(rangeMainSelection)) {
727 // Common case is that the selection is completely within the target but
728 // may also have overlap at start or end.
729 if (rangeMainSelection.end < rangeTarget.end)
730 searchRanges.push_back(Range(rangeMainSelection.end, rangeTarget.end));
731 if (rangeTarget.start < rangeMainSelection.start)
732 searchRanges.push_back(Range(rangeTarget.start, rangeMainSelection.start));
733 } else {
734 // No overlap
735 searchRanges.push_back(rangeTarget);
738 for (std::vector<Range>::const_iterator it = searchRanges.begin(); it != searchRanges.end(); ++it) {
739 int searchStart = it->start;
740 const int searchEnd = it->end;
741 for (;;) {
742 int lengthFound = static_cast<int>(selectedText.length());
743 int pos = pdoc->FindText(searchStart, searchEnd, selectedText.c_str(),
744 searchFlags, &lengthFound);
745 if (pos >= 0) {
746 sel.AddSelection(SelectionRange(pos + lengthFound, pos));
747 ScrollRange(sel.RangeMain());
748 Redraw();
749 if (addNumber == addOne)
750 return;
751 searchStart = pos + lengthFound;
752 } else {
753 break;
760 bool Editor::RangeContainsProtected(int start, int end) const {
761 if (vs.ProtectionActive()) {
762 if (start > end) {
763 int t = start;
764 start = end;
765 end = t;
767 for (int pos = start; pos < end; pos++) {
768 if (vs.styles[pdoc->StyleIndexAt(pos)].IsProtected())
769 return true;
772 return false;
775 bool Editor::SelectionContainsProtected() {
776 for (size_t r=0; r<sel.Count(); r++) {
777 if (RangeContainsProtected(sel.Range(r).Start().Position(),
778 sel.Range(r).End().Position())) {
779 return true;
782 return false;
786 * Asks document to find a good position and then moves out of any invisible positions.
788 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
789 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
792 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
793 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
794 if (posMoved != pos.Position())
795 pos.SetPosition(posMoved);
796 if (vs.ProtectionActive()) {
797 if (moveDir > 0) {
798 if ((pos.Position() > 0) && vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()) {
799 while ((pos.Position() < pdoc->Length()) &&
800 (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()))
801 pos.Add(1);
803 } else if (moveDir < 0) {
804 if (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()) {
805 while ((pos.Position() > 0) &&
806 (vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()))
807 pos.Add(-1);
811 return pos;
814 void Editor::MovedCaret(SelectionPosition newPos, SelectionPosition previousPos, bool ensureVisible) {
815 const int currentLine = pdoc->LineFromPosition(newPos.Position());
816 if (ensureVisible) {
817 // In case in need of wrapping to ensure DisplayFromDoc works.
818 if (currentLine >= wrapPending.start)
819 WrapLines(wsAll);
820 XYScrollPosition newXY = XYScrollToMakeVisible(
821 SelectionRange(posDrag.IsValid() ? posDrag : newPos), xysDefault);
822 if (previousPos.IsValid() && (newXY.xOffset == xOffset)) {
823 // simple vertical scroll then invalidate
824 ScrollTo(newXY.topLine);
825 InvalidateSelection(SelectionRange(previousPos), true);
826 } else {
827 SetXYScroll(newXY);
831 ShowCaretAtCurrentPosition();
833 ClaimSelection();
834 SetHoverIndicatorPosition(sel.MainCaret());
835 QueueIdleWork(WorkNeeded::workUpdateUI);
837 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
838 RedrawSelMargin();
842 void Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
843 const SelectionPosition spCaret = ((sel.Count() == 1) && sel.Empty()) ?
844 sel.Last() : SelectionPosition(INVALID_POSITION);
846 int delta = newPos.Position() - sel.MainCaret();
847 newPos = ClampPositionIntoDocument(newPos);
848 newPos = MovePositionOutsideChar(newPos, delta);
849 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
850 // Can't turn into multiple selection so clear additional selections
851 InvalidateSelection(SelectionRange(newPos), true);
852 sel.DropAdditionalRanges();
854 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
855 // Switching to rectangular
856 InvalidateSelection(sel.RangeMain(), false);
857 SelectionRange rangeMain = sel.RangeMain();
858 sel.Clear();
859 sel.Rectangular() = rangeMain;
861 if (selt != Selection::noSel) {
862 sel.selType = selt;
864 if (selt != Selection::noSel || sel.MoveExtends()) {
865 SetSelection(newPos);
866 } else {
867 SetEmptySelection(newPos);
870 MovedCaret(newPos, spCaret, ensureVisible);
873 void Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
874 MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
877 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
878 pos = ClampPositionIntoDocument(pos);
879 pos = MovePositionOutsideChar(pos, moveDir);
880 int lineDoc = pdoc->LineFromPosition(pos.Position());
881 if (cs.GetVisible(lineDoc)) {
882 return pos;
883 } else {
884 int lineDisplay = cs.DisplayFromDoc(lineDoc);
885 if (moveDir > 0) {
886 // lineDisplay is already line before fold as lines in fold use display line of line after fold
887 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
888 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
889 } else {
890 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
891 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
896 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
897 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
900 Point Editor::PointMainCaret() {
901 return LocationFromPosition(sel.Range(sel.Main()).caret);
905 * Choose the x position that the caret will try to stick to
906 * as it moves up and down.
908 void Editor::SetLastXChosen() {
909 Point pt = PointMainCaret();
910 lastXChosen = static_cast<int>(pt.x) + xOffset;
913 void Editor::ScrollTo(int line, bool moveThumb) {
914 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
915 if (topLineNew != topLine) {
916 // Try to optimise small scrolls
917 #ifndef UNDER_CE
918 int linesToMove = topLine - topLineNew;
919 bool performBlit = (abs(linesToMove) <= 10) && (paintState == notPainting);
920 willRedrawAll = !performBlit;
921 #endif
922 SetTopLine(topLineNew);
923 // Optimize by styling the view as this will invalidate any needed area
924 // which could abort the initial paint if discovered later.
925 StyleAreaBounded(GetClientRectangle(), true);
926 #ifndef UNDER_CE
927 // Perform redraw rather than scroll if many lines would be redrawn anyway.
928 if (performBlit) {
929 ScrollText(linesToMove);
930 } else {
931 Redraw();
933 willRedrawAll = false;
934 #else
935 Redraw();
936 #endif
937 if (moveThumb) {
938 SetVerticalScrollPos();
943 void Editor::ScrollText(int /* linesToMove */) {
944 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
945 Redraw();
948 void Editor::HorizontalScrollTo(int xPos) {
949 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
950 if (xPos < 0)
951 xPos = 0;
952 if (!Wrapping() && (xOffset != xPos)) {
953 xOffset = xPos;
954 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
955 SetHorizontalScrollPos();
956 RedrawRect(GetClientRectangle());
960 void Editor::VerticalCentreCaret() {
961 int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
962 int lineDisplay = cs.DisplayFromDoc(lineDoc);
963 int newTop = lineDisplay - (LinesOnScreen() / 2);
964 if (topLine != newTop) {
965 SetTopLine(newTop > 0 ? newTop : 0);
966 RedrawRect(GetClientRectangle());
970 // Avoid 64 bit compiler warnings.
971 // Scintilla does not support text buffers larger than 2**31
972 static int istrlen(const char *s) {
973 return static_cast<int>(s ? strlen(s) : 0);
976 void Editor::MoveSelectedLines(int lineDelta) {
978 // if selection doesn't start at the beginning of the line, set the new start
979 int selectionStart = SelectionStart().Position();
980 int startLine = pdoc->LineFromPosition(selectionStart);
981 int beginningOfStartLine = pdoc->LineStart(startLine);
982 selectionStart = beginningOfStartLine;
984 // if selection doesn't end at the beginning of a line greater than that of the start,
985 // then set it at the beginning of the next one
986 int selectionEnd = SelectionEnd().Position();
987 int endLine = pdoc->LineFromPosition(selectionEnd);
988 int beginningOfEndLine = pdoc->LineStart(endLine);
989 bool appendEol = false;
990 if (selectionEnd > beginningOfEndLine
991 || selectionStart == selectionEnd) {
992 selectionEnd = pdoc->LineStart(endLine + 1);
993 appendEol = (selectionEnd == pdoc->Length() && pdoc->LineFromPosition(selectionEnd) == endLine);
996 // if there's nowhere for the selection to move
997 // (i.e. at the beginning going up or at the end going down),
998 // stop it right there!
999 if ((selectionStart == 0 && lineDelta < 0)
1000 || (selectionEnd == pdoc->Length() && lineDelta > 0)
1001 || selectionStart == selectionEnd) {
1002 return;
1005 UndoGroup ug(pdoc);
1007 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
1008 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
1009 ClearSelection();
1010 selectionEnd = CurrentPosition();
1012 SetSelection(selectionStart, selectionEnd);
1014 SelectionText selectedText;
1015 CopySelectionRange(&selectedText);
1017 int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
1018 Point currentLocation = LocationFromPosition(CurrentPosition());
1019 int currentLine = LineFromLocation(currentLocation);
1021 if (appendEol)
1022 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
1023 ClearSelection();
1025 const char *eol = StringFromEOLMode(pdoc->eolMode);
1026 if (currentLine + lineDelta >= pdoc->LinesTotal())
1027 pdoc->InsertString(pdoc->Length(), eol, istrlen(eol));
1028 GoToLine(currentLine + lineDelta);
1030 selectionLength = pdoc->InsertString(CurrentPosition(), selectedText.Data(), selectionLength);
1031 if (appendEol) {
1032 const int lengthInserted = pdoc->InsertString(CurrentPosition() + selectionLength, eol, istrlen(eol));
1033 selectionLength += lengthInserted;
1035 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1038 void Editor::MoveSelectedLinesUp() {
1039 MoveSelectedLines(-1);
1042 void Editor::MoveSelectedLinesDown() {
1043 MoveSelectedLines(1);
1046 void Editor::MoveCaretInsideView(bool ensureVisible) {
1047 PRectangle rcClient = GetTextRectangle();
1048 Point pt = PointMainCaret();
1049 if (pt.y < rcClient.top) {
1050 MovePositionTo(SPositionFromLocation(
1051 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top)),
1052 false, false, UserVirtualSpace()),
1053 Selection::noSel, ensureVisible);
1054 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1055 int yOfLastLineFullyDisplayed = static_cast<int>(rcClient.top) + (LinesOnScreen() - 1) * vs.lineHeight;
1056 MovePositionTo(SPositionFromLocation(
1057 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top) + yOfLastLineFullyDisplayed),
1058 false, false, UserVirtualSpace()),
1059 Selection::noSel, ensureVisible);
1063 int Editor::DisplayFromPosition(int pos) {
1064 AutoSurface surface(this);
1065 return view.DisplayFromPosition(surface, *this, pos, vs);
1069 * Ensure the caret is reasonably visible in context.
1071 Caret policy in SciTE
1073 If slop is set, we can define a slop value.
1074 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1075 This zone is defined as a number of pixels near the vertical margins,
1076 and as a number of lines near the horizontal margins.
1077 By keeping the caret away from the edges, it is seen within its context,
1078 so it is likely that the identifier that the caret is on can be completely seen,
1079 and that the current line is seen with some of the lines following it which are
1080 often dependent on that line.
1082 If strict is set, the policy is enforced... strictly.
1083 The caret is centred on the display if slop is not set,
1084 and cannot go in the UZ if slop is set.
1086 If jumps is set, the display is moved more energetically
1087 so the caret can move in the same direction longer before the policy is applied again.
1088 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1090 If even is not set, instead of having symmetrical UZs,
1091 the left and bottom UZs are extended up to right and top UZs respectively.
1092 This way, we favour the displaying of useful information: the beginning of lines,
1093 where most code reside, and the lines after the caret, eg. the body of a function.
1095 | | | | |
1096 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1097 | | | | | visibility or going into the UZ) display is...
1098 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1099 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1100 0 | 0 | 0 | 1 | Yes | moved by one position
1101 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1102 0 | 0 | 1 | 1 | Yes | centred on the caret
1103 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1104 0 | 1 | - | 1 | No, caret is always centred | -
1105 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1106 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1107 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1108 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1109 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1110 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1111 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1114 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange &range, const XYScrollOptions options) {
1115 PRectangle rcClient = GetTextRectangle();
1116 Point pt = LocationFromPosition(range.caret);
1117 Point ptAnchor = LocationFromPosition(range.anchor);
1118 const Point ptOrigin = GetVisibleOriginInMain();
1119 pt.x += ptOrigin.x;
1120 pt.y += ptOrigin.y;
1121 ptAnchor.x += ptOrigin.x;
1122 ptAnchor.y += ptOrigin.y;
1123 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1125 XYScrollPosition newXY(xOffset, topLine);
1126 if (rcClient.Empty()) {
1127 return newXY;
1130 // Vertical positioning
1131 if ((options & xysVertical) && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1132 const int lineCaret = DisplayFromPosition(range.caret.Position());
1133 const int linesOnScreen = LinesOnScreen();
1134 const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1135 const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
1136 const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
1137 const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
1138 const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
1140 // It should be possible to scroll the window to show the caret,
1141 // but this fails to remove the caret on GTK+
1142 if (bSlop) { // A margin is defined
1143 int yMoveT, yMoveB;
1144 if (bStrict) {
1145 int yMarginT, yMarginB;
1146 if (!(options & xysUseMargin)) {
1147 // In drag mode, avoid moves
1148 // otherwise, a double click will select several lines.
1149 yMarginT = yMarginB = 0;
1150 } else {
1151 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1152 // a maximum of slightly less than half the heigth of the text area.
1153 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1154 if (bEven) {
1155 yMarginB = yMarginT;
1156 } else {
1157 yMarginB = linesOnScreen - yMarginT - 1;
1160 yMoveT = yMarginT;
1161 if (bEven) {
1162 if (bJump) {
1163 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1165 yMoveB = yMoveT;
1166 } else {
1167 yMoveB = linesOnScreen - yMoveT - 1;
1169 if (lineCaret < topLine + yMarginT) {
1170 // Caret goes too high
1171 newXY.topLine = lineCaret - yMoveT;
1172 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1173 // Caret goes too low
1174 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1176 } else { // Not strict
1177 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1178 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1179 if (bEven) {
1180 yMoveB = yMoveT;
1181 } else {
1182 yMoveB = linesOnScreen - yMoveT - 1;
1184 if (lineCaret < topLine) {
1185 // Caret goes too high
1186 newXY.topLine = lineCaret - yMoveT;
1187 } else if (lineCaret > topLine + linesOnScreen - 1) {
1188 // Caret goes too low
1189 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1192 } else { // No slop
1193 if (!bStrict && !bJump) {
1194 // Minimal move
1195 if (lineCaret < topLine) {
1196 // Caret goes too high
1197 newXY.topLine = lineCaret;
1198 } else if (lineCaret > topLine + linesOnScreen - 1) {
1199 // Caret goes too low
1200 if (bEven) {
1201 newXY.topLine = lineCaret - linesOnScreen + 1;
1202 } else {
1203 newXY.topLine = lineCaret;
1206 } else { // Strict or going out of display
1207 if (bEven) {
1208 // Always center caret
1209 newXY.topLine = lineCaret - halfScreen;
1210 } else {
1211 // Always put caret on top of display
1212 newXY.topLine = lineCaret;
1216 if (!(range.caret == range.anchor)) {
1217 const int lineAnchor = DisplayFromPosition(range.anchor.Position());
1218 if (lineAnchor < lineCaret) {
1219 // Shift up to show anchor or as much of range as possible
1220 newXY.topLine = std::min(newXY.topLine, lineAnchor);
1221 newXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());
1222 } else {
1223 // Shift down to show anchor or as much of range as possible
1224 newXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());
1225 newXY.topLine = std::min(newXY.topLine, lineCaret);
1228 newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
1231 // Horizontal positioning
1232 if ((options & xysHorizontal) && !Wrapping()) {
1233 const int halfScreen = Platform::Maximum(static_cast<int>(rcClient.Width()) - 4, 4) / 2;
1234 const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
1235 const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
1236 const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
1237 const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
1239 if (bSlop) { // A margin is defined
1240 int xMoveL, xMoveR;
1241 if (bStrict) {
1242 int xMarginL, xMarginR;
1243 if (!(options & xysUseMargin)) {
1244 // In drag mode, avoid moves unless very near of the margin
1245 // otherwise, a simple click will select text.
1246 xMarginL = xMarginR = 2;
1247 } else {
1248 // xMargin must equal to caretXSlop, with a minimum of 2 and
1249 // a maximum of slightly less than half the width of the text area.
1250 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1251 if (bEven) {
1252 xMarginL = xMarginR;
1253 } else {
1254 xMarginL = static_cast<int>(rcClient.Width()) - xMarginR - 4;
1257 if (bJump && bEven) {
1258 // Jump is used only in even mode
1259 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1260 } else {
1261 xMoveL = xMoveR = 0; // Not used, avoid a warning
1263 if (pt.x < rcClient.left + xMarginL) {
1264 // Caret is on the left of the display
1265 if (bJump && bEven) {
1266 newXY.xOffset -= xMoveL;
1267 } else {
1268 // Move just enough to allow to display the caret
1269 newXY.xOffset -= static_cast<int>((rcClient.left + xMarginL) - pt.x);
1271 } else if (pt.x >= rcClient.right - xMarginR) {
1272 // Caret is on the right of the display
1273 if (bJump && bEven) {
1274 newXY.xOffset += xMoveR;
1275 } else {
1276 // Move just enough to allow to display the caret
1277 newXY.xOffset += static_cast<int>(pt.x - (rcClient.right - xMarginR) + 1);
1280 } else { // Not strict
1281 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1282 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1283 if (bEven) {
1284 xMoveL = xMoveR;
1285 } else {
1286 xMoveL = static_cast<int>(rcClient.Width()) - xMoveR - 4;
1288 if (pt.x < rcClient.left) {
1289 // Caret is on the left of the display
1290 newXY.xOffset -= xMoveL;
1291 } else if (pt.x >= rcClient.right) {
1292 // Caret is on the right of the display
1293 newXY.xOffset += xMoveR;
1296 } else { // No slop
1297 if (bStrict ||
1298 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1299 // Strict or going out of display
1300 if (bEven) {
1301 // Center caret
1302 newXY.xOffset += static_cast<int>(pt.x - rcClient.left - halfScreen);
1303 } else {
1304 // Put caret on right
1305 newXY.xOffset += static_cast<int>(pt.x - rcClient.right + 1);
1307 } else {
1308 // Move just enough to allow to display the caret
1309 if (pt.x < rcClient.left) {
1310 // Caret is on the left of the display
1311 if (bEven) {
1312 newXY.xOffset -= static_cast<int>(rcClient.left - pt.x);
1313 } else {
1314 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1316 } else if (pt.x >= rcClient.right) {
1317 // Caret is on the right of the display
1318 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1322 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1323 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1324 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 2;
1325 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1326 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 2;
1327 if ((vs.caretStyle == CARETSTYLE_BLOCK) || view.imeCaretBlockOverride) {
1328 // Ensure we can see a good portion of the block caret
1329 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1332 if (!(range.caret == range.anchor)) {
1333 if (ptAnchor.x < pt.x) {
1334 // Shift to left to show anchor or as much of range as possible
1335 int maxOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.left) - 1;
1336 int minOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 1;
1337 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1338 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1339 } else {
1340 // Shift to right to show anchor or as much of range as possible
1341 int minOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.right) + 1;
1342 int maxOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 1;
1343 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1344 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1347 if (newXY.xOffset < 0) {
1348 newXY.xOffset = 0;
1352 return newXY;
1355 void Editor::SetXYScroll(XYScrollPosition newXY) {
1356 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1357 if (newXY.topLine != topLine) {
1358 SetTopLine(newXY.topLine);
1359 SetVerticalScrollPos();
1361 if (newXY.xOffset != xOffset) {
1362 xOffset = newXY.xOffset;
1363 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1364 if (newXY.xOffset > 0) {
1365 PRectangle rcText = GetTextRectangle();
1366 if (horizontalScrollBarVisible &&
1367 rcText.Width() + xOffset > scrollWidth) {
1368 scrollWidth = xOffset + static_cast<int>(rcText.Width());
1369 SetScrollBars();
1372 SetHorizontalScrollPos();
1374 Redraw();
1375 UpdateSystemCaret();
1379 void Editor::ScrollRange(SelectionRange range) {
1380 SetXYScroll(XYScrollToMakeVisible(range, xysDefault));
1383 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1384 SetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret),
1385 static_cast<XYScrollOptions>((useMargin?xysUseMargin:0)|(vert?xysVertical:0)|(horiz?xysHorizontal:0))));
1388 void Editor::ShowCaretAtCurrentPosition() {
1389 if (hasFocus) {
1390 caret.active = true;
1391 caret.on = true;
1392 if (FineTickerAvailable()) {
1393 FineTickerCancel(tickCaret);
1394 if (caret.period > 0)
1395 FineTickerStart(tickCaret, caret.period, caret.period/10);
1396 } else {
1397 SetTicking(true);
1399 } else {
1400 caret.active = false;
1401 caret.on = false;
1402 if (FineTickerAvailable()) {
1403 FineTickerCancel(tickCaret);
1406 InvalidateCaret();
1409 void Editor::DropCaret() {
1410 caret.active = false;
1411 if (FineTickerAvailable()) {
1412 FineTickerCancel(tickCaret);
1414 InvalidateCaret();
1417 void Editor::CaretSetPeriod(int period) {
1418 if (caret.period != period) {
1419 caret.period = period;
1420 caret.on = true;
1421 if (FineTickerAvailable()) {
1422 FineTickerCancel(tickCaret);
1423 if ((caret.active) && (caret.period > 0))
1424 FineTickerStart(tickCaret, caret.period, caret.period/10);
1426 InvalidateCaret();
1430 void Editor::InvalidateCaret() {
1431 if (posDrag.IsValid()) {
1432 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1433 } else {
1434 for (size_t r=0; r<sel.Count(); r++) {
1435 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1438 UpdateSystemCaret();
1441 void Editor::UpdateSystemCaret() {
1444 bool Editor::Wrapping() const {
1445 return vs.wrapState != eWrapNone;
1448 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1449 //Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd);
1450 if (wrapPending.AddRange(docLineStart, docLineEnd)) {
1451 view.llc.Invalidate(LineLayout::llPositions);
1453 // Wrap lines during idle.
1454 if (Wrapping() && wrapPending.NeedsWrap()) {
1455 SetIdle(true);
1459 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1460 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(lineToWrap, *this));
1461 int linesWrapped = 1;
1462 if (ll) {
1463 view.LayoutLine(*this, lineToWrap, surface, vs, ll, wrapWidth);
1464 linesWrapped = ll->lines;
1466 return cs.SetHeight(lineToWrap, linesWrapped +
1467 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1470 // Perform wrapping for a subset of the lines needing wrapping.
1471 // wsAll: wrap all lines which need wrapping in this single call
1472 // wsVisible: wrap currently visible lines
1473 // wsIdle: wrap one page + 100 lines
1474 // Return true if wrapping occurred.
1475 bool Editor::WrapLines(enum wrapScope ws) {
1476 int goodTopLine = topLine;
1477 bool wrapOccurred = false;
1478 if (!Wrapping()) {
1479 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1480 wrapWidth = LineLayout::wrapWidthInfinite;
1481 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1482 cs.SetHeight(lineDoc, 1 +
1483 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1485 wrapOccurred = true;
1487 wrapPending.Reset();
1489 } else if (wrapPending.NeedsWrap()) {
1490 wrapPending.start = std::min(wrapPending.start, pdoc->LinesTotal());
1491 if (!SetIdle(true)) {
1492 // Idle processing not supported so full wrap required.
1493 ws = wsAll;
1495 // Decide where to start wrapping
1496 int lineToWrap = wrapPending.start;
1497 int lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal());
1498 const int lineDocTop = cs.DocFromDisplay(topLine);
1499 const int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1500 if (ws == wsVisible) {
1501 lineToWrap = Platform::Clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal());
1502 // Priority wrap to just after visible area.
1503 // Since wrapping could reduce display lines, treat each
1504 // as taking only one display line.
1505 lineToWrapEnd = lineDocTop;
1506 int lines = LinesOnScreen() + 1;
1507 while ((lineToWrapEnd < cs.LinesInDoc()) && (lines>0)) {
1508 if (cs.GetVisible(lineToWrapEnd))
1509 lines--;
1510 lineToWrapEnd++;
1512 // .. and if the paint window is outside pending wraps
1513 if ((lineToWrap > wrapPending.end) || (lineToWrapEnd < wrapPending.start)) {
1514 // Currently visible text does not need wrapping
1515 return false;
1517 } else if (ws == wsIdle) {
1518 lineToWrapEnd = lineToWrap + LinesOnScreen() + 100;
1520 const int lineEndNeedWrap = std::min(wrapPending.end, pdoc->LinesTotal());
1521 lineToWrapEnd = std::min(lineToWrapEnd, lineEndNeedWrap);
1523 // Ensure all lines being wrapped are styled.
1524 pdoc->EnsureStyledTo(pdoc->LineStart(lineToWrapEnd));
1526 if (lineToWrap < lineToWrapEnd) {
1528 PRectangle rcTextArea = GetClientRectangle();
1529 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1530 rcTextArea.right -= vs.rightMarginWidth;
1531 wrapWidth = static_cast<int>(rcTextArea.Width());
1532 RefreshStyleData();
1533 AutoSurface surface(this);
1534 if (surface) {
1535 //Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
1537 while (lineToWrap < lineToWrapEnd) {
1538 if (WrapOneLine(surface, lineToWrap)) {
1539 wrapOccurred = true;
1541 wrapPending.Wrapped(lineToWrap);
1542 lineToWrap++;
1545 goodTopLine = cs.DisplayFromDoc(lineDocTop) + std::min(subLineTop, cs.GetHeight(lineDocTop)-1);
1549 // If wrapping is done, bring it to resting position
1550 if (wrapPending.start >= lineEndNeedWrap) {
1551 wrapPending.Reset();
1555 if (wrapOccurred) {
1556 SetScrollBars();
1557 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1558 SetVerticalScrollPos();
1561 return wrapOccurred;
1564 void Editor::LinesJoin() {
1565 if (!RangeContainsProtected(targetStart, targetEnd)) {
1566 UndoGroup ug(pdoc);
1567 bool prevNonWS = true;
1568 for (int pos = targetStart; pos < targetEnd; pos++) {
1569 if (pdoc->IsPositionInLineEnd(pos)) {
1570 targetEnd -= pdoc->LenChar(pos);
1571 pdoc->DelChar(pos);
1572 if (prevNonWS) {
1573 // Ensure at least one space separating previous lines
1574 const int lengthInserted = pdoc->InsertString(pos, " ", 1);
1575 targetEnd += lengthInserted;
1577 } else {
1578 prevNonWS = pdoc->CharAt(pos) != ' ';
1584 const char *Editor::StringFromEOLMode(int eolMode) {
1585 if (eolMode == SC_EOL_CRLF) {
1586 return "\r\n";
1587 } else if (eolMode == SC_EOL_CR) {
1588 return "\r";
1589 } else {
1590 return "\n";
1594 void Editor::LinesSplit(int pixelWidth) {
1595 if (!RangeContainsProtected(targetStart, targetEnd)) {
1596 if (pixelWidth == 0) {
1597 PRectangle rcText = GetTextRectangle();
1598 pixelWidth = static_cast<int>(rcText.Width());
1600 int lineStart = pdoc->LineFromPosition(targetStart);
1601 int lineEnd = pdoc->LineFromPosition(targetEnd);
1602 const char *eol = StringFromEOLMode(pdoc->eolMode);
1603 UndoGroup ug(pdoc);
1604 for (int line = lineStart; line <= lineEnd; line++) {
1605 AutoSurface surface(this);
1606 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
1607 if (surface && ll) {
1608 unsigned int posLineStart = pdoc->LineStart(line);
1609 view.LayoutLine(*this, line, surface, vs, ll, pixelWidth);
1610 int lengthInsertedTotal = 0;
1611 for (int subLine = 1; subLine < ll->lines; subLine++) {
1612 const int lengthInserted = pdoc->InsertString(
1613 static_cast<int>(posLineStart + lengthInsertedTotal +
1614 ll->LineStart(subLine)),
1615 eol, istrlen(eol));
1616 targetEnd += lengthInserted;
1617 lengthInsertedTotal += lengthInserted;
1620 lineEnd = pdoc->LineFromPosition(targetEnd);
1625 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1626 if (vs.fixedColumnWidth == 0)
1627 return;
1629 AllocateGraphics();
1630 RefreshStyleData();
1631 RefreshPixMaps(surfWindow);
1633 // On GTK+ with Ubuntu overlay scroll bars, the surface may have been finished
1634 // at this point. The Initialised call checks for this case and sets the status
1635 // to be bad which avoids crashes in following calls.
1636 if (!surfWindow->Initialised()) {
1637 return;
1640 PRectangle rcMargin = GetClientRectangle();
1641 Point ptOrigin = GetVisibleOriginInMain();
1642 rcMargin.Move(0, -ptOrigin.y);
1643 rcMargin.left = 0;
1644 rcMargin.right = static_cast<XYPOSITION>(vs.fixedColumnWidth);
1646 if (!rc.Intersects(rcMargin))
1647 return;
1649 Surface *surface;
1650 if (view.bufferedDraw) {
1651 surface = marginView.pixmapSelMargin;
1652 } else {
1653 surface = surfWindow;
1656 // Clip vertically to paint area to avoid drawing line numbers
1657 if (rcMargin.bottom > rc.bottom)
1658 rcMargin.bottom = rc.bottom;
1659 if (rcMargin.top < rc.top)
1660 rcMargin.top = rc.top;
1662 marginView.PaintMargin(surface, topLine, rc, rcMargin, *this, vs);
1664 if (view.bufferedDraw) {
1665 surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *marginView.pixmapSelMargin);
1669 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
1670 view.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1671 marginView.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1672 if (view.bufferedDraw) {
1673 PRectangle rcClient = GetClientRectangle();
1674 if (!view.pixmapLine->Initialised()) {
1676 view.pixmapLine->InitPixMap(static_cast<int>(rcClient.Width()), vs.lineHeight,
1677 surfaceWindow, wMain.GetID());
1679 if (!marginView.pixmapSelMargin->Initialised()) {
1680 marginView.pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
1681 static_cast<int>(rcClient.Height()), surfaceWindow, wMain.GetID());
1686 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
1687 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
1688 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
1689 AllocateGraphics();
1691 RefreshStyleData();
1692 if (paintState == paintAbandoned)
1693 return; // Scroll bars may have changed so need redraw
1694 RefreshPixMaps(surfaceWindow);
1696 paintAbandonedByStyling = false;
1698 StyleAreaBounded(rcArea, false);
1700 PRectangle rcClient = GetClientRectangle();
1701 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
1702 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1704 if (NotifyUpdateUI()) {
1705 RefreshStyleData();
1706 RefreshPixMaps(surfaceWindow);
1709 // Wrap the visible lines if needed.
1710 if (WrapLines(wsVisible)) {
1711 // The wrapping process has changed the height of some lines so
1712 // abandon this paint for a complete repaint.
1713 if (AbandonPaint()) {
1714 return;
1716 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
1718 PLATFORM_ASSERT(marginView.pixmapSelPattern->Initialised());
1720 if (!view.bufferedDraw)
1721 surfaceWindow->SetClip(rcArea);
1723 if (paintState != paintAbandoned) {
1724 if (vs.marginInside) {
1725 PaintSelMargin(surfaceWindow, rcArea);
1726 PRectangle rcRightMargin = rcClient;
1727 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
1728 if (rcArea.Intersects(rcRightMargin)) {
1729 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
1731 } else { // Else separate view so separate paint event but leftMargin included to allow overlap
1732 PRectangle rcLeftMargin = rcArea;
1733 rcLeftMargin.left = 0;
1734 rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
1735 if (rcArea.Intersects(rcLeftMargin)) {
1736 surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[STYLE_DEFAULT].back);
1741 if (paintState == paintAbandoned) {
1742 // Either styling or NotifyUpdateUI noticed that painting is needed
1743 // outside the current painting rectangle
1744 //Platform::DebugPrintf("Abandoning paint\n");
1745 if (Wrapping()) {
1746 if (paintAbandonedByStyling) {
1747 // Styling has spilled over a line end, such as occurs by starting a multiline
1748 // comment. The width of subsequent text may have changed, so rewrap.
1749 NeedWrapping(cs.DocFromDisplay(topLine));
1752 return;
1755 view.PaintText(surfaceWindow, *this, rcArea, rcClient, vs);
1757 if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) {
1758 if (FineTickerAvailable()) {
1759 scrollWidth = view.lineWidthMaxSeen;
1760 if (!FineTickerRunning(tickWiden)) {
1761 FineTickerStart(tickWiden, 50, 5);
1766 NotifyPainted();
1769 // This is mostly copied from the Paint method but with some things omitted
1770 // such as the margin markers, line numbers, selection and caret
1771 // Should be merged back into a combined Draw method.
1772 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
1773 if (!pfr)
1774 return 0;
1776 AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
1777 if (!surface)
1778 return 0;
1779 AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
1780 if (!surfaceMeasure) {
1781 return 0;
1783 return view.FormatRange(draw, pfr, surface, surfaceMeasure, *this, vs);
1786 int Editor::TextWidth(int style, const char *text) {
1787 RefreshStyleData();
1788 AutoSurface surface(this);
1789 if (surface) {
1790 return static_cast<int>(surface->WidthText(vs.styles[style].font, text, istrlen(text)));
1791 } else {
1792 return 1;
1796 // Empty method is overridden on GTK+ to show / hide scrollbars
1797 void Editor::ReconfigureScrollBars() {}
1799 void Editor::SetScrollBars() {
1800 RefreshStyleData();
1802 int nMax = MaxScrollPos();
1803 int nPage = LinesOnScreen();
1804 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
1805 if (modified) {
1806 DwellEnd(true);
1809 // TODO: ensure always showing as many lines as possible
1810 // May not be, if, for example, window made larger
1811 if (topLine > MaxScrollPos()) {
1812 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
1813 SetVerticalScrollPos();
1814 Redraw();
1816 if (modified) {
1817 if (!AbandonPaint())
1818 Redraw();
1820 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
1823 void Editor::ChangeSize() {
1824 DropGraphics(false);
1825 SetScrollBars();
1826 if (Wrapping()) {
1827 PRectangle rcTextArea = GetClientRectangle();
1828 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1829 rcTextArea.right -= vs.rightMarginWidth;
1830 if (wrapWidth != rcTextArea.Width()) {
1831 NeedWrapping();
1832 Redraw();
1837 int Editor::InsertSpace(int position, unsigned int spaces) {
1838 if (spaces > 0) {
1839 std::string spaceText(spaces, ' ');
1840 const int lengthInserted = pdoc->InsertString(position, spaceText.c_str(), spaces);
1841 position += lengthInserted;
1843 return position;
1846 void Editor::AddChar(char ch) {
1847 char s[2];
1848 s[0] = ch;
1849 s[1] = '\0';
1850 AddCharUTF(s, 1);
1853 void Editor::FilterSelections() {
1854 if (!additionalSelectionTyping && (sel.Count() > 1)) {
1855 InvalidateWholeSelection();
1856 sel.DropAdditionalRanges();
1860 static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
1861 return *a < *b;
1864 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
1865 void Editor::AddCharUTF(const char *s, unsigned int len, bool treatAsDBCS) {
1866 FilterSelections();
1868 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
1870 // Vector elements point into selection in order to change selection.
1871 std::vector<SelectionRange *> selPtrs;
1872 for (size_t r = 0; r < sel.Count(); r++) {
1873 selPtrs.push_back(&sel.Range(r));
1875 // Order selections by position in document.
1876 std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
1878 // Loop in reverse to avoid disturbing positions of selections yet to be processed.
1879 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
1880 rit != selPtrs.rend(); ++rit) {
1881 SelectionRange *currentSel = *rit;
1882 if (!RangeContainsProtected(currentSel->Start().Position(),
1883 currentSel->End().Position())) {
1884 int positionInsert = currentSel->Start().Position();
1885 if (!currentSel->Empty()) {
1886 if (currentSel->Length()) {
1887 pdoc->DeleteChars(positionInsert, currentSel->Length());
1888 currentSel->ClearVirtualSpace();
1889 } else {
1890 // Range is all virtual so collapse to start of virtual space
1891 currentSel->MinimizeVirtualSpace();
1893 } else if (inOverstrike) {
1894 if (positionInsert < pdoc->Length()) {
1895 if (!pdoc->IsPositionInLineEnd(positionInsert)) {
1896 pdoc->DelChar(positionInsert);
1897 currentSel->ClearVirtualSpace();
1901 positionInsert = InsertSpace(positionInsert, currentSel->caret.VirtualSpace());
1902 const int lengthInserted = pdoc->InsertString(positionInsert, s, len);
1903 if (lengthInserted > 0) {
1904 currentSel->caret.SetPosition(positionInsert + lengthInserted);
1905 currentSel->anchor.SetPosition(positionInsert + lengthInserted);
1907 currentSel->ClearVirtualSpace();
1908 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1909 if (Wrapping()) {
1910 AutoSurface surface(this);
1911 if (surface) {
1912 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
1913 SetScrollBars();
1914 SetVerticalScrollPos();
1915 Redraw();
1922 if (Wrapping()) {
1923 SetScrollBars();
1925 ThinRectangularRange();
1926 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1927 EnsureCaretVisible();
1928 // Avoid blinking during rapid typing:
1929 ShowCaretAtCurrentPosition();
1930 if ((caretSticky == SC_CARETSTICKY_OFF) ||
1931 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
1932 SetLastXChosen();
1935 if (treatAsDBCS) {
1936 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
1937 static_cast<unsigned char>(s[1]));
1938 } else if (len > 0) {
1939 int byte = static_cast<unsigned char>(s[0]);
1940 if ((byte < 0xC0) || (1 == len)) {
1941 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
1942 // characters when not in UTF-8 mode.
1943 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
1944 // characters representing themselves.
1945 } else {
1946 unsigned int utf32[1] = { 0 };
1947 UTF32FromUTF8(s, len, utf32, ELEMENTS(utf32));
1948 byte = utf32[0];
1950 NotifyChar(byte);
1953 if (recordingMacro) {
1954 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
1958 void Editor::ClearBeforeTentativeStart() {
1959 // Make positions for the first composition string.
1960 FilterSelections();
1961 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
1962 for (size_t r = 0; r<sel.Count(); r++) {
1963 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
1964 sel.Range(r).End().Position())) {
1965 int positionInsert = sel.Range(r).Start().Position();
1966 if (!sel.Range(r).Empty()) {
1967 if (sel.Range(r).Length()) {
1968 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
1969 sel.Range(r).ClearVirtualSpace();
1970 } else {
1971 // Range is all virtual so collapse to start of virtual space
1972 sel.Range(r).MinimizeVirtualSpace();
1975 InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
1976 sel.Range(r).ClearVirtualSpace();
1981 void Editor::InsertPaste(const char *text, int len) {
1982 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
1983 SelectionPosition selStart = sel.Start();
1984 selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
1985 const int lengthInserted = pdoc->InsertString(selStart.Position(), text, len);
1986 if (lengthInserted > 0) {
1987 SetEmptySelection(selStart.Position() + lengthInserted);
1989 } else {
1990 // SC_MULTIPASTE_EACH
1991 for (size_t r=0; r<sel.Count(); r++) {
1992 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
1993 sel.Range(r).End().Position())) {
1994 int positionInsert = sel.Range(r).Start().Position();
1995 if (!sel.Range(r).Empty()) {
1996 if (sel.Range(r).Length()) {
1997 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
1998 sel.Range(r).ClearVirtualSpace();
1999 } else {
2000 // Range is all virtual so collapse to start of virtual space
2001 sel.Range(r).MinimizeVirtualSpace();
2004 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
2005 const int lengthInserted = pdoc->InsertString(positionInsert, text, len);
2006 if (lengthInserted > 0) {
2007 sel.Range(r).caret.SetPosition(positionInsert + lengthInserted);
2008 sel.Range(r).anchor.SetPosition(positionInsert + lengthInserted);
2010 sel.Range(r).ClearVirtualSpace();
2016 void Editor::InsertPasteShape(const char *text, int len, PasteShape shape) {
2017 std::string convertedText;
2018 if (convertPastes) {
2019 // Convert line endings of the paste into our local line-endings mode
2020 convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
2021 len = static_cast<int>(convertedText.length());
2022 text = convertedText.c_str();
2024 if (shape == pasteRectangular) {
2025 PasteRectangular(sel.Start(), text, len);
2026 } else {
2027 if (shape == pasteLine) {
2028 int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
2029 int lengthInserted = pdoc->InsertString(insertPos, text, len);
2030 // add the newline if necessary
2031 if ((len > 0) && (text[len - 1] != '\n' && text[len - 1] != '\r')) {
2032 const char *endline = StringFromEOLMode(pdoc->eolMode);
2033 int length = static_cast<int>(strlen(endline));
2034 lengthInserted += pdoc->InsertString(insertPos + lengthInserted, endline, length);
2036 if (sel.MainCaret() == insertPos) {
2037 SetEmptySelection(sel.MainCaret() + lengthInserted);
2039 } else {
2040 InsertPaste(text, len);
2045 void Editor::ClearSelection(bool retainMultipleSelections) {
2046 if (!sel.IsRectangular() && !retainMultipleSelections)
2047 FilterSelections();
2048 UndoGroup ug(pdoc);
2049 for (size_t r=0; r<sel.Count(); r++) {
2050 if (!sel.Range(r).Empty()) {
2051 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
2052 sel.Range(r).End().Position())) {
2053 pdoc->DeleteChars(sel.Range(r).Start().Position(),
2054 sel.Range(r).Length());
2055 sel.Range(r) = SelectionRange(sel.Range(r).Start());
2059 ThinRectangularRange();
2060 sel.RemoveDuplicates();
2061 ClaimSelection();
2062 SetHoverIndicatorPosition(sel.MainCaret());
2065 void Editor::ClearAll() {
2067 UndoGroup ug(pdoc);
2068 if (0 != pdoc->Length()) {
2069 pdoc->DeleteChars(0, pdoc->Length());
2071 if (!pdoc->IsReadOnly()) {
2072 cs.Clear();
2073 pdoc->AnnotationClearAll();
2074 pdoc->MarginClearAll();
2078 view.ClearAllTabstops();
2080 sel.Clear();
2081 SetTopLine(0);
2082 SetVerticalScrollPos();
2083 InvalidateStyleRedraw();
2086 void Editor::ClearDocumentStyle() {
2087 Decoration *deco = pdoc->decorations.root;
2088 while (deco) {
2089 // Save next in case deco deleted
2090 Decoration *decoNext = deco->next;
2091 if (deco->indicator < INDIC_CONTAINER) {
2092 pdoc->decorations.SetCurrentIndicator(deco->indicator);
2093 pdoc->DecorationFillRange(0, 0, pdoc->Length());
2095 deco = decoNext;
2097 pdoc->StartStyling(0, '\377');
2098 pdoc->SetStyleFor(pdoc->Length(), 0);
2099 cs.ShowAll();
2100 SetAnnotationHeights(0, pdoc->LinesTotal());
2101 pdoc->ClearLevels();
2104 void Editor::CopyAllowLine() {
2105 SelectionText selectedText;
2106 CopySelectionRange(&selectedText, true);
2107 CopyToClipboard(selectedText);
2110 void Editor::Cut() {
2111 pdoc->CheckReadOnly();
2112 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
2113 Copy();
2114 ClearSelection();
2118 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
2119 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
2120 return;
2122 sel.Clear();
2123 sel.RangeMain() = SelectionRange(pos);
2124 int line = pdoc->LineFromPosition(sel.MainCaret());
2125 UndoGroup ug(pdoc);
2126 sel.RangeMain().caret = SelectionPosition(
2127 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
2128 int xInsert = XFromPosition(sel.RangeMain().caret);
2129 bool prevCr = false;
2130 while ((len > 0) && IsEOLChar(ptr[len-1]))
2131 len--;
2132 for (int i = 0; i < len; i++) {
2133 if (IsEOLChar(ptr[i])) {
2134 if ((ptr[i] == '\r') || (!prevCr))
2135 line++;
2136 if (line >= pdoc->LinesTotal()) {
2137 if (pdoc->eolMode != SC_EOL_LF)
2138 pdoc->InsertString(pdoc->Length(), "\r", 1);
2139 if (pdoc->eolMode != SC_EOL_CR)
2140 pdoc->InsertString(pdoc->Length(), "\n", 1);
2142 // Pad the end of lines with spaces if required
2143 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
2144 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
2145 while (XFromPosition(sel.MainCaret()) < xInsert) {
2146 assert(pdoc);
2147 const int lengthInserted = pdoc->InsertString(sel.MainCaret(), " ", 1);
2148 sel.RangeMain().caret.Add(lengthInserted);
2151 prevCr = ptr[i] == '\r';
2152 } else {
2153 const int lengthInserted = pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
2154 sel.RangeMain().caret.Add(lengthInserted);
2155 prevCr = false;
2158 SetEmptySelection(pos);
2161 bool Editor::CanPaste() {
2162 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
2165 void Editor::Clear() {
2166 // If multiple selections, don't delete EOLS
2167 if (sel.Empty()) {
2168 bool singleVirtual = false;
2169 if ((sel.Count() == 1) &&
2170 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
2171 sel.RangeMain().Start().VirtualSpace()) {
2172 singleVirtual = true;
2174 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
2175 for (size_t r=0; r<sel.Count(); r++) {
2176 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
2177 if (sel.Range(r).Start().VirtualSpace()) {
2178 if (sel.Range(r).anchor < sel.Range(r).caret)
2179 sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
2180 else
2181 sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
2183 if ((sel.Count() == 1) || !pdoc->IsPositionInLineEnd(sel.Range(r).caret.Position())) {
2184 pdoc->DelChar(sel.Range(r).caret.Position());
2185 sel.Range(r).ClearVirtualSpace();
2186 } // else multiple selection so don't eat line ends
2187 } else {
2188 sel.Range(r).ClearVirtualSpace();
2191 } else {
2192 ClearSelection();
2194 sel.RemoveDuplicates();
2195 ShowCaretAtCurrentPosition(); // Avoid blinking
2198 void Editor::SelectAll() {
2199 sel.Clear();
2200 SetSelection(0, pdoc->Length());
2201 Redraw();
2204 void Editor::Undo() {
2205 if (pdoc->CanUndo()) {
2206 InvalidateCaret();
2207 int newPos = pdoc->Undo();
2208 if (newPos >= 0)
2209 SetEmptySelection(newPos);
2210 EnsureCaretVisible();
2214 void Editor::Redo() {
2215 if (pdoc->CanRedo()) {
2216 int newPos = pdoc->Redo();
2217 if (newPos >= 0)
2218 SetEmptySelection(newPos);
2219 EnsureCaretVisible();
2223 void Editor::DelCharBack(bool allowLineStartDeletion) {
2224 RefreshStyleData();
2225 if (!sel.IsRectangular())
2226 FilterSelections();
2227 if (sel.IsRectangular())
2228 allowLineStartDeletion = false;
2229 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
2230 if (sel.Empty()) {
2231 for (size_t r=0; r<sel.Count(); r++) {
2232 if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
2233 if (sel.Range(r).caret.VirtualSpace()) {
2234 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
2235 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
2236 } else {
2237 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
2238 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
2239 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
2240 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
2241 UndoGroup ugInner(pdoc, !ug.Needed());
2242 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
2243 int indentationStep = pdoc->IndentSize();
2244 int indentationChange = indentation % indentationStep;
2245 if (indentationChange == 0)
2246 indentationChange = indentationStep;
2247 const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationChange);
2248 // SetEmptySelection
2249 sel.Range(r) = SelectionRange(posSelect);
2250 } else {
2251 pdoc->DelCharBack(sel.Range(r).caret.Position());
2255 } else {
2256 sel.Range(r).ClearVirtualSpace();
2259 ThinRectangularRange();
2260 } else {
2261 ClearSelection();
2263 sel.RemoveDuplicates();
2264 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
2265 // Avoid blinking during rapid typing:
2266 ShowCaretAtCurrentPosition();
2269 int Editor::ModifierFlags(bool shift, bool ctrl, bool alt, bool meta, bool super) {
2270 return
2271 (shift ? SCI_SHIFT : 0) |
2272 (ctrl ? SCI_CTRL : 0) |
2273 (alt ? SCI_ALT : 0) |
2274 (meta ? SCI_META : 0) |
2275 (super ? SCI_SUPER : 0);
2278 void Editor::NotifyFocus(bool focus) {
2279 SCNotification scn = {};
2280 scn.nmhdr.code = focus ? SCN_FOCUSIN : SCN_FOCUSOUT;
2281 NotifyParent(scn);
2284 void Editor::SetCtrlID(int identifier) {
2285 ctrlID = identifier;
2288 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
2289 SCNotification scn = {};
2290 scn.nmhdr.code = SCN_STYLENEEDED;
2291 scn.position = endStyleNeeded;
2292 NotifyParent(scn);
2295 void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
2296 NotifyStyleToNeeded(endStyleNeeded);
2299 void Editor::NotifyLexerChanged(Document *, void *) {
2302 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
2303 errorStatus = status;
2306 void Editor::NotifyChar(int ch) {
2307 SCNotification scn = {};
2308 scn.nmhdr.code = SCN_CHARADDED;
2309 scn.ch = ch;
2310 NotifyParent(scn);
2313 void Editor::NotifySavePoint(bool isSavePoint) {
2314 SCNotification scn = {};
2315 if (isSavePoint) {
2316 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
2317 } else {
2318 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
2320 NotifyParent(scn);
2323 void Editor::NotifyModifyAttempt() {
2324 SCNotification scn = {};
2325 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
2326 NotifyParent(scn);
2329 void Editor::NotifyDoubleClick(Point pt, int modifiers) {
2330 SCNotification scn = {};
2331 scn.nmhdr.code = SCN_DOUBLECLICK;
2332 scn.line = LineFromLocation(pt);
2333 scn.position = PositionFromLocation(pt, true);
2334 scn.modifiers = modifiers;
2335 NotifyParent(scn);
2338 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
2339 NotifyDoubleClick(pt, ModifierFlags(shift, ctrl, alt));
2342 void Editor::NotifyHotSpotDoubleClicked(int position, int modifiers) {
2343 SCNotification scn = {};
2344 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
2345 scn.position = position;
2346 scn.modifiers = modifiers;
2347 NotifyParent(scn);
2350 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
2351 NotifyHotSpotDoubleClicked(position, ModifierFlags(shift, ctrl, alt));
2354 void Editor::NotifyHotSpotClicked(int position, int modifiers) {
2355 SCNotification scn = {};
2356 scn.nmhdr.code = SCN_HOTSPOTCLICK;
2357 scn.position = position;
2358 scn.modifiers = modifiers;
2359 NotifyParent(scn);
2362 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
2363 NotifyHotSpotClicked(position, ModifierFlags(shift, ctrl, alt));
2366 void Editor::NotifyHotSpotReleaseClick(int position, int modifiers) {
2367 SCNotification scn = {};
2368 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
2369 scn.position = position;
2370 scn.modifiers = modifiers;
2371 NotifyParent(scn);
2374 void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
2375 NotifyHotSpotReleaseClick(position, ModifierFlags(shift, ctrl, alt));
2378 bool Editor::NotifyUpdateUI() {
2379 if (needUpdateUI) {
2380 SCNotification scn = {};
2381 scn.nmhdr.code = SCN_UPDATEUI;
2382 scn.updated = needUpdateUI;
2383 NotifyParent(scn);
2384 needUpdateUI = 0;
2385 return true;
2387 return false;
2390 void Editor::NotifyPainted() {
2391 SCNotification scn = {};
2392 scn.nmhdr.code = SCN_PAINTED;
2393 NotifyParent(scn);
2396 void Editor::NotifyIndicatorClick(bool click, int position, int modifiers) {
2397 int mask = pdoc->decorations.AllOnFor(position);
2398 if ((click && mask) || pdoc->decorations.clickNotified) {
2399 SCNotification scn = {};
2400 pdoc->decorations.clickNotified = click;
2401 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
2402 scn.modifiers = modifiers;
2403 scn.position = position;
2404 NotifyParent(scn);
2408 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
2409 NotifyIndicatorClick(click, position, ModifierFlags(shift, ctrl, alt));
2412 bool Editor::NotifyMarginClick(Point pt, int modifiers) {
2413 int marginClicked = -1;
2414 int x = vs.textStart - vs.fixedColumnWidth;
2415 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
2416 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
2417 marginClicked = margin;
2418 x += vs.ms[margin].width;
2420 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
2421 int position = pdoc->LineStart(LineFromLocation(pt));
2422 if ((vs.ms[marginClicked].mask & SC_MASK_FOLDERS) && (foldAutomatic & SC_AUTOMATICFOLD_CLICK)) {
2423 const bool ctrl = (modifiers & SCI_CTRL) != 0;
2424 const bool shift = (modifiers & SCI_SHIFT) != 0;
2425 int lineClick = pdoc->LineFromPosition(position);
2426 if (shift && ctrl) {
2427 FoldAll(SC_FOLDACTION_TOGGLE);
2428 } else {
2429 int levelClick = pdoc->GetLevel(lineClick);
2430 if (levelClick & SC_FOLDLEVELHEADERFLAG) {
2431 if (shift) {
2432 // Ensure all children visible
2433 FoldExpand(lineClick, SC_FOLDACTION_EXPAND, levelClick);
2434 } else if (ctrl) {
2435 FoldExpand(lineClick, SC_FOLDACTION_TOGGLE, levelClick);
2436 } else {
2437 // Toggle this line
2438 FoldLine(lineClick, SC_FOLDACTION_TOGGLE);
2442 return true;
2444 SCNotification scn = {};
2445 scn.nmhdr.code = SCN_MARGINCLICK;
2446 scn.modifiers = modifiers;
2447 scn.position = position;
2448 scn.margin = marginClicked;
2449 NotifyParent(scn);
2450 return true;
2451 } else {
2452 return false;
2456 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
2457 return NotifyMarginClick(pt, ModifierFlags(shift, ctrl, alt));
2460 void Editor::NotifyNeedShown(int pos, int len) {
2461 SCNotification scn = {};
2462 scn.nmhdr.code = SCN_NEEDSHOWN;
2463 scn.position = pos;
2464 scn.length = len;
2465 NotifyParent(scn);
2468 void Editor::NotifyDwelling(Point pt, bool state) {
2469 SCNotification scn = {};
2470 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
2471 scn.position = PositionFromLocation(pt, true);
2472 scn.x = static_cast<int>(pt.x + vs.ExternalMarginWidth());
2473 scn.y = static_cast<int>(pt.y);
2474 NotifyParent(scn);
2477 void Editor::NotifyZoom() {
2478 SCNotification scn = {};
2479 scn.nmhdr.code = SCN_ZOOM;
2480 NotifyParent(scn);
2483 // Notifications from document
2484 void Editor::NotifyModifyAttempt(Document *, void *) {
2485 //Platform::DebugPrintf("** Modify Attempt\n");
2486 NotifyModifyAttempt();
2489 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
2490 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
2491 NotifySavePoint(atSavePoint);
2494 void Editor::CheckModificationForWrap(DocModification mh) {
2495 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
2496 view.llc.Invalidate(LineLayout::llCheckTextAndStyle);
2497 int lineDoc = pdoc->LineFromPosition(mh.position);
2498 int lines = Platform::Maximum(0, mh.linesAdded);
2499 if (Wrapping()) {
2500 NeedWrapping(lineDoc, lineDoc + lines + 1);
2502 RefreshStyleData();
2503 // Fix up annotation heights
2504 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
2508 // Move a position so it is still after the same character as before the insertion.
2509 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
2510 if (position > startInsertion) {
2511 return position + length;
2513 return position;
2516 // Move a position so it is still after the same character as before the deletion if that
2517 // character is still present else after the previous surviving character.
2518 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
2519 if (position > startDeletion) {
2520 int endDeletion = startDeletion + length;
2521 if (position > endDeletion) {
2522 return position - length;
2523 } else {
2524 return startDeletion;
2526 } else {
2527 return position;
2531 void Editor::NotifyModified(Document *, DocModification mh, void *) {
2532 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
2533 if (paintState == painting) {
2534 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
2536 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
2537 if (paintState == painting) {
2538 CheckForChangeOutsidePaint(
2539 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
2540 } else {
2541 // Could check that change is before last visible line.
2542 Redraw();
2545 if (mh.modificationType & SC_MOD_CHANGETABSTOPS) {
2546 Redraw();
2548 if (mh.modificationType & SC_MOD_LEXERSTATE) {
2549 if (paintState == painting) {
2550 CheckForChangeOutsidePaint(
2551 Range(mh.position, mh.position + mh.length));
2552 } else {
2553 Redraw();
2556 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
2557 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
2558 pdoc->IncrementStyleClock();
2560 if (paintState == notPainting) {
2561 if (mh.position < pdoc->LineStart(topLine)) {
2562 // Styling performed before this view
2563 Redraw();
2564 } else {
2565 InvalidateRange(mh.position, mh.position + mh.length);
2568 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
2569 view.llc.Invalidate(LineLayout::llCheckTextAndStyle);
2571 } else {
2572 // Move selection and brace highlights
2573 if (mh.modificationType & SC_MOD_INSERTTEXT) {
2574 sel.MovePositions(true, mh.position, mh.length);
2575 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
2576 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
2577 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
2578 sel.MovePositions(false, mh.position, mh.length);
2579 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
2580 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
2582 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && cs.HiddenLines()) {
2583 // Some lines are hidden so may need shown.
2584 const int lineOfPos = pdoc->LineFromPosition(mh.position);
2585 int endNeedShown = mh.position;
2586 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
2587 if (pdoc->ContainsLineEnd(mh.text, mh.length) && (mh.position != pdoc->LineStart(lineOfPos)))
2588 endNeedShown = pdoc->LineStart(lineOfPos+1);
2589 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
2590 // Extend the need shown area over any folded lines
2591 endNeedShown = mh.position + mh.length;
2592 int lineLast = pdoc->LineFromPosition(mh.position+mh.length);
2593 for (int line = lineOfPos; line <= lineLast; line++) {
2594 const int lineMaxSubord = pdoc->GetLastChild(line, -1, -1);
2595 if (lineLast < lineMaxSubord) {
2596 lineLast = lineMaxSubord;
2597 endNeedShown = pdoc->LineEnd(lineLast);
2601 NeedShown(mh.position, endNeedShown - mh.position);
2603 if (mh.linesAdded != 0) {
2604 // Update contraction state for inserted and removed lines
2605 // lineOfPos should be calculated in context of state before modification, shouldn't it
2606 int lineOfPos = pdoc->LineFromPosition(mh.position);
2607 if (mh.position > pdoc->LineStart(lineOfPos))
2608 lineOfPos++; // Affecting subsequent lines
2609 if (mh.linesAdded > 0) {
2610 cs.InsertLines(lineOfPos, mh.linesAdded);
2611 } else {
2612 cs.DeleteLines(lineOfPos, -mh.linesAdded);
2614 view.LinesAddedOrRemoved(lineOfPos, mh.linesAdded);
2616 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
2617 int lineDoc = pdoc->LineFromPosition(mh.position);
2618 if (vs.annotationVisible) {
2619 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
2620 Redraw();
2623 CheckModificationForWrap(mh);
2624 if (mh.linesAdded != 0) {
2625 // Avoid scrolling of display if change before current display
2626 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
2627 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
2628 if (newTop != topLine) {
2629 SetTopLine(newTop);
2630 SetVerticalScrollPos();
2634 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
2635 QueueIdleWork(WorkNeeded::workStyle, pdoc->Length());
2636 Redraw();
2638 } else {
2639 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
2640 QueueIdleWork(WorkNeeded::workStyle, mh.position + mh.length);
2641 InvalidateRange(mh.position, mh.position + mh.length);
2646 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
2647 SetScrollBars();
2650 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
2651 if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) {
2652 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
2653 // Fold changes can affect the drawing of following lines so redraw whole margin
2654 RedrawSelMargin(marginView.highlightDelimiter.isEnabled ? -1 : mh.line - 1, true);
2655 } else {
2656 RedrawSelMargin(mh.line);
2660 if ((mh.modificationType & SC_MOD_CHANGEFOLD) && (foldAutomatic & SC_AUTOMATICFOLD_CHANGE)) {
2661 FoldChanged(mh.line, mh.foldLevelNow, mh.foldLevelPrev);
2664 // NOW pay the piper WRT "deferred" visual updates
2665 if (IsLastStep(mh)) {
2666 SetScrollBars();
2667 Redraw();
2670 // If client wants to see this modification
2671 if (mh.modificationType & modEventMask) {
2672 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
2673 // Real modification made to text of document.
2674 NotifyChange(); // Send EN_CHANGE
2677 SCNotification scn = {};
2678 scn.nmhdr.code = SCN_MODIFIED;
2679 scn.position = mh.position;
2680 scn.modificationType = mh.modificationType;
2681 scn.text = mh.text;
2682 scn.length = mh.length;
2683 scn.linesAdded = mh.linesAdded;
2684 scn.line = mh.line;
2685 scn.foldLevelNow = mh.foldLevelNow;
2686 scn.foldLevelPrev = mh.foldLevelPrev;
2687 scn.token = mh.token;
2688 scn.annotationLinesAdded = mh.annotationLinesAdded;
2689 NotifyParent(scn);
2693 void Editor::NotifyDeleted(Document *, void *) {
2694 /* Do nothing */
2697 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2699 // Enumerates all macroable messages
2700 switch (iMessage) {
2701 case SCI_CUT:
2702 case SCI_COPY:
2703 case SCI_PASTE:
2704 case SCI_CLEAR:
2705 case SCI_REPLACESEL:
2706 case SCI_ADDTEXT:
2707 case SCI_INSERTTEXT:
2708 case SCI_APPENDTEXT:
2709 case SCI_CLEARALL:
2710 case SCI_SELECTALL:
2711 case SCI_GOTOLINE:
2712 case SCI_GOTOPOS:
2713 case SCI_SEARCHANCHOR:
2714 case SCI_SEARCHNEXT:
2715 case SCI_SEARCHPREV:
2716 case SCI_LINEDOWN:
2717 case SCI_LINEDOWNEXTEND:
2718 case SCI_PARADOWN:
2719 case SCI_PARADOWNEXTEND:
2720 case SCI_LINEUP:
2721 case SCI_LINEUPEXTEND:
2722 case SCI_PARAUP:
2723 case SCI_PARAUPEXTEND:
2724 case SCI_CHARLEFT:
2725 case SCI_CHARLEFTEXTEND:
2726 case SCI_CHARRIGHT:
2727 case SCI_CHARRIGHTEXTEND:
2728 case SCI_WORDLEFT:
2729 case SCI_WORDLEFTEXTEND:
2730 case SCI_WORDRIGHT:
2731 case SCI_WORDRIGHTEXTEND:
2732 case SCI_WORDPARTLEFT:
2733 case SCI_WORDPARTLEFTEXTEND:
2734 case SCI_WORDPARTRIGHT:
2735 case SCI_WORDPARTRIGHTEXTEND:
2736 case SCI_WORDLEFTEND:
2737 case SCI_WORDLEFTENDEXTEND:
2738 case SCI_WORDRIGHTEND:
2739 case SCI_WORDRIGHTENDEXTEND:
2740 case SCI_HOME:
2741 case SCI_HOMEEXTEND:
2742 case SCI_LINEEND:
2743 case SCI_LINEENDEXTEND:
2744 case SCI_HOMEWRAP:
2745 case SCI_HOMEWRAPEXTEND:
2746 case SCI_LINEENDWRAP:
2747 case SCI_LINEENDWRAPEXTEND:
2748 case SCI_DOCUMENTSTART:
2749 case SCI_DOCUMENTSTARTEXTEND:
2750 case SCI_DOCUMENTEND:
2751 case SCI_DOCUMENTENDEXTEND:
2752 case SCI_STUTTEREDPAGEUP:
2753 case SCI_STUTTEREDPAGEUPEXTEND:
2754 case SCI_STUTTEREDPAGEDOWN:
2755 case SCI_STUTTEREDPAGEDOWNEXTEND:
2756 case SCI_PAGEUP:
2757 case SCI_PAGEUPEXTEND:
2758 case SCI_PAGEDOWN:
2759 case SCI_PAGEDOWNEXTEND:
2760 case SCI_EDITTOGGLEOVERTYPE:
2761 case SCI_CANCEL:
2762 case SCI_DELETEBACK:
2763 case SCI_TAB:
2764 case SCI_BACKTAB:
2765 case SCI_FORMFEED:
2766 case SCI_VCHOME:
2767 case SCI_VCHOMEEXTEND:
2768 case SCI_VCHOMEWRAP:
2769 case SCI_VCHOMEWRAPEXTEND:
2770 case SCI_VCHOMEDISPLAY:
2771 case SCI_VCHOMEDISPLAYEXTEND:
2772 case SCI_DELWORDLEFT:
2773 case SCI_DELWORDRIGHT:
2774 case SCI_DELWORDRIGHTEND:
2775 case SCI_DELLINELEFT:
2776 case SCI_DELLINERIGHT:
2777 case SCI_LINECOPY:
2778 case SCI_LINECUT:
2779 case SCI_LINEDELETE:
2780 case SCI_LINETRANSPOSE:
2781 case SCI_LINEDUPLICATE:
2782 case SCI_LOWERCASE:
2783 case SCI_UPPERCASE:
2784 case SCI_LINESCROLLDOWN:
2785 case SCI_LINESCROLLUP:
2786 case SCI_DELETEBACKNOTLINE:
2787 case SCI_HOMEDISPLAY:
2788 case SCI_HOMEDISPLAYEXTEND:
2789 case SCI_LINEENDDISPLAY:
2790 case SCI_LINEENDDISPLAYEXTEND:
2791 case SCI_SETSELECTIONMODE:
2792 case SCI_LINEDOWNRECTEXTEND:
2793 case SCI_LINEUPRECTEXTEND:
2794 case SCI_CHARLEFTRECTEXTEND:
2795 case SCI_CHARRIGHTRECTEXTEND:
2796 case SCI_HOMERECTEXTEND:
2797 case SCI_VCHOMERECTEXTEND:
2798 case SCI_LINEENDRECTEXTEND:
2799 case SCI_PAGEUPRECTEXTEND:
2800 case SCI_PAGEDOWNRECTEXTEND:
2801 case SCI_SELECTIONDUPLICATE:
2802 case SCI_COPYALLOWLINE:
2803 case SCI_VERTICALCENTRECARET:
2804 case SCI_MOVESELECTEDLINESUP:
2805 case SCI_MOVESELECTEDLINESDOWN:
2806 case SCI_SCROLLTOSTART:
2807 case SCI_SCROLLTOEND:
2808 break;
2810 // Filter out all others like display changes. Also, newlines are redundant
2811 // with char insert messages.
2812 case SCI_NEWLINE:
2813 default:
2814 // printf("Filtered out %ld of macro recording\n", iMessage);
2815 return;
2818 // Send notification
2819 SCNotification scn = {};
2820 scn.nmhdr.code = SCN_MACRORECORD;
2821 scn.message = iMessage;
2822 scn.wParam = wParam;
2823 scn.lParam = lParam;
2824 NotifyParent(scn);
2827 // Something has changed that the container should know about
2828 void Editor::ContainerNeedsUpdate(int flags) {
2829 needUpdateUI |= flags;
2833 * Force scroll and keep position relative to top of window.
2835 * If stuttered = true and not already at first/last row, move to first/last row of window.
2836 * If stuttered = true and already at first/last row, scroll as normal.
2838 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
2839 int topLineNew;
2840 SelectionPosition newPos;
2842 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
2843 int topStutterLine = topLine + caretYSlop;
2844 int bottomStutterLine =
2845 pdoc->LineFromPosition(PositionFromLocation(
2846 Point::FromInts(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
2847 - caretYSlop - 1;
2849 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
2850 topLineNew = topLine;
2851 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
2852 false, false, UserVirtualSpace());
2854 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
2855 topLineNew = topLine;
2856 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
2857 false, false, UserVirtualSpace());
2859 } else {
2860 Point pt = LocationFromPosition(sel.MainCaret());
2862 topLineNew = Platform::Clamp(
2863 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
2864 newPos = SPositionFromLocation(
2865 Point::FromInts(lastXChosen - xOffset, static_cast<int>(pt.y) + direction * (vs.lineHeight * LinesToScroll())),
2866 false, false, UserVirtualSpace());
2869 if (topLineNew != topLine) {
2870 SetTopLine(topLineNew);
2871 MovePositionTo(newPos, selt);
2872 Redraw();
2873 SetVerticalScrollPos();
2874 } else {
2875 MovePositionTo(newPos, selt);
2879 void Editor::ChangeCaseOfSelection(int caseMapping) {
2880 UndoGroup ug(pdoc);
2881 for (size_t r=0; r<sel.Count(); r++) {
2882 SelectionRange current = sel.Range(r);
2883 SelectionRange currentNoVS = current;
2884 currentNoVS.ClearVirtualSpace();
2885 size_t rangeBytes = currentNoVS.Length();
2886 if (rangeBytes > 0) {
2887 std::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position());
2889 std::string sMapped = CaseMapString(sText, caseMapping);
2891 if (sMapped != sText) {
2892 size_t firstDifference = 0;
2893 while (sMapped[firstDifference] == sText[firstDifference])
2894 firstDifference++;
2895 size_t lastDifferenceText = sText.size() - 1;
2896 size_t lastDifferenceMapped = sMapped.size() - 1;
2897 while (sMapped[lastDifferenceMapped] == sText[lastDifferenceText]) {
2898 lastDifferenceText--;
2899 lastDifferenceMapped--;
2901 size_t endDifferenceText = sText.size() - 1 - lastDifferenceText;
2902 pdoc->DeleteChars(
2903 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
2904 static_cast<int>(rangeBytes - firstDifference - endDifferenceText));
2905 const int lengthChange = static_cast<int>(lastDifferenceMapped - firstDifference + 1);
2906 const int lengthInserted = pdoc->InsertString(
2907 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
2908 sMapped.c_str() + firstDifference,
2909 lengthChange);
2910 // Automatic movement changes selection so reset to exactly the same as it was.
2911 int diffSizes = static_cast<int>(sMapped.size() - sText.size()) + lengthInserted - lengthChange;
2912 if (diffSizes != 0) {
2913 if (current.anchor > current.caret)
2914 current.anchor.Add(diffSizes);
2915 else
2916 current.caret.Add(diffSizes);
2918 sel.Range(r) = current;
2924 void Editor::LineTranspose() {
2925 int line = pdoc->LineFromPosition(sel.MainCaret());
2926 if (line > 0) {
2927 UndoGroup ug(pdoc);
2929 const int startPrevious = pdoc->LineStart(line - 1);
2930 const std::string linePrevious = RangeText(startPrevious, pdoc->LineEnd(line - 1));
2932 int startCurrent = pdoc->LineStart(line);
2933 const std::string lineCurrent = RangeText(startCurrent, pdoc->LineEnd(line));
2935 pdoc->DeleteChars(startCurrent, static_cast<int>(lineCurrent.length()));
2936 pdoc->DeleteChars(startPrevious, static_cast<int>(linePrevious.length()));
2937 startCurrent -= static_cast<int>(linePrevious.length());
2939 startCurrent += pdoc->InsertString(startPrevious, lineCurrent.c_str(),
2940 static_cast<int>(lineCurrent.length()));
2941 pdoc->InsertString(startCurrent, linePrevious.c_str(),
2942 static_cast<int>(linePrevious.length()));
2943 // Move caret to start of current line
2944 MovePositionTo(SelectionPosition(startCurrent));
2948 void Editor::Duplicate(bool forLine) {
2949 if (sel.Empty()) {
2950 forLine = true;
2952 UndoGroup ug(pdoc);
2953 const char *eol = "";
2954 int eolLen = 0;
2955 if (forLine) {
2956 eol = StringFromEOLMode(pdoc->eolMode);
2957 eolLen = istrlen(eol);
2959 for (size_t r=0; r<sel.Count(); r++) {
2960 SelectionPosition start = sel.Range(r).Start();
2961 SelectionPosition end = sel.Range(r).End();
2962 if (forLine) {
2963 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
2964 start = SelectionPosition(pdoc->LineStart(line));
2965 end = SelectionPosition(pdoc->LineEnd(line));
2967 std::string text = RangeText(start.Position(), end.Position());
2968 int lengthInserted = eolLen;
2969 if (forLine)
2970 lengthInserted = pdoc->InsertString(end.Position(), eol, eolLen);
2971 pdoc->InsertString(end.Position() + lengthInserted, text.c_str(), static_cast<int>(text.length()));
2973 if (sel.Count() && sel.IsRectangular()) {
2974 SelectionPosition last = sel.Last();
2975 if (forLine) {
2976 int line = pdoc->LineFromPosition(last.Position());
2977 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
2979 if (sel.Rectangular().anchor > sel.Rectangular().caret)
2980 sel.Rectangular().anchor = last;
2981 else
2982 sel.Rectangular().caret = last;
2983 SetRectangularRange();
2987 void Editor::CancelModes() {
2988 sel.SetMoveExtends(false);
2991 void Editor::NewLine() {
2992 InvalidateWholeSelection();
2993 if (sel.IsRectangular() || !additionalSelectionTyping) {
2994 // Remove non-main ranges
2995 sel.DropAdditionalRanges();
2998 UndoGroup ug(pdoc, !sel.Empty() || (sel.Count() > 1));
3000 // Clear each range
3001 if (!sel.Empty()) {
3002 ClearSelection();
3005 // Insert each line end
3006 size_t countInsertions = 0;
3007 for (size_t r = 0; r < sel.Count(); r++) {
3008 sel.Range(r).ClearVirtualSpace();
3009 const char *eol = StringFromEOLMode(pdoc->eolMode);
3010 const int positionInsert = sel.Range(r).caret.Position();
3011 const int insertLength = pdoc->InsertString(positionInsert, eol, istrlen(eol));
3012 if (insertLength > 0) {
3013 sel.Range(r) = SelectionRange(positionInsert + insertLength);
3014 countInsertions++;
3018 // Perform notifications after all the changes as the application may change the
3019 // selections in response to the characters.
3020 for (size_t i = 0; i < countInsertions; i++) {
3021 const char *eol = StringFromEOLMode(pdoc->eolMode);
3022 while (*eol) {
3023 NotifyChar(*eol);
3024 if (recordingMacro) {
3025 char txt[2];
3026 txt[0] = *eol;
3027 txt[1] = '\0';
3028 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
3030 eol++;
3034 SetLastXChosen();
3035 SetScrollBars();
3036 EnsureCaretVisible();
3037 // Avoid blinking during rapid typing:
3038 ShowCaretAtCurrentPosition();
3041 SelectionPosition Editor::PositionUpOrDown(SelectionPosition spStart, int direction, int lastX) {
3042 const Point pt = LocationFromPosition(spStart);
3043 int skipLines = 0;
3045 if (vs.annotationVisible) {
3046 const int lineDoc = pdoc->LineFromPosition(spStart.Position());
3047 const Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
3048 const int subLine = static_cast<int>(pt.y - ptStartLine.y) / vs.lineHeight;
3050 if (direction < 0 && subLine == 0) {
3051 const int lineDisplay = cs.DisplayFromDoc(lineDoc);
3052 if (lineDisplay > 0) {
3053 skipLines = pdoc->AnnotationLines(cs.DocFromDisplay(lineDisplay - 1));
3055 } else if (direction > 0 && subLine >= (cs.GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
3056 skipLines = pdoc->AnnotationLines(lineDoc);
3060 const int newY = static_cast<int>(pt.y) + (1 + skipLines) * direction * vs.lineHeight;
3061 if (lastX < 0) {
3062 lastX = static_cast<int>(pt.x) + xOffset;
3064 SelectionPosition posNew = SPositionFromLocation(
3065 Point::FromInts(lastX - xOffset, newY), false, false, UserVirtualSpace());
3067 if (direction < 0) {
3068 // Line wrapping may lead to a location on the same line, so
3069 // seek back if that is the case.
3070 Point ptNew = LocationFromPosition(posNew.Position());
3071 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
3072 posNew.Add(-1);
3073 posNew.SetVirtualSpace(0);
3074 ptNew = LocationFromPosition(posNew.Position());
3076 } else if (direction > 0 && posNew.Position() != pdoc->Length()) {
3077 // There is an equivalent case when moving down which skips
3078 // over a line.
3079 Point ptNew = LocationFromPosition(posNew.Position());
3080 while ((posNew.Position() > spStart.Position()) && (ptNew.y > newY)) {
3081 posNew.Add(-1);
3082 posNew.SetVirtualSpace(0);
3083 ptNew = LocationFromPosition(posNew.Position());
3086 return posNew;
3089 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
3090 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
3091 if (sel.IsRectangular()) {
3092 if (selt == Selection::noSel) {
3093 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
3094 } else {
3095 caretToUse = sel.Rectangular().caret;
3098 if (selt == Selection::selRectangle) {
3099 const SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();
3100 if (!sel.IsRectangular()) {
3101 InvalidateWholeSelection();
3102 sel.DropAdditionalRanges();
3104 const SelectionPosition posNew = MovePositionSoVisible(
3105 PositionUpOrDown(caretToUse, direction, lastXChosen), direction);
3106 sel.selType = Selection::selRectangle;
3107 sel.Rectangular() = SelectionRange(posNew, rangeBase.anchor);
3108 SetRectangularRange();
3109 MovedCaret(posNew, caretToUse, true);
3110 } else {
3111 InvalidateWholeSelection();
3112 if (!additionalSelectionTyping || (sel.IsRectangular())) {
3113 sel.DropAdditionalRanges();
3115 sel.selType = Selection::selStream;
3116 for (size_t r = 0; r < sel.Count(); r++) {
3117 const int lastX = (r == sel.Main()) ? lastXChosen : -1;
3118 const SelectionPosition spCaretNow = sel.Range(r).caret;
3119 const SelectionPosition posNew = MovePositionSoVisible(
3120 PositionUpOrDown(spCaretNow, direction, lastX), direction);
3121 sel.Range(r) = selt == Selection::selStream ?
3122 SelectionRange(posNew, sel.Range(r).anchor) : SelectionRange(posNew);
3124 sel.RemoveDuplicates();
3125 MovedCaret(sel.RangeMain().caret, caretToUse, true);
3129 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
3130 int lineDoc, savedPos = sel.MainCaret();
3131 do {
3132 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
3133 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
3134 if (direction > 0) {
3135 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
3136 if (selt == Selection::noSel) {
3137 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
3139 break;
3142 } while (!cs.GetVisible(lineDoc));
3145 int Editor::StartEndDisplayLine(int pos, bool start) {
3146 RefreshStyleData();
3147 AutoSurface surface(this);
3148 int posRet = view.StartEndDisplayLine(surface, *this, pos, start, vs);
3149 if (posRet == INVALID_POSITION) {
3150 return pos;
3151 } else {
3152 return posRet;
3156 namespace {
3158 unsigned int WithExtends(unsigned int iMessage) {
3159 switch (iMessage) {
3160 case SCI_CHARLEFT: return SCI_CHARLEFTEXTEND;
3161 case SCI_CHARRIGHT: return SCI_CHARRIGHTEXTEND;
3163 case SCI_WORDLEFT: return SCI_WORDLEFTEXTEND;
3164 case SCI_WORDRIGHT: return SCI_WORDRIGHTEXTEND;
3165 case SCI_WORDLEFTEND: return SCI_WORDLEFTENDEXTEND;
3166 case SCI_WORDRIGHTEND: return SCI_WORDRIGHTENDEXTEND;
3167 case SCI_WORDPARTLEFT: return SCI_WORDPARTLEFTEXTEND;
3168 case SCI_WORDPARTRIGHT: return SCI_WORDPARTRIGHTEXTEND;
3170 case SCI_HOME: return SCI_HOMEEXTEND;
3171 case SCI_HOMEDISPLAY: return SCI_HOMEDISPLAYEXTEND;
3172 case SCI_HOMEWRAP: return SCI_HOMEWRAPEXTEND;
3173 case SCI_VCHOME: return SCI_VCHOMEEXTEND;
3174 case SCI_VCHOMEDISPLAY: return SCI_VCHOMEDISPLAYEXTEND;
3175 case SCI_VCHOMEWRAP: return SCI_VCHOMEWRAPEXTEND;
3177 case SCI_LINEEND: return SCI_LINEENDEXTEND;
3178 case SCI_LINEENDDISPLAY: return SCI_LINEENDDISPLAYEXTEND;
3179 case SCI_LINEENDWRAP: return SCI_LINEENDWRAPEXTEND;
3181 default: return iMessage;
3185 int NaturalDirection(unsigned int iMessage) {
3186 switch (iMessage) {
3187 case SCI_CHARLEFT:
3188 case SCI_CHARLEFTEXTEND:
3189 case SCI_CHARLEFTRECTEXTEND:
3190 case SCI_WORDLEFT:
3191 case SCI_WORDLEFTEXTEND:
3192 case SCI_WORDLEFTEND:
3193 case SCI_WORDLEFTENDEXTEND:
3194 case SCI_WORDPARTLEFT:
3195 case SCI_WORDPARTLEFTEXTEND:
3196 case SCI_HOME:
3197 case SCI_HOMEEXTEND:
3198 case SCI_HOMEDISPLAY:
3199 case SCI_HOMEDISPLAYEXTEND:
3200 case SCI_HOMEWRAP:
3201 case SCI_HOMEWRAPEXTEND:
3202 // VC_HOME* mostly goes back
3203 case SCI_VCHOME:
3204 case SCI_VCHOMEEXTEND:
3205 case SCI_VCHOMEDISPLAY:
3206 case SCI_VCHOMEDISPLAYEXTEND:
3207 case SCI_VCHOMEWRAP:
3208 case SCI_VCHOMEWRAPEXTEND:
3209 return -1;
3211 default:
3212 return 1;
3216 bool IsRectExtend(unsigned int iMessage) {
3217 switch (iMessage) {
3218 case SCI_CHARLEFTRECTEXTEND:
3219 case SCI_CHARRIGHTRECTEXTEND:
3220 case SCI_HOMERECTEXTEND:
3221 case SCI_VCHOMERECTEXTEND:
3222 case SCI_LINEENDRECTEXTEND:
3223 return true;
3224 default:
3225 return false;
3231 int Editor::VCHomeDisplayPosition(int position) {
3232 const int homePos = pdoc->VCHomePosition(position);
3233 const int viewLineStart = StartEndDisplayLine(position, true);
3234 if (viewLineStart > homePos)
3235 return viewLineStart;
3236 else
3237 return homePos;
3240 int Editor::VCHomeWrapPosition(int position) {
3241 const int homePos = pdoc->VCHomePosition(position);
3242 const int viewLineStart = StartEndDisplayLine(position, true);
3243 if ((viewLineStart < position) && (viewLineStart > homePos))
3244 return viewLineStart;
3245 else
3246 return homePos;
3249 int Editor::LineEndWrapPosition(int position) {
3250 const int endPos = StartEndDisplayLine(position, false);
3251 const int realEndPos = pdoc->LineEndPosition(position);
3252 if (endPos > realEndPos // if moved past visible EOLs
3253 || position >= endPos) // if at end of display line already
3254 return realEndPos;
3255 else
3256 return endPos;
3259 int Editor::HorizontalMove(unsigned int iMessage) {
3260 if (sel.MoveExtends()) {
3261 iMessage = WithExtends(iMessage);
3264 if (!multipleSelection && !sel.IsRectangular()) {
3265 // Simplify selection down to 1
3266 sel.SetSelection(sel.RangeMain());
3269 // Invalidate each of the current selections
3270 InvalidateWholeSelection();
3272 if (IsRectExtend(iMessage)) {
3273 const SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();
3274 if (!sel.IsRectangular()) {
3275 sel.DropAdditionalRanges();
3277 // Will change to rectangular if not currently rectangular
3278 SelectionPosition spCaret = rangeBase.caret;
3279 switch (iMessage) {
3280 case SCI_CHARLEFTRECTEXTEND:
3281 if (pdoc->IsLineEndPosition(spCaret.Position()) && spCaret.VirtualSpace()) {
3282 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3283 } else {
3284 spCaret = SelectionPosition(spCaret.Position() - 1);
3286 break;
3287 case SCI_CHARRIGHTRECTEXTEND:
3288 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
3289 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3290 } else {
3291 spCaret = SelectionPosition(spCaret.Position() + 1);
3293 break;
3294 case SCI_HOMERECTEXTEND:
3295 spCaret = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3296 break;
3297 case SCI_VCHOMERECTEXTEND:
3298 spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
3299 break;
3300 case SCI_LINEENDRECTEXTEND:
3301 spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3302 break;
3304 const int directionMove = (spCaret < rangeBase.caret) ? -1 : 1;
3305 spCaret = MovePositionSoVisible(spCaret, directionMove);
3306 sel.selType = Selection::selRectangle;
3307 sel.Rectangular() = SelectionRange(spCaret, rangeBase.anchor);
3308 SetRectangularRange();
3309 } else {
3310 if (sel.IsRectangular()) {
3311 // Not a rectangular extension so switch to stream.
3312 SelectionPosition selAtLimit = (NaturalDirection(iMessage) > 0) ? sel.Limits().end : sel.Limits().start;
3313 sel.selType = Selection::selStream;
3314 sel.SetSelection(SelectionRange(selAtLimit));
3316 if (!additionalSelectionTyping) {
3317 InvalidateWholeSelection();
3318 sel.DropAdditionalRanges();
3320 for (size_t r = 0; r < sel.Count(); r++) {
3321 const SelectionPosition spCaretNow = sel.Range(r).caret;
3322 SelectionPosition spCaret = spCaretNow;
3323 switch (iMessage) {
3324 case SCI_CHARLEFT:
3325 case SCI_CHARLEFTEXTEND:
3326 if (spCaret.VirtualSpace()) {
3327 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3328 } else {
3329 spCaret = SelectionPosition(spCaret.Position() - 1);
3331 break;
3332 case SCI_CHARRIGHT:
3333 case SCI_CHARRIGHTEXTEND:
3334 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(spCaret.Position())) {
3335 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3336 } else {
3337 spCaret = SelectionPosition(spCaret.Position() + 1);
3339 break;
3340 case SCI_WORDLEFT:
3341 case SCI_WORDLEFTEXTEND:
3342 spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), -1));
3343 break;
3344 case SCI_WORDRIGHT:
3345 case SCI_WORDRIGHTEXTEND:
3346 spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), 1));
3347 break;
3348 case SCI_WORDLEFTEND:
3349 case SCI_WORDLEFTENDEXTEND:
3350 spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), -1));
3351 break;
3352 case SCI_WORDRIGHTEND:
3353 case SCI_WORDRIGHTENDEXTEND:
3354 spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), 1));
3355 break;
3356 case SCI_WORDPARTLEFT:
3357 case SCI_WORDPARTLEFTEXTEND:
3358 spCaret = SelectionPosition(pdoc->WordPartLeft(spCaret.Position()));
3359 break;
3360 case SCI_WORDPARTRIGHT:
3361 case SCI_WORDPARTRIGHTEXTEND:
3362 spCaret = SelectionPosition(pdoc->WordPartRight(spCaret.Position()));
3363 break;
3364 case SCI_HOME:
3365 case SCI_HOMEEXTEND:
3366 spCaret = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3367 break;
3368 case SCI_HOMEDISPLAY:
3369 case SCI_HOMEDISPLAYEXTEND:
3370 spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), true));
3371 break;
3372 case SCI_HOMEWRAP:
3373 case SCI_HOMEWRAPEXTEND:
3374 spCaret = MovePositionSoVisible(StartEndDisplayLine(spCaret.Position(), true), -1);
3375 if (spCaretNow <= spCaret)
3376 spCaret = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3377 break;
3378 case SCI_VCHOME:
3379 case SCI_VCHOMEEXTEND:
3380 // VCHome alternates between beginning of line and beginning of text so may move back or forwards
3381 spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
3382 break;
3383 case SCI_VCHOMEDISPLAY:
3384 case SCI_VCHOMEDISPLAYEXTEND:
3385 spCaret = SelectionPosition(VCHomeDisplayPosition(spCaret.Position()));
3386 break;
3387 case SCI_VCHOMEWRAP:
3388 case SCI_VCHOMEWRAPEXTEND:
3389 spCaret = SelectionPosition(VCHomeWrapPosition(spCaret.Position()));
3390 break;
3391 case SCI_LINEEND:
3392 case SCI_LINEENDEXTEND:
3393 spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3394 break;
3395 case SCI_LINEENDDISPLAY:
3396 case SCI_LINEENDDISPLAYEXTEND:
3397 spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), false));
3398 break;
3399 case SCI_LINEENDWRAP:
3400 case SCI_LINEENDWRAPEXTEND:
3401 spCaret = SelectionPosition(LineEndWrapPosition(spCaret.Position()));
3402 break;
3404 default:
3405 PLATFORM_ASSERT(false);
3408 const int directionMove = (spCaret < spCaretNow) ? -1 : 1;
3409 spCaret = MovePositionSoVisible(spCaret, directionMove);
3411 // Handle move versus extend, and special behaviour for non-emoty left/right
3412 switch (iMessage) {
3413 case SCI_CHARLEFT:
3414 case SCI_CHARRIGHT:
3415 if (sel.Range(r).Empty()) {
3416 sel.Range(r) = SelectionRange(spCaret);
3417 } else {
3418 sel.Range(r) = SelectionRange(
3419 (iMessage == SCI_CHARLEFT) ? sel.Range(r).Start() : sel.Range(r).End());
3421 break;
3423 case SCI_WORDLEFT:
3424 case SCI_WORDRIGHT:
3425 case SCI_WORDLEFTEND:
3426 case SCI_WORDRIGHTEND:
3427 case SCI_WORDPARTLEFT:
3428 case SCI_WORDPARTRIGHT:
3429 case SCI_HOME:
3430 case SCI_HOMEDISPLAY:
3431 case SCI_HOMEWRAP:
3432 case SCI_VCHOME:
3433 case SCI_VCHOMEDISPLAY:
3434 case SCI_VCHOMEWRAP:
3435 case SCI_LINEEND:
3436 case SCI_LINEENDDISPLAY:
3437 case SCI_LINEENDWRAP:
3438 sel.Range(r) = SelectionRange(spCaret);
3439 break;
3441 case SCI_CHARLEFTEXTEND:
3442 case SCI_CHARRIGHTEXTEND:
3443 case SCI_WORDLEFTEXTEND:
3444 case SCI_WORDRIGHTEXTEND:
3445 case SCI_WORDLEFTENDEXTEND:
3446 case SCI_WORDRIGHTENDEXTEND:
3447 case SCI_WORDPARTLEFTEXTEND:
3448 case SCI_WORDPARTRIGHTEXTEND:
3449 case SCI_HOMEEXTEND:
3450 case SCI_HOMEDISPLAYEXTEND:
3451 case SCI_HOMEWRAPEXTEND:
3452 case SCI_VCHOMEEXTEND:
3453 case SCI_VCHOMEDISPLAYEXTEND:
3454 case SCI_VCHOMEWRAPEXTEND:
3455 case SCI_LINEENDEXTEND:
3456 case SCI_LINEENDDISPLAYEXTEND:
3457 case SCI_LINEENDWRAPEXTEND: {
3458 SelectionRange rangeNew = SelectionRange(spCaret, sel.Range(r).anchor);
3459 sel.TrimOtherSelections(r, SelectionRange(rangeNew));
3460 sel.Range(r) = rangeNew;
3462 break;
3464 default:
3465 PLATFORM_ASSERT(false);
3470 sel.RemoveDuplicates();
3472 MovedCaret(sel.RangeMain().caret, SelectionPosition(INVALID_POSITION), true);
3474 // Invalidate the new state of the selection
3475 InvalidateWholeSelection();
3477 SetLastXChosen();
3478 // Need the line moving and so forth from MovePositionTo
3479 return 0;
3482 int Editor::DelWordOrLine(unsigned int iMessage) {
3483 // Virtual space may be realised for SCI_DELWORDRIGHT or SCI_DELWORDRIGHTEND
3484 // which means 2 actions so wrap in an undo group.
3486 // Rightwards and leftwards deletions differ in treatment of virtual space.
3487 // Clear virtual space for leftwards, realise for rightwards.
3488 const bool leftwards = (iMessage == SCI_DELWORDLEFT) || (iMessage == SCI_DELLINELEFT);
3490 if (!additionalSelectionTyping) {
3491 InvalidateWholeSelection();
3492 sel.DropAdditionalRanges();
3495 UndoGroup ug0(pdoc, (sel.Count() > 1) || !leftwards);
3497 for (size_t r = 0; r < sel.Count(); r++) {
3498 if (leftwards) {
3499 // Delete to the left so first clear the virtual space.
3500 sel.Range(r).ClearVirtualSpace();
3501 } else {
3502 // Delete to the right so first realise the virtual space.
3503 sel.Range(r) = SelectionRange(
3504 InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
3507 Range rangeDelete;
3508 switch (iMessage) {
3509 case SCI_DELWORDLEFT:
3510 rangeDelete = Range(
3511 pdoc->NextWordStart(sel.Range(r).caret.Position(), -1),
3512 sel.Range(r).caret.Position());
3513 break;
3514 case SCI_DELWORDRIGHT:
3515 rangeDelete = Range(
3516 sel.Range(r).caret.Position(),
3517 pdoc->NextWordStart(sel.Range(r).caret.Position(), 1));
3518 break;
3519 case SCI_DELWORDRIGHTEND:
3520 rangeDelete = Range(
3521 sel.Range(r).caret.Position(),
3522 pdoc->NextWordEnd(sel.Range(r).caret.Position(), 1));
3523 break;
3524 case SCI_DELLINELEFT:
3525 rangeDelete = Range(
3526 pdoc->LineStart(pdoc->LineFromPosition(sel.Range(r).caret.Position())),
3527 sel.Range(r).caret.Position());
3528 break;
3529 case SCI_DELLINERIGHT:
3530 rangeDelete = Range(
3531 sel.Range(r).caret.Position(),
3532 pdoc->LineEnd(pdoc->LineFromPosition(sel.Range(r).caret.Position())));
3533 break;
3535 if (!RangeContainsProtected(rangeDelete.start, rangeDelete.end)) {
3536 pdoc->DeleteChars(rangeDelete.start, rangeDelete.end - rangeDelete.start);
3540 // May need something stronger here: can selections overlap at this point?
3541 sel.RemoveDuplicates();
3543 MovedCaret(sel.RangeMain().caret, SelectionPosition(INVALID_POSITION), true);
3545 // Invalidate the new state of the selection
3546 InvalidateWholeSelection();
3548 SetLastXChosen();
3549 return 0;
3552 int Editor::KeyCommand(unsigned int iMessage) {
3553 switch (iMessage) {
3554 case SCI_LINEDOWN:
3555 CursorUpOrDown(1, Selection::noSel);
3556 break;
3557 case SCI_LINEDOWNEXTEND:
3558 CursorUpOrDown(1, Selection::selStream);
3559 break;
3560 case SCI_LINEDOWNRECTEXTEND:
3561 CursorUpOrDown(1, Selection::selRectangle);
3562 break;
3563 case SCI_PARADOWN:
3564 ParaUpOrDown(1, Selection::noSel);
3565 break;
3566 case SCI_PARADOWNEXTEND:
3567 ParaUpOrDown(1, Selection::selStream);
3568 break;
3569 case SCI_LINESCROLLDOWN:
3570 ScrollTo(topLine + 1);
3571 MoveCaretInsideView(false);
3572 break;
3573 case SCI_LINEUP:
3574 CursorUpOrDown(-1, Selection::noSel);
3575 break;
3576 case SCI_LINEUPEXTEND:
3577 CursorUpOrDown(-1, Selection::selStream);
3578 break;
3579 case SCI_LINEUPRECTEXTEND:
3580 CursorUpOrDown(-1, Selection::selRectangle);
3581 break;
3582 case SCI_PARAUP:
3583 ParaUpOrDown(-1, Selection::noSel);
3584 break;
3585 case SCI_PARAUPEXTEND:
3586 ParaUpOrDown(-1, Selection::selStream);
3587 break;
3588 case SCI_LINESCROLLUP:
3589 ScrollTo(topLine - 1);
3590 MoveCaretInsideView(false);
3591 break;
3593 case SCI_CHARLEFT:
3594 case SCI_CHARLEFTEXTEND:
3595 case SCI_CHARLEFTRECTEXTEND:
3596 case SCI_CHARRIGHT:
3597 case SCI_CHARRIGHTEXTEND:
3598 case SCI_CHARRIGHTRECTEXTEND:
3599 case SCI_WORDLEFT:
3600 case SCI_WORDLEFTEXTEND:
3601 case SCI_WORDRIGHT:
3602 case SCI_WORDRIGHTEXTEND:
3603 case SCI_WORDLEFTEND:
3604 case SCI_WORDLEFTENDEXTEND:
3605 case SCI_WORDRIGHTEND:
3606 case SCI_WORDRIGHTENDEXTEND:
3607 case SCI_WORDPARTLEFT:
3608 case SCI_WORDPARTLEFTEXTEND:
3609 case SCI_WORDPARTRIGHT:
3610 case SCI_WORDPARTRIGHTEXTEND:
3611 case SCI_HOME:
3612 case SCI_HOMEEXTEND:
3613 case SCI_HOMERECTEXTEND:
3614 case SCI_HOMEDISPLAY:
3615 case SCI_HOMEDISPLAYEXTEND:
3616 case SCI_HOMEWRAP:
3617 case SCI_HOMEWRAPEXTEND:
3618 case SCI_VCHOME:
3619 case SCI_VCHOMEEXTEND:
3620 case SCI_VCHOMERECTEXTEND:
3621 case SCI_VCHOMEDISPLAY:
3622 case SCI_VCHOMEDISPLAYEXTEND:
3623 case SCI_VCHOMEWRAP:
3624 case SCI_VCHOMEWRAPEXTEND:
3625 case SCI_LINEEND:
3626 case SCI_LINEENDEXTEND:
3627 case SCI_LINEENDRECTEXTEND:
3628 case SCI_LINEENDDISPLAY:
3629 case SCI_LINEENDDISPLAYEXTEND:
3630 case SCI_LINEENDWRAP:
3631 case SCI_LINEENDWRAPEXTEND:
3632 return HorizontalMove(iMessage);
3634 case SCI_DOCUMENTSTART:
3635 MovePositionTo(0);
3636 SetLastXChosen();
3637 break;
3638 case SCI_DOCUMENTSTARTEXTEND:
3639 MovePositionTo(0, Selection::selStream);
3640 SetLastXChosen();
3641 break;
3642 case SCI_DOCUMENTEND:
3643 MovePositionTo(pdoc->Length());
3644 SetLastXChosen();
3645 break;
3646 case SCI_DOCUMENTENDEXTEND:
3647 MovePositionTo(pdoc->Length(), Selection::selStream);
3648 SetLastXChosen();
3649 break;
3650 case SCI_STUTTEREDPAGEUP:
3651 PageMove(-1, Selection::noSel, true);
3652 break;
3653 case SCI_STUTTEREDPAGEUPEXTEND:
3654 PageMove(-1, Selection::selStream, true);
3655 break;
3656 case SCI_STUTTEREDPAGEDOWN:
3657 PageMove(1, Selection::noSel, true);
3658 break;
3659 case SCI_STUTTEREDPAGEDOWNEXTEND:
3660 PageMove(1, Selection::selStream, true);
3661 break;
3662 case SCI_PAGEUP:
3663 PageMove(-1);
3664 break;
3665 case SCI_PAGEUPEXTEND:
3666 PageMove(-1, Selection::selStream);
3667 break;
3668 case SCI_PAGEUPRECTEXTEND:
3669 PageMove(-1, Selection::selRectangle);
3670 break;
3671 case SCI_PAGEDOWN:
3672 PageMove(1);
3673 break;
3674 case SCI_PAGEDOWNEXTEND:
3675 PageMove(1, Selection::selStream);
3676 break;
3677 case SCI_PAGEDOWNRECTEXTEND:
3678 PageMove(1, Selection::selRectangle);
3679 break;
3680 case SCI_EDITTOGGLEOVERTYPE:
3681 inOverstrike = !inOverstrike;
3682 ShowCaretAtCurrentPosition();
3683 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
3684 NotifyUpdateUI();
3685 break;
3686 case SCI_CANCEL: // Cancel any modes - handled in subclass
3687 // Also unselect text
3688 CancelModes();
3689 if (sel.Count() > 1) {
3690 // Drop additional selections
3691 InvalidateWholeSelection();
3692 sel.DropAdditionalRanges();
3694 break;
3695 case SCI_DELETEBACK:
3696 DelCharBack(true);
3697 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3698 SetLastXChosen();
3700 EnsureCaretVisible();
3701 break;
3702 case SCI_DELETEBACKNOTLINE:
3703 DelCharBack(false);
3704 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3705 SetLastXChosen();
3707 EnsureCaretVisible();
3708 break;
3709 case SCI_TAB:
3710 Indent(true);
3711 if (caretSticky == SC_CARETSTICKY_OFF) {
3712 SetLastXChosen();
3714 EnsureCaretVisible();
3715 ShowCaretAtCurrentPosition(); // Avoid blinking
3716 break;
3717 case SCI_BACKTAB:
3718 Indent(false);
3719 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3720 SetLastXChosen();
3722 EnsureCaretVisible();
3723 ShowCaretAtCurrentPosition(); // Avoid blinking
3724 break;
3725 case SCI_NEWLINE:
3726 NewLine();
3727 break;
3728 case SCI_FORMFEED:
3729 AddChar('\f');
3730 break;
3731 case SCI_ZOOMIN:
3732 if (vs.zoomLevel < 20) {
3733 vs.zoomLevel++;
3734 InvalidateStyleRedraw();
3735 NotifyZoom();
3737 break;
3738 case SCI_ZOOMOUT:
3739 if (vs.zoomLevel > -10) {
3740 vs.zoomLevel--;
3741 InvalidateStyleRedraw();
3742 NotifyZoom();
3744 break;
3746 case SCI_DELWORDLEFT:
3747 case SCI_DELWORDRIGHT:
3748 case SCI_DELWORDRIGHTEND:
3749 case SCI_DELLINELEFT:
3750 case SCI_DELLINERIGHT:
3751 return DelWordOrLine(iMessage);
3753 case SCI_LINECOPY: {
3754 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
3755 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
3756 CopyRangeToClipboard(pdoc->LineStart(lineStart),
3757 pdoc->LineStart(lineEnd + 1));
3759 break;
3760 case SCI_LINECUT: {
3761 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
3762 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
3763 int start = pdoc->LineStart(lineStart);
3764 int end = pdoc->LineStart(lineEnd + 1);
3765 SetSelection(start, end);
3766 Cut();
3767 SetLastXChosen();
3769 break;
3770 case SCI_LINEDELETE: {
3771 int line = pdoc->LineFromPosition(sel.MainCaret());
3772 int start = pdoc->LineStart(line);
3773 int end = pdoc->LineStart(line + 1);
3774 pdoc->DeleteChars(start, end - start);
3776 break;
3777 case SCI_LINETRANSPOSE:
3778 LineTranspose();
3779 break;
3780 case SCI_LINEDUPLICATE:
3781 Duplicate(true);
3782 break;
3783 case SCI_SELECTIONDUPLICATE:
3784 Duplicate(false);
3785 break;
3786 case SCI_LOWERCASE:
3787 ChangeCaseOfSelection(cmLower);
3788 break;
3789 case SCI_UPPERCASE:
3790 ChangeCaseOfSelection(cmUpper);
3791 break;
3792 case SCI_SCROLLTOSTART:
3793 ScrollTo(0);
3794 break;
3795 case SCI_SCROLLTOEND:
3796 ScrollTo(MaxScrollPos());
3797 break;
3799 return 0;
3802 int Editor::KeyDefault(int, int) {
3803 return 0;
3806 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
3807 DwellEnd(false);
3808 int msg = kmap.Find(key, modifiers);
3809 if (msg) {
3810 if (consumed)
3811 *consumed = true;
3812 return static_cast<int>(WndProc(msg, 0, 0));
3813 } else {
3814 if (consumed)
3815 *consumed = false;
3816 return KeyDefault(key, modifiers);
3820 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
3821 return KeyDownWithModifiers(key, ModifierFlags(shift, ctrl, alt), consumed);
3824 void Editor::Indent(bool forwards) {
3825 UndoGroup ug(pdoc);
3826 for (size_t r=0; r<sel.Count(); r++) {
3827 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
3828 int caretPosition = sel.Range(r).caret.Position();
3829 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
3830 if (lineOfAnchor == lineCurrentPos) {
3831 if (forwards) {
3832 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
3833 caretPosition = sel.Range(r).caret.Position();
3834 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
3835 pdoc->tabIndents) {
3836 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3837 int indentationStep = pdoc->IndentSize();
3838 const int posSelect = pdoc->SetLineIndentation(
3839 lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
3840 sel.Range(r) = SelectionRange(posSelect);
3841 } else {
3842 if (pdoc->useTabs) {
3843 const int lengthInserted = pdoc->InsertString(caretPosition, "\t", 1);
3844 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
3845 } else {
3846 int numSpaces = (pdoc->tabInChars) -
3847 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
3848 if (numSpaces < 1)
3849 numSpaces = pdoc->tabInChars;
3850 const std::string spaceText(numSpaces, ' ');
3851 const int lengthInserted = pdoc->InsertString(caretPosition, spaceText.c_str(),
3852 static_cast<int>(spaceText.length()));
3853 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
3856 } else {
3857 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
3858 pdoc->tabIndents) {
3859 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3860 int indentationStep = pdoc->IndentSize();
3861 const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
3862 sel.Range(r) = SelectionRange(posSelect);
3863 } else {
3864 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
3865 pdoc->tabInChars;
3866 if (newColumn < 0)
3867 newColumn = 0;
3868 int newPos = caretPosition;
3869 while (pdoc->GetColumn(newPos) > newColumn)
3870 newPos--;
3871 sel.Range(r) = SelectionRange(newPos);
3874 } else { // Multiline
3875 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
3876 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
3877 // Multiple lines selected so indent / dedent
3878 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
3879 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
3880 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
3881 lineBottomSel--; // If not selecting any characters on a line, do not indent
3882 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
3883 if (lineOfAnchor < lineCurrentPos) {
3884 if (currentPosPosOnLine == 0)
3885 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
3886 else
3887 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
3888 } else {
3889 if (anchorPosOnLine == 0)
3890 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
3891 else
3892 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
3896 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
3899 class CaseFolderASCII : public CaseFolderTable {
3900 public:
3901 CaseFolderASCII() {
3902 StandardASCII();
3904 ~CaseFolderASCII() {
3909 CaseFolder *Editor::CaseFolderForEncoding() {
3910 // Simple default that only maps ASCII upper case to lower case.
3911 return new CaseFolderASCII();
3915 * Search of a text in the document, in the given range.
3916 * @return The position of the found text, -1 if not found.
3918 long Editor::FindText(
3919 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
3920 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
3921 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
3923 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
3924 int lengthFound = istrlen(ft->lpstrText);
3925 if (!pdoc->HasCaseFolder())
3926 pdoc->SetCaseFolder(CaseFolderForEncoding());
3927 try {
3928 long pos = pdoc->FindText(
3929 static_cast<int>(ft->chrg.cpMin),
3930 static_cast<int>(ft->chrg.cpMax),
3931 ft->lpstrText,
3932 static_cast<int>(wParam),
3933 &lengthFound);
3934 if (pos != -1) {
3935 ft->chrgText.cpMin = pos;
3936 ft->chrgText.cpMax = pos + lengthFound;
3938 return static_cast<int>(pos);
3939 } catch (RegexError &) {
3940 errorStatus = SC_STATUS_WARN_REGEX;
3941 return -1;
3946 * Relocatable search support : Searches relative to current selection
3947 * point and sets the selection to the found text range with
3948 * each search.
3951 * Anchor following searches at current selection start: This allows
3952 * multiple incremental interactive searches to be macro recorded
3953 * while still setting the selection to found text so the find/select
3954 * operation is self-contained.
3956 void Editor::SearchAnchor() {
3957 searchAnchor = SelectionStart().Position();
3961 * Find text from current search anchor: Must call @c SearchAnchor first.
3962 * Used for next text and previous text requests.
3963 * @return The position of the found text, -1 if not found.
3965 long Editor::SearchText(
3966 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
3967 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
3968 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
3969 sptr_t lParam) { ///< The text to search for.
3971 const char *txt = reinterpret_cast<char *>(lParam);
3972 long pos;
3973 int lengthFound = istrlen(txt);
3974 if (!pdoc->HasCaseFolder())
3975 pdoc->SetCaseFolder(CaseFolderForEncoding());
3976 try {
3977 if (iMessage == SCI_SEARCHNEXT) {
3978 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
3979 static_cast<int>(wParam),
3980 &lengthFound);
3981 } else {
3982 pos = pdoc->FindText(searchAnchor, 0, txt,
3983 static_cast<int>(wParam),
3984 &lengthFound);
3986 } catch (RegexError &) {
3987 errorStatus = SC_STATUS_WARN_REGEX;
3988 return -1;
3990 if (pos != -1) {
3991 SetSelection(static_cast<int>(pos), static_cast<int>(pos + lengthFound));
3994 return pos;
3997 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
3998 std::string ret(s);
3999 for (size_t i=0; i<ret.size(); i++) {
4000 switch (caseMapping) {
4001 case cmUpper:
4002 if (ret[i] >= 'a' && ret[i] <= 'z')
4003 ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
4004 break;
4005 case cmLower:
4006 if (ret[i] >= 'A' && ret[i] <= 'Z')
4007 ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
4008 break;
4011 return ret;
4015 * Search for text in the target range of the document.
4016 * @return The position of the found text, -1 if not found.
4018 long Editor::SearchInTarget(const char *text, int length) {
4019 int lengthFound = length;
4021 if (!pdoc->HasCaseFolder())
4022 pdoc->SetCaseFolder(CaseFolderForEncoding());
4023 try {
4024 long pos = pdoc->FindText(targetStart, targetEnd, text,
4025 searchFlags,
4026 &lengthFound);
4027 if (pos != -1) {
4028 targetStart = static_cast<int>(pos);
4029 targetEnd = static_cast<int>(pos + lengthFound);
4031 return pos;
4032 } catch (RegexError &) {
4033 errorStatus = SC_STATUS_WARN_REGEX;
4034 return -1;
4038 void Editor::GoToLine(int lineNo) {
4039 if (lineNo > pdoc->LinesTotal())
4040 lineNo = pdoc->LinesTotal();
4041 if (lineNo < 0)
4042 lineNo = 0;
4043 SetEmptySelection(pdoc->LineStart(lineNo));
4044 ShowCaretAtCurrentPosition();
4045 EnsureCaretVisible();
4048 static bool Close(Point pt1, Point pt2, Point threshold) {
4049 if (std::abs(pt1.x - pt2.x) > threshold.x)
4050 return false;
4051 if (std::abs(pt1.y - pt2.y) > threshold.y)
4052 return false;
4053 return true;
4056 std::string Editor::RangeText(int start, int end) const {
4057 if (start < end) {
4058 int len = end - start;
4059 std::string ret(len, '\0');
4060 for (int i = 0; i < len; i++) {
4061 ret[i] = pdoc->CharAt(start + i);
4063 return ret;
4065 return std::string();
4068 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
4069 if (sel.Empty()) {
4070 if (allowLineCopy) {
4071 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
4072 int start = pdoc->LineStart(currentLine);
4073 int end = pdoc->LineEnd(currentLine);
4075 std::string text = RangeText(start, end);
4076 if (pdoc->eolMode != SC_EOL_LF)
4077 text.push_back('\r');
4078 if (pdoc->eolMode != SC_EOL_CR)
4079 text.push_back('\n');
4080 ss->Copy(text, pdoc->dbcsCodePage,
4081 vs.styles[STYLE_DEFAULT].characterSet, false, true);
4083 } else {
4084 std::string text;
4085 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
4086 if (sel.selType == Selection::selRectangle)
4087 std::sort(rangesInOrder.begin(), rangesInOrder.end());
4088 for (size_t r=0; r<rangesInOrder.size(); r++) {
4089 SelectionRange current = rangesInOrder[r];
4090 text.append(RangeText(current.Start().Position(), current.End().Position()));
4091 if (sel.selType == Selection::selRectangle) {
4092 if (pdoc->eolMode != SC_EOL_LF)
4093 text.push_back('\r');
4094 if (pdoc->eolMode != SC_EOL_CR)
4095 text.push_back('\n');
4098 ss->Copy(text, pdoc->dbcsCodePage,
4099 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
4103 void Editor::CopyRangeToClipboard(int start, int end) {
4104 start = pdoc->ClampPositionIntoDocument(start);
4105 end = pdoc->ClampPositionIntoDocument(end);
4106 SelectionText selectedText;
4107 std::string text = RangeText(start, end);
4108 selectedText.Copy(text,
4109 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
4110 CopyToClipboard(selectedText);
4113 void Editor::CopyText(int length, const char *text) {
4114 SelectionText selectedText;
4115 selectedText.Copy(std::string(text, length),
4116 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
4117 CopyToClipboard(selectedText);
4120 void Editor::SetDragPosition(SelectionPosition newPos) {
4121 if (newPos.Position() >= 0) {
4122 newPos = MovePositionOutsideChar(newPos, 1);
4123 posDrop = newPos;
4125 if (!(posDrag == newPos)) {
4126 caret.on = true;
4127 if (FineTickerAvailable()) {
4128 FineTickerCancel(tickCaret);
4129 if ((caret.active) && (caret.period > 0) && (newPos.Position() < 0))
4130 FineTickerStart(tickCaret, caret.period, caret.period/10);
4131 } else {
4132 SetTicking(true);
4134 InvalidateCaret();
4135 posDrag = newPos;
4136 InvalidateCaret();
4140 void Editor::DisplayCursor(Window::Cursor c) {
4141 if (cursorMode == SC_CURSORNORMAL)
4142 wMain.SetCursor(c);
4143 else
4144 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
4147 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
4148 int xMove = static_cast<int>(ptStart.x - ptNow.x);
4149 int yMove = static_cast<int>(ptStart.y - ptNow.y);
4150 int distanceSquared = xMove * xMove + yMove * yMove;
4151 return distanceSquared > 16;
4154 void Editor::StartDrag() {
4155 // Always handled by subclasses
4156 //SetMouseCapture(true);
4157 //DisplayCursor(Window::cursorArrow);
4160 void Editor::DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular) {
4161 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
4162 if (inDragDrop == ddDragging)
4163 dropWentOutside = false;
4165 bool positionWasInSelection = PositionInSelection(position.Position());
4167 bool positionOnEdgeOfSelection =
4168 (position == SelectionStart()) || (position == SelectionEnd());
4170 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
4171 (positionOnEdgeOfSelection && !moving)) {
4173 SelectionPosition selStart = SelectionStart();
4174 SelectionPosition selEnd = SelectionEnd();
4176 UndoGroup ug(pdoc);
4178 SelectionPosition positionAfterDeletion = position;
4179 if ((inDragDrop == ddDragging) && moving) {
4180 // Remove dragged out text
4181 if (rectangular || sel.selType == Selection::selLines) {
4182 for (size_t r=0; r<sel.Count(); r++) {
4183 if (position >= sel.Range(r).Start()) {
4184 if (position > sel.Range(r).End()) {
4185 positionAfterDeletion.Add(-sel.Range(r).Length());
4186 } else {
4187 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
4191 } else {
4192 if (position > selStart) {
4193 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
4196 ClearSelection();
4198 position = positionAfterDeletion;
4200 std::string convertedText = Document::TransformLineEnds(value, lengthValue, pdoc->eolMode);
4202 if (rectangular) {
4203 PasteRectangular(position, convertedText.c_str(), static_cast<int>(convertedText.length()));
4204 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
4205 SetEmptySelection(position);
4206 } else {
4207 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
4208 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
4209 const int lengthInserted = pdoc->InsertString(
4210 position.Position(), convertedText.c_str(), static_cast<int>(convertedText.length()));
4211 if (lengthInserted > 0) {
4212 SelectionPosition posAfterInsertion = position;
4213 posAfterInsertion.Add(lengthInserted);
4214 SetSelection(posAfterInsertion, position);
4217 } else if (inDragDrop == ddDragging) {
4218 SetEmptySelection(position);
4222 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
4223 DropAt(position, value, strlen(value), moving, rectangular);
4227 * @return true if given position is inside the selection,
4229 bool Editor::PositionInSelection(int pos) {
4230 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
4231 for (size_t r=0; r<sel.Count(); r++) {
4232 if (sel.Range(r).Contains(pos))
4233 return true;
4235 return false;
4238 bool Editor::PointInSelection(Point pt) {
4239 SelectionPosition pos = SPositionFromLocation(pt, false, true);
4240 Point ptPos = LocationFromPosition(pos);
4241 for (size_t r=0; r<sel.Count(); r++) {
4242 SelectionRange range = sel.Range(r);
4243 if (range.Contains(pos)) {
4244 bool hit = true;
4245 if (pos == range.Start()) {
4246 // see if just before selection
4247 if (pt.x < ptPos.x) {
4248 hit = false;
4251 if (pos == range.End()) {
4252 // see if just after selection
4253 if (pt.x > ptPos.x) {
4254 hit = false;
4257 if (hit)
4258 return true;
4261 return false;
4264 bool Editor::PointInSelMargin(Point pt) const {
4265 // Really means: "Point in a margin"
4266 if (vs.fixedColumnWidth > 0) { // There is a margin
4267 PRectangle rcSelMargin = GetClientRectangle();
4268 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart - vs.leftMarginWidth);
4269 rcSelMargin.left = static_cast<XYPOSITION>(vs.textStart - vs.fixedColumnWidth);
4270 return rcSelMargin.ContainsWholePixel(pt);
4271 } else {
4272 return false;
4276 Window::Cursor Editor::GetMarginCursor(Point pt) const {
4277 int x = 0;
4278 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
4279 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
4280 return static_cast<Window::Cursor>(vs.ms[margin].cursor);
4281 x += vs.ms[margin].width;
4283 return Window::cursorReverseArrow;
4286 void Editor::TrimAndSetSelection(int currentPos_, int anchor_) {
4287 sel.TrimSelection(SelectionRange(currentPos_, anchor_));
4288 SetSelection(currentPos_, anchor_);
4291 void Editor::LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine) {
4292 int selCurrentPos, selAnchorPos;
4293 if (wholeLine) {
4294 int lineCurrent_ = pdoc->LineFromPosition(lineCurrentPos_);
4295 int lineAnchor_ = pdoc->LineFromPosition(lineAnchorPos_);
4296 if (lineAnchorPos_ < lineCurrentPos_) {
4297 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
4298 selAnchorPos = pdoc->LineStart(lineAnchor_);
4299 } else if (lineAnchorPos_ > lineCurrentPos_) {
4300 selCurrentPos = pdoc->LineStart(lineCurrent_);
4301 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
4302 } else { // Same line, select it
4303 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
4304 selAnchorPos = pdoc->LineStart(lineAnchor_);
4306 } else {
4307 if (lineAnchorPos_ < lineCurrentPos_) {
4308 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
4309 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4310 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4311 } else if (lineAnchorPos_ > lineCurrentPos_) {
4312 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
4313 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4314 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
4315 } else { // Same line, select it
4316 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4317 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4318 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4321 TrimAndSetSelection(selCurrentPos, selAnchorPos);
4324 void Editor::WordSelection(int pos) {
4325 if (pos < wordSelectAnchorStartPos) {
4326 // Extend backward to the word containing pos.
4327 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
4328 // This ensures that a series of empty lines isn't counted as a single "word".
4329 if (!pdoc->IsLineEndPosition(pos))
4330 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
4331 TrimAndSetSelection(pos, wordSelectAnchorEndPos);
4332 } else if (pos > wordSelectAnchorEndPos) {
4333 // Extend forward to the word containing the character to the left of pos.
4334 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
4335 // This ensures that a series of empty lines isn't counted as a single "word".
4336 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
4337 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
4338 TrimAndSetSelection(pos, wordSelectAnchorStartPos);
4339 } else {
4340 // Select only the anchored word
4341 if (pos >= originalAnchorPos)
4342 TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
4343 else
4344 TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
4348 void Editor::DwellEnd(bool mouseMoved) {
4349 if (mouseMoved)
4350 ticksToDwell = dwellDelay;
4351 else
4352 ticksToDwell = SC_TIME_FOREVER;
4353 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
4354 dwelling = false;
4355 NotifyDwelling(ptMouseLast, dwelling);
4357 if (FineTickerAvailable()) {
4358 FineTickerCancel(tickDwell);
4359 if (mouseMoved && (dwellDelay < SC_TIME_FOREVER)) {
4360 //FineTickerStart(tickDwell, dwellDelay, dwellDelay/10);
4365 void Editor::MouseLeave() {
4366 SetHotSpotRange(NULL);
4367 if (!HaveMouseCapture()) {
4368 ptMouseLast = Point(-1,-1);
4369 DwellEnd(true);
4373 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
4374 return (!rectangular && ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0))
4375 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
4378 void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) {
4379 SetHoverIndicatorPoint(pt);
4380 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
4381 ptMouseLast = pt;
4382 const bool ctrl = (modifiers & SCI_CTRL) != 0;
4383 const bool shift = (modifiers & SCI_SHIFT) != 0;
4384 const bool alt = (modifiers & SCI_ALT) != 0;
4385 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
4386 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4387 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4388 newCharPos = MovePositionOutsideChar(newCharPos, -1);
4389 inDragDrop = ddNone;
4390 sel.SetMoveExtends(false);
4392 if (NotifyMarginClick(pt, modifiers))
4393 return;
4395 NotifyIndicatorClick(true, newPos.Position(), modifiers);
4397 bool inSelMargin = PointInSelMargin(pt);
4398 // In margin ctrl+(double)click should always select everything
4399 if (ctrl && inSelMargin) {
4400 SelectAll();
4401 lastClickTime = curTime;
4402 lastClick = pt;
4403 return;
4405 if (shift && !inSelMargin) {
4406 SetSelection(newPos);
4408 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick, doubleClickCloseThreshold)) {
4409 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
4410 SetMouseCapture(true);
4411 if (FineTickerAvailable()) {
4412 FineTickerStart(tickScroll, 100, 10);
4414 if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))
4415 SetEmptySelection(newPos.Position());
4416 bool doubleClick = false;
4417 // Stop mouse button bounce changing selection type
4418 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
4419 if (inSelMargin) {
4420 // Inside margin selection type should be either selSubLine or selWholeLine.
4421 if (selectionType == selSubLine) {
4422 // If it is selSubLine, we're inside a *double* click and word wrap is enabled,
4423 // so we switch to selWholeLine in order to select whole line.
4424 selectionType = selWholeLine;
4425 } else if (selectionType != selSubLine && selectionType != selWholeLine) {
4426 // If it is neither, reset selection type to line selection.
4427 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4429 } else {
4430 if (selectionType == selChar) {
4431 selectionType = selWord;
4432 doubleClick = true;
4433 } else if (selectionType == selWord) {
4434 // Since we ended up here, we're inside a *triple* click, which should always select
4435 // whole line regardless of word wrap being enabled or not.
4436 selectionType = selWholeLine;
4437 } else {
4438 selectionType = selChar;
4439 originalAnchorPos = sel.MainCaret();
4444 if (selectionType == selWord) {
4445 int charPos = originalAnchorPos;
4446 if (sel.MainCaret() == originalAnchorPos) {
4447 charPos = PositionFromLocation(pt, false, true);
4448 charPos = MovePositionOutsideChar(charPos, -1);
4451 int startWord, endWord;
4452 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
4453 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
4454 endWord = pdoc->ExtendWordSelect(charPos, 1);
4455 } else {
4456 // Selecting backwards, or anchor beyond last character on line. In these cases,
4457 // we select the word containing the character to the *left* of the anchor.
4458 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
4459 startWord = pdoc->ExtendWordSelect(charPos, -1);
4460 endWord = pdoc->ExtendWordSelect(startWord, 1);
4461 } else {
4462 // Anchor at start of line; select nothing to begin with.
4463 startWord = charPos;
4464 endWord = charPos;
4468 wordSelectAnchorStartPos = startWord;
4469 wordSelectAnchorEndPos = endWord;
4470 wordSelectInitialCaretPos = sel.MainCaret();
4471 WordSelection(wordSelectInitialCaretPos);
4472 } else if (selectionType == selSubLine || selectionType == selWholeLine) {
4473 lineAnchorPos = newPos.Position();
4474 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
4475 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
4476 } else {
4477 SetEmptySelection(sel.MainCaret());
4479 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
4480 if (doubleClick) {
4481 NotifyDoubleClick(pt, modifiers);
4482 if (PositionIsHotspot(newCharPos.Position()))
4483 NotifyHotSpotDoubleClicked(newCharPos.Position(), modifiers);
4485 } else { // Single click
4486 if (inSelMargin) {
4487 sel.selType = Selection::selStream;
4488 if (!shift) {
4489 // Single click in margin: select whole line or only subline if word wrap is enabled
4490 lineAnchorPos = newPos.Position();
4491 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4492 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
4493 } else {
4494 // Single shift+click in margin: select from line anchor to clicked line
4495 if (sel.MainAnchor() > sel.MainCaret())
4496 lineAnchorPos = sel.MainAnchor() - 1;
4497 else
4498 lineAnchorPos = sel.MainAnchor();
4499 // Reset selection type if there is an empty selection.
4500 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
4501 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
4502 // This ensures that we continue selecting in the same selection mode.
4503 if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
4504 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4505 LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
4508 SetDragPosition(SelectionPosition(invalidPosition));
4509 SetMouseCapture(true);
4510 if (FineTickerAvailable()) {
4511 FineTickerStart(tickScroll, 100, 10);
4513 } else {
4514 if (PointIsHotspot(pt)) {
4515 NotifyHotSpotClicked(newCharPos.Position(), modifiers);
4516 hotSpotClickPos = newCharPos.Position();
4518 if (!shift) {
4519 if (PointInSelection(pt) && !SelectionEmpty())
4520 inDragDrop = ddInitial;
4521 else
4522 inDragDrop = ddNone;
4524 SetMouseCapture(true);
4525 if (FineTickerAvailable()) {
4526 FineTickerStart(tickScroll, 100, 10);
4528 if (inDragDrop != ddInitial) {
4529 SetDragPosition(SelectionPosition(invalidPosition));
4530 if (!shift) {
4531 if (ctrl && multipleSelection) {
4532 SelectionRange range(newPos);
4533 sel.TentativeSelection(range);
4534 InvalidateSelection(range, true);
4535 } else {
4536 InvalidateSelection(SelectionRange(newPos), true);
4537 if (sel.Count() > 1)
4538 Redraw();
4539 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
4540 sel.Clear();
4541 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
4542 SetSelection(newPos, newPos);
4545 SelectionPosition anchorCurrent = newPos;
4546 if (shift)
4547 anchorCurrent = sel.IsRectangular() ?
4548 sel.Rectangular().anchor : sel.RangeMain().anchor;
4549 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
4550 selectionType = selChar;
4551 originalAnchorPos = sel.MainCaret();
4552 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
4553 SetRectangularRange();
4557 lastClickTime = curTime;
4558 lastClick = pt;
4559 lastXChosen = static_cast<int>(pt.x) + xOffset;
4560 ShowCaretAtCurrentPosition();
4563 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
4564 return ButtonDownWithModifiers(pt, curTime, ModifierFlags(shift, ctrl, alt));
4567 bool Editor::PositionIsHotspot(int position) const {
4568 return vs.styles[pdoc->StyleIndexAt(position)].hotspot;
4571 bool Editor::PointIsHotspot(Point pt) {
4572 int pos = PositionFromLocation(pt, true, true);
4573 if (pos == INVALID_POSITION)
4574 return false;
4575 return PositionIsHotspot(pos);
4578 void Editor::SetHoverIndicatorPosition(int position) {
4579 int hoverIndicatorPosPrev = hoverIndicatorPos;
4580 hoverIndicatorPos = INVALID_POSITION;
4581 if (vs.indicatorsDynamic == 0)
4582 return;
4583 if (position != INVALID_POSITION) {
4584 for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
4585 if (vs.indicators[deco->indicator].IsDynamic()) {
4586 if (pdoc->decorations.ValueAt(deco->indicator, position)) {
4587 hoverIndicatorPos = position;
4592 if (hoverIndicatorPosPrev != hoverIndicatorPos) {
4593 Redraw();
4597 void Editor::SetHoverIndicatorPoint(Point pt) {
4598 if (vs.indicatorsDynamic == 0) {
4599 SetHoverIndicatorPosition(INVALID_POSITION);
4600 } else {
4601 SetHoverIndicatorPosition(PositionFromLocation(pt, true, true));
4605 void Editor::SetHotSpotRange(Point *pt) {
4606 if (pt) {
4607 int pos = PositionFromLocation(*pt, false, true);
4609 // If we don't limit this to word characters then the
4610 // range can encompass more than the run range and then
4611 // the underline will not be drawn properly.
4612 Range hsNew;
4613 hsNew.start = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
4614 hsNew.end = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
4616 // Only invalidate the range if the hotspot range has changed...
4617 if (!(hsNew == hotspot)) {
4618 if (hotspot.Valid()) {
4619 InvalidateRange(hotspot.start, hotspot.end);
4621 hotspot = hsNew;
4622 InvalidateRange(hotspot.start, hotspot.end);
4624 } else {
4625 if (hotspot.Valid()) {
4626 InvalidateRange(hotspot.start, hotspot.end);
4628 hotspot = Range(invalidPosition);
4632 Range Editor::GetHotSpotRange() const {
4633 return hotspot;
4636 void Editor::ButtonMoveWithModifiers(Point pt, int modifiers) {
4637 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
4638 DwellEnd(true);
4641 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
4642 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4643 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
4645 if (inDragDrop == ddInitial) {
4646 if (DragThreshold(ptMouseLast, pt)) {
4647 SetMouseCapture(false);
4648 if (FineTickerAvailable()) {
4649 FineTickerCancel(tickScroll);
4651 SetDragPosition(movePos);
4652 CopySelectionRange(&drag);
4653 StartDrag();
4655 return;
4658 ptMouseLast = pt;
4659 PRectangle rcClient = GetClientRectangle();
4660 Point ptOrigin = GetVisibleOriginInMain();
4661 rcClient.Move(0, -ptOrigin.y);
4662 if (FineTickerAvailable() && (dwellDelay < SC_TIME_FOREVER) && rcClient.Contains(pt)) {
4663 FineTickerStart(tickDwell, dwellDelay, dwellDelay/10);
4665 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
4666 if (HaveMouseCapture()) {
4668 // Slow down autoscrolling/selection
4669 autoScrollTimer.ticksToWait -= timer.tickSize;
4670 if (autoScrollTimer.ticksToWait > 0)
4671 return;
4672 autoScrollTimer.ticksToWait = autoScrollDelay;
4674 // Adjust selection
4675 if (posDrag.IsValid()) {
4676 SetDragPosition(movePos);
4677 } else {
4678 if (selectionType == selChar) {
4679 if (sel.selType == Selection::selStream && (modifiers & SCI_ALT) && mouseSelectionRectangularSwitch) {
4680 sel.selType = Selection::selRectangle;
4682 if (sel.IsRectangular()) {
4683 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
4684 SetSelection(movePos, sel.RangeMain().anchor);
4685 } else if (sel.Count() > 1) {
4686 InvalidateSelection(sel.RangeMain(), false);
4687 SelectionRange range(movePos, sel.RangeMain().anchor);
4688 sel.TentativeSelection(range);
4689 InvalidateSelection(range, true);
4690 } else {
4691 SetSelection(movePos, sel.RangeMain().anchor);
4693 } else if (selectionType == selWord) {
4694 // Continue selecting by word
4695 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
4696 // No need to do anything. Previously this case was lumped
4697 // in with "Moved forward", but that can be harmful in this
4698 // case: a handler for the NotifyDoubleClick re-adjusts
4699 // the selection for a fancier definition of "word" (for
4700 // example, in Perl it is useful to include the leading
4701 // '$', '%' or '@' on variables for word selection). In this
4702 // the ButtonMove() called via Tick() for auto-scrolling
4703 // could result in the fancier word selection adjustment
4704 // being unmade.
4705 } else {
4706 wordSelectInitialCaretPos = -1;
4707 WordSelection(movePos.Position());
4709 } else {
4710 // Continue selecting by line
4711 LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
4715 // Autoscroll
4716 int lineMove = DisplayFromPosition(movePos.Position());
4717 if (pt.y > rcClient.bottom) {
4718 ScrollTo(lineMove - LinesOnScreen() + 1);
4719 Redraw();
4720 } else if (pt.y < rcClient.top) {
4721 ScrollTo(lineMove);
4722 Redraw();
4724 EnsureCaretVisible(false, false, true);
4726 if (hotspot.Valid() && !PointIsHotspot(pt))
4727 SetHotSpotRange(NULL);
4729 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,true) != hotSpotClickPos) {
4730 if (inDragDrop == ddNone) {
4731 DisplayCursor(Window::cursorText);
4733 hotSpotClickPos = INVALID_POSITION;
4736 } else {
4737 if (vs.fixedColumnWidth > 0) { // There is a margin
4738 if (PointInSelMargin(pt)) {
4739 DisplayCursor(GetMarginCursor(pt));
4740 SetHotSpotRange(NULL);
4741 return; // No need to test for selection
4744 // Display regular (drag) cursor over selection
4745 if (PointInSelection(pt) && !SelectionEmpty()) {
4746 DisplayCursor(Window::cursorArrow);
4747 } else {
4748 SetHoverIndicatorPoint(pt);
4749 if (PointIsHotspot(pt)) {
4750 DisplayCursor(Window::cursorHand);
4751 SetHotSpotRange(&pt);
4752 } else {
4753 if (hoverIndicatorPos != invalidPosition)
4754 DisplayCursor(Window::cursorHand);
4755 else
4756 DisplayCursor(Window::cursorText);
4757 SetHotSpotRange(NULL);
4763 void Editor::ButtonMove(Point pt) {
4764 ButtonMoveWithModifiers(pt, 0);
4767 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
4768 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
4769 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
4770 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4771 if (hoverIndicatorPos != INVALID_POSITION)
4772 InvalidateRange(newPos.Position(), newPos.Position() + 1);
4773 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4774 if (inDragDrop == ddInitial) {
4775 inDragDrop = ddNone;
4776 SetEmptySelection(newPos);
4777 selectionType = selChar;
4778 originalAnchorPos = sel.MainCaret();
4780 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
4781 hotSpotClickPos = INVALID_POSITION;
4782 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4783 newCharPos = MovePositionOutsideChar(newCharPos, -1);
4784 NotifyHotSpotReleaseClick(newCharPos.Position(), ctrl ? SCI_CTRL : 0);
4786 if (HaveMouseCapture()) {
4787 if (PointInSelMargin(pt)) {
4788 DisplayCursor(GetMarginCursor(pt));
4789 } else {
4790 DisplayCursor(Window::cursorText);
4791 SetHotSpotRange(NULL);
4793 ptMouseLast = pt;
4794 SetMouseCapture(false);
4795 if (FineTickerAvailable()) {
4796 FineTickerCancel(tickScroll);
4798 NotifyIndicatorClick(false, newPos.Position(), 0);
4799 if (inDragDrop == ddDragging) {
4800 SelectionPosition selStart = SelectionStart();
4801 SelectionPosition selEnd = SelectionEnd();
4802 if (selStart < selEnd) {
4803 if (drag.Length()) {
4804 const int length = static_cast<int>(drag.Length());
4805 if (ctrl) {
4806 const int lengthInserted = pdoc->InsertString(
4807 newPos.Position(), drag.Data(), length);
4808 if (lengthInserted > 0) {
4809 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4811 } else if (newPos < selStart) {
4812 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
4813 const int lengthInserted = pdoc->InsertString(
4814 newPos.Position(), drag.Data(), length);
4815 if (lengthInserted > 0) {
4816 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4818 } else if (newPos > selEnd) {
4819 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
4820 newPos.Add(-static_cast<int>(drag.Length()));
4821 const int lengthInserted = pdoc->InsertString(
4822 newPos.Position(), drag.Data(), length);
4823 if (lengthInserted > 0) {
4824 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4826 } else {
4827 SetEmptySelection(newPos.Position());
4829 drag.Clear();
4831 selectionType = selChar;
4833 } else {
4834 if (selectionType == selChar) {
4835 if (sel.Count() > 1) {
4836 sel.RangeMain() =
4837 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
4838 InvalidateWholeSelection();
4839 } else {
4840 SetSelection(newPos, sel.RangeMain().anchor);
4843 sel.CommitTentative();
4845 SetRectangularRange();
4846 lastClickTime = curTime;
4847 lastClick = pt;
4848 lastXChosen = static_cast<int>(pt.x) + xOffset;
4849 if (sel.selType == Selection::selStream) {
4850 SetLastXChosen();
4852 inDragDrop = ddNone;
4853 EnsureCaretVisible(false);
4857 // Called frequently to perform background UI including
4858 // caret blinking and automatic scrolling.
4859 void Editor::Tick() {
4860 if (HaveMouseCapture()) {
4861 // Auto scroll
4862 ButtonMove(ptMouseLast);
4864 if (caret.period > 0) {
4865 timer.ticksToWait -= timer.tickSize;
4866 if (timer.ticksToWait <= 0) {
4867 caret.on = !caret.on;
4868 timer.ticksToWait = caret.period;
4869 if (caret.active) {
4870 InvalidateCaret();
4874 if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) {
4875 scrollWidth = view.lineWidthMaxSeen;
4876 SetScrollBars();
4878 if ((dwellDelay < SC_TIME_FOREVER) &&
4879 (ticksToDwell > 0) &&
4880 (!HaveMouseCapture()) &&
4881 (ptMouseLast.y >= 0)) {
4882 ticksToDwell -= timer.tickSize;
4883 if (ticksToDwell <= 0) {
4884 dwelling = true;
4885 NotifyDwelling(ptMouseLast, dwelling);
4890 bool Editor::Idle() {
4891 bool needWrap = Wrapping() && wrapPending.NeedsWrap();
4893 if (needWrap) {
4894 // Wrap lines during idle.
4895 WrapLines(wsIdle);
4896 // No more wrapping
4897 needWrap = wrapPending.NeedsWrap();
4898 } else if (needIdleStyling) {
4899 IdleStyling();
4902 // Add more idle things to do here, but make sure idleDone is
4903 // set correctly before the function returns. returning
4904 // false will stop calling this idle function until SetIdle() is
4905 // called again.
4907 const bool idleDone = !needWrap && !needIdleStyling; // && thatDone && theOtherThingDone...
4909 return !idleDone;
4912 void Editor::SetTicking(bool) {
4913 // SetTicking is deprecated. In the past it was pure virtual and was overridden in each
4914 // derived platform class but fine grained timers should now be implemented.
4915 // Either way, execution should not arrive here so assert failure.
4916 assert(false);
4919 void Editor::TickFor(TickReason reason) {
4920 switch (reason) {
4921 case tickCaret:
4922 caret.on = !caret.on;
4923 if (caret.active) {
4924 InvalidateCaret();
4926 break;
4927 case tickScroll:
4928 // Auto scroll
4929 ButtonMove(ptMouseLast);
4930 break;
4931 case tickWiden:
4932 SetScrollBars();
4933 FineTickerCancel(tickWiden);
4934 break;
4935 case tickDwell:
4936 if ((!HaveMouseCapture()) &&
4937 (ptMouseLast.y >= 0)) {
4938 dwelling = true;
4939 NotifyDwelling(ptMouseLast, dwelling);
4941 FineTickerCancel(tickDwell);
4942 break;
4943 default:
4944 // tickPlatform handled by subclass
4945 break;
4949 bool Editor::FineTickerAvailable() {
4950 return false;
4953 // FineTickerStart is be overridden by subclasses that support fine ticking so
4954 // this method should never be called.
4955 bool Editor::FineTickerRunning(TickReason) {
4956 assert(false);
4957 return false;
4960 // FineTickerStart is be overridden by subclasses that support fine ticking so
4961 // this method should never be called.
4962 void Editor::FineTickerStart(TickReason, int, int) {
4963 assert(false);
4966 // FineTickerCancel is be overridden by subclasses that support fine ticking so
4967 // this method should never be called.
4968 void Editor::FineTickerCancel(TickReason) {
4969 assert(false);
4972 void Editor::SetFocusState(bool focusState) {
4973 hasFocus = focusState;
4974 NotifyFocus(hasFocus);
4975 if (!hasFocus) {
4976 CancelModes();
4978 ShowCaretAtCurrentPosition();
4981 int Editor::PositionAfterArea(PRectangle rcArea) const {
4982 // The start of the document line after the display line after the area
4983 // This often means that the line after a modification is restyled which helps
4984 // detect multiline comment additions and heals single line comments
4985 int lineAfter = TopLineOfMain() + static_cast<int>(rcArea.bottom - 1) / vs.lineHeight + 1;
4986 if (lineAfter < cs.LinesDisplayed())
4987 return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
4988 else
4989 return pdoc->Length();
4992 // Style to a position within the view. If this causes a change at end of last line then
4993 // affects later lines so style all the viewed text.
4994 void Editor::StyleToPositionInView(Position pos) {
4995 int endWindow = PositionAfterArea(GetClientDrawingRectangle());
4996 if (pos > endWindow)
4997 pos = endWindow;
4998 const int styleAtEnd = pdoc->StyleIndexAt(pos-1);
4999 pdoc->EnsureStyledTo(pos);
5000 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleIndexAt(pos-1))) {
5001 // Style at end of line changed so is multi-line change like starting a comment
5002 // so require rest of window to be styled.
5003 DiscardOverdraw(); // Prepared bitmaps may be invalid
5004 // DiscardOverdraw may have truncated client drawing area so recalculate endWindow
5005 endWindow = PositionAfterArea(GetClientDrawingRectangle());
5006 pdoc->EnsureStyledTo(endWindow);
5010 int Editor::PositionAfterMaxStyling(int posMax, bool scrolling) const {
5011 if ((idleStyling == SC_IDLESTYLING_NONE) || (idleStyling == SC_IDLESTYLING_AFTERVISIBLE)) {
5012 // Both states do not limit styling
5013 return posMax;
5016 // Try to keep time taken by styling reasonable so interaction remains smooth.
5017 // When scrolling, allow less time to ensure responsive
5018 const double secondsAllowed = scrolling ? 0.005 : 0.02;
5020 const int linesToStyle = Platform::Clamp(static_cast<int>(secondsAllowed / pdoc->durationStyleOneLine),
5021 10, 0x10000);
5022 const int stylingMaxLine = std::min(
5023 static_cast<int>(pdoc->LineFromPosition(pdoc->GetEndStyled()) + linesToStyle),
5024 pdoc->LinesTotal());
5025 return std::min(static_cast<int>(pdoc->LineStart(stylingMaxLine)), posMax);
5028 void Editor::StartIdleStyling(bool truncatedLastStyling) {
5029 if ((idleStyling == SC_IDLESTYLING_ALL) || (idleStyling == SC_IDLESTYLING_AFTERVISIBLE)) {
5030 if (pdoc->GetEndStyled() < pdoc->Length()) {
5031 // Style remainder of document in idle time
5032 needIdleStyling = true;
5034 } else if (truncatedLastStyling) {
5035 needIdleStyling = true;
5038 if (needIdleStyling) {
5039 SetIdle(true);
5043 // Style for an area but bound the amount of styling to remain responsive
5044 void Editor::StyleAreaBounded(PRectangle rcArea, bool scrolling) {
5045 const int posAfterArea = PositionAfterArea(rcArea);
5046 const int posAfterMax = PositionAfterMaxStyling(posAfterArea, scrolling);
5047 if (posAfterMax < posAfterArea) {
5048 // Idle styling may be performed before current visible area
5049 // Style a bit now then style further in idle time
5050 pdoc->StyleToAdjustingLineDuration(posAfterMax);
5051 } else {
5052 // Can style all wanted now.
5053 StyleToPositionInView(posAfterArea);
5055 StartIdleStyling(posAfterMax < posAfterArea);
5058 void Editor::IdleStyling() {
5059 const int posAfterArea = PositionAfterArea(GetClientRectangle());
5060 const int endGoal = (idleStyling >= SC_IDLESTYLING_AFTERVISIBLE) ?
5061 pdoc->Length() : posAfterArea;
5062 const int posAfterMax = PositionAfterMaxStyling(endGoal, false);
5063 pdoc->StyleToAdjustingLineDuration(posAfterMax);
5064 if (pdoc->GetEndStyled() >= endGoal) {
5065 needIdleStyling = false;
5069 void Editor::IdleWork() {
5070 // Style the line after the modification as this allows modifications that change just the
5071 // line of the modification to heal instead of propagating to the rest of the window.
5072 if (workNeeded.items & WorkNeeded::workStyle) {
5073 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2));
5075 NotifyUpdateUI();
5076 workNeeded.Reset();
5079 void Editor::QueueIdleWork(WorkNeeded::workItems items, int upTo) {
5080 workNeeded.Need(items, upTo);
5083 bool Editor::PaintContains(PRectangle rc) {
5084 if (rc.Empty()) {
5085 return true;
5086 } else {
5087 return rcPaint.Contains(rc);
5091 bool Editor::PaintContainsMargin() {
5092 if (wMargin.GetID()) {
5093 // With separate margin view, paint of text view
5094 // never contains margin.
5095 return false;
5097 PRectangle rcSelMargin = GetClientRectangle();
5098 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart);
5099 return PaintContains(rcSelMargin);
5102 void Editor::CheckForChangeOutsidePaint(Range r) {
5103 if (paintState == painting && !paintingAllText) {
5104 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
5105 if (!r.Valid())
5106 return;
5108 PRectangle rcRange = RectangleFromRange(r, 0);
5109 PRectangle rcText = GetTextRectangle();
5110 if (rcRange.top < rcText.top) {
5111 rcRange.top = rcText.top;
5113 if (rcRange.bottom > rcText.bottom) {
5114 rcRange.bottom = rcText.bottom;
5117 if (!PaintContains(rcRange)) {
5118 AbandonPaint();
5119 paintAbandonedByStyling = true;
5124 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
5125 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
5126 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
5127 CheckForChangeOutsidePaint(Range(braces[0]));
5128 CheckForChangeOutsidePaint(Range(pos0));
5129 braces[0] = pos0;
5131 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
5132 CheckForChangeOutsidePaint(Range(braces[1]));
5133 CheckForChangeOutsidePaint(Range(pos1));
5134 braces[1] = pos1;
5136 bracesMatchStyle = matchStyle;
5137 if (paintState == notPainting) {
5138 Redraw();
5143 void Editor::SetAnnotationHeights(int start, int end) {
5144 if (vs.annotationVisible) {
5145 RefreshStyleData();
5146 bool changedHeight = false;
5147 for (int line=start; line<end && line<pdoc->LinesTotal(); line++) {
5148 int linesWrapped = 1;
5149 if (Wrapping()) {
5150 AutoSurface surface(this);
5151 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
5152 if (surface && ll) {
5153 view.LayoutLine(*this, line, surface, vs, ll, wrapWidth);
5154 linesWrapped = ll->lines;
5157 if (cs.SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
5158 changedHeight = true;
5160 if (changedHeight) {
5161 Redraw();
5166 void Editor::SetDocPointer(Document *document) {
5167 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
5168 pdoc->RemoveWatcher(this, 0);
5169 pdoc->Release();
5170 if (document == NULL) {
5171 pdoc = new Document();
5172 } else {
5173 pdoc = document;
5175 pdoc->AddRef();
5177 // Ensure all positions within document
5178 sel.Clear();
5179 targetStart = 0;
5180 targetEnd = 0;
5182 braces[0] = invalidPosition;
5183 braces[1] = invalidPosition;
5185 vs.ReleaseAllExtendedStyles();
5187 SetRepresentations();
5189 // Reset the contraction state to fully shown.
5190 cs.Clear();
5191 cs.InsertLines(0, pdoc->LinesTotal() - 1);
5192 SetAnnotationHeights(0, pdoc->LinesTotal());
5193 view.llc.Deallocate();
5194 NeedWrapping();
5196 hotspot = Range(invalidPosition);
5197 hoverIndicatorPos = invalidPosition;
5199 view.ClearAllTabstops();
5201 pdoc->AddWatcher(this, 0);
5202 SetScrollBars();
5203 Redraw();
5206 void Editor::SetAnnotationVisible(int visible) {
5207 if (vs.annotationVisible != visible) {
5208 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
5209 vs.annotationVisible = visible;
5210 if (changedFromOrToHidden) {
5211 int dir = vs.annotationVisible ? 1 : -1;
5212 for (int line=0; line<pdoc->LinesTotal(); line++) {
5213 int annotationLines = pdoc->AnnotationLines(line);
5214 if (annotationLines > 0) {
5215 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
5219 Redraw();
5224 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
5226 int Editor::ExpandLine(int line) {
5227 int lineMaxSubord = pdoc->GetLastChild(line);
5228 line++;
5229 while (line <= lineMaxSubord) {
5230 cs.SetVisible(line, line, true);
5231 int level = pdoc->GetLevel(line);
5232 if (level & SC_FOLDLEVELHEADERFLAG) {
5233 if (cs.GetExpanded(line)) {
5234 line = ExpandLine(line);
5235 } else {
5236 line = pdoc->GetLastChild(line);
5239 line++;
5241 return lineMaxSubord;
5244 void Editor::SetFoldExpanded(int lineDoc, bool expanded) {
5245 if (cs.SetExpanded(lineDoc, expanded)) {
5246 RedrawSelMargin();
5250 void Editor::FoldLine(int line, int action) {
5251 if (line >= 0) {
5252 if (action == SC_FOLDACTION_TOGGLE) {
5253 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
5254 line = pdoc->GetFoldParent(line);
5255 if (line < 0)
5256 return;
5258 action = (cs.GetExpanded(line)) ? SC_FOLDACTION_CONTRACT : SC_FOLDACTION_EXPAND;
5261 if (action == SC_FOLDACTION_CONTRACT) {
5262 int lineMaxSubord = pdoc->GetLastChild(line);
5263 if (lineMaxSubord > line) {
5264 cs.SetExpanded(line, 0);
5265 cs.SetVisible(line + 1, lineMaxSubord, false);
5267 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
5268 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
5269 // This does not re-expand the fold
5270 EnsureCaretVisible();
5274 } else {
5275 if (!(cs.GetVisible(line))) {
5276 EnsureLineVisible(line, false);
5277 GoToLine(line);
5279 cs.SetExpanded(line, 1);
5280 ExpandLine(line);
5283 SetScrollBars();
5284 Redraw();
5288 void Editor::FoldExpand(int line, int action, int level) {
5289 bool expanding = action == SC_FOLDACTION_EXPAND;
5290 if (action == SC_FOLDACTION_TOGGLE) {
5291 expanding = !cs.GetExpanded(line);
5293 SetFoldExpanded(line, expanding);
5294 if (expanding && (cs.HiddenLines() == 0))
5295 // Nothing to do
5296 return;
5297 int lineMaxSubord = pdoc->GetLastChild(line, LevelNumber(level));
5298 line++;
5299 cs.SetVisible(line, lineMaxSubord, expanding);
5300 while (line <= lineMaxSubord) {
5301 int levelLine = pdoc->GetLevel(line);
5302 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
5303 SetFoldExpanded(line, expanding);
5305 line++;
5307 SetScrollBars();
5308 Redraw();
5311 int Editor::ContractedFoldNext(int lineStart) const {
5312 for (int line = lineStart; line<pdoc->LinesTotal();) {
5313 if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
5314 return line;
5315 line = cs.ContractedNext(line+1);
5316 if (line < 0)
5317 return -1;
5320 return -1;
5324 * Recurse up from this line to find any folds that prevent this line from being visible
5325 * and unfold them all.
5327 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
5329 // In case in need of wrapping to ensure DisplayFromDoc works.
5330 if (lineDoc >= wrapPending.start)
5331 WrapLines(wsAll);
5333 if (!cs.GetVisible(lineDoc)) {
5334 // Back up to find a non-blank line
5335 int lookLine = lineDoc;
5336 int lookLineLevel = pdoc->GetLevel(lookLine);
5337 while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
5338 lookLineLevel = pdoc->GetLevel(--lookLine);
5340 int lineParent = pdoc->GetFoldParent(lookLine);
5341 if (lineParent < 0) {
5342 // Backed up to a top level line, so try to find parent of initial line
5343 lineParent = pdoc->GetFoldParent(lineDoc);
5345 if (lineParent >= 0) {
5346 if (lineDoc != lineParent)
5347 EnsureLineVisible(lineParent, enforcePolicy);
5348 if (!cs.GetExpanded(lineParent)) {
5349 cs.SetExpanded(lineParent, 1);
5350 ExpandLine(lineParent);
5353 SetScrollBars();
5354 Redraw();
5356 if (enforcePolicy) {
5357 int lineDisplay = cs.DisplayFromDoc(lineDoc);
5358 if (visiblePolicy & VISIBLE_SLOP) {
5359 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
5360 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
5361 SetVerticalScrollPos();
5362 Redraw();
5363 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
5364 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
5365 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
5366 SetVerticalScrollPos();
5367 Redraw();
5369 } else {
5370 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
5371 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
5372 SetVerticalScrollPos();
5373 Redraw();
5379 void Editor::FoldAll(int action) {
5380 pdoc->EnsureStyledTo(pdoc->Length());
5381 int maxLine = pdoc->LinesTotal();
5382 bool expanding = action == SC_FOLDACTION_EXPAND;
5383 if (action == SC_FOLDACTION_TOGGLE) {
5384 // Discover current state
5385 for (int lineSeek = 0; lineSeek < maxLine; lineSeek++) {
5386 if (pdoc->GetLevel(lineSeek) & SC_FOLDLEVELHEADERFLAG) {
5387 expanding = !cs.GetExpanded(lineSeek);
5388 break;
5392 if (expanding) {
5393 cs.SetVisible(0, maxLine-1, true);
5394 for (int line = 0; line < maxLine; line++) {
5395 int levelLine = pdoc->GetLevel(line);
5396 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
5397 SetFoldExpanded(line, true);
5400 } else {
5401 for (int line = 0; line < maxLine; line++) {
5402 int level = pdoc->GetLevel(line);
5403 if ((level & SC_FOLDLEVELHEADERFLAG) &&
5404 (SC_FOLDLEVELBASE == LevelNumber(level))) {
5405 SetFoldExpanded(line, false);
5406 int lineMaxSubord = pdoc->GetLastChild(line, -1);
5407 if (lineMaxSubord > line) {
5408 cs.SetVisible(line + 1, lineMaxSubord, false);
5413 SetScrollBars();
5414 Redraw();
5417 void Editor::FoldChanged(int line, int levelNow, int levelPrev) {
5418 if (levelNow & SC_FOLDLEVELHEADERFLAG) {
5419 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
5420 // Adding a fold point.
5421 if (cs.SetExpanded(line, true)) {
5422 RedrawSelMargin();
5424 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
5426 } else if (levelPrev & SC_FOLDLEVELHEADERFLAG) {
5427 const int prevLine = line - 1;
5428 const int prevLineLevel = pdoc->GetLevel(prevLine);
5430 // Combining two blocks where the first block is collapsed (e.g. by deleting the line(s) which separate(s) the two blocks)
5431 if ((LevelNumber(prevLineLevel) == LevelNumber(levelNow)) && !cs.GetVisible(prevLine))
5432 FoldLine(pdoc->GetFoldParent(prevLine), SC_FOLDACTION_EXPAND);
5434 if (!cs.GetExpanded(line)) {
5435 // Removing the fold from one that has been contracted so should expand
5436 // otherwise lines are left invisible with no way to make them visible
5437 if (cs.SetExpanded(line, true)) {
5438 RedrawSelMargin();
5440 // Combining two blocks where the second one is collapsed (e.g. by adding characters in the line which separates the two blocks)
5441 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
5444 if (!(levelNow & SC_FOLDLEVELWHITEFLAG) &&
5445 (LevelNumber(levelPrev) > LevelNumber(levelNow))) {
5446 if (cs.HiddenLines()) {
5447 // See if should still be hidden
5448 int parentLine = pdoc->GetFoldParent(line);
5449 if ((parentLine < 0) || (cs.GetExpanded(parentLine) && cs.GetVisible(parentLine))) {
5450 cs.SetVisible(line, line, true);
5451 SetScrollBars();
5452 Redraw();
5457 // Combining two blocks where the first one is collapsed (e.g. by adding characters in the line which separates the two blocks)
5458 if (!(levelNow & SC_FOLDLEVELWHITEFLAG) && (LevelNumber(levelPrev) < LevelNumber(levelNow))) {
5459 if (cs.HiddenLines()) {
5460 const int parentLine = pdoc->GetFoldParent(line);
5461 if (!cs.GetExpanded(parentLine) && cs.GetExpanded(line))
5462 FoldLine(parentLine, SC_FOLDACTION_EXPAND);
5467 void Editor::NeedShown(int pos, int len) {
5468 if (foldAutomatic & SC_AUTOMATICFOLD_SHOW) {
5469 int lineStart = pdoc->LineFromPosition(pos);
5470 int lineEnd = pdoc->LineFromPosition(pos+len);
5471 for (int line = lineStart; line <= lineEnd; line++) {
5472 EnsureLineVisible(line, false);
5474 } else {
5475 NotifyNeedShown(pos, len);
5479 int Editor::GetTag(char *tagValue, int tagNumber) {
5480 const char *text = 0;
5481 int length = 0;
5482 if ((tagNumber >= 1) && (tagNumber <= 9)) {
5483 char name[3] = "\\?";
5484 name[1] = static_cast<char>(tagNumber + '0');
5485 length = 2;
5486 text = pdoc->SubstituteByPosition(name, &length);
5488 if (tagValue) {
5489 if (text)
5490 memcpy(tagValue, text, length + 1);
5491 else
5492 *tagValue = '\0';
5494 return length;
5497 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
5498 UndoGroup ug(pdoc);
5499 if (length == -1)
5500 length = istrlen(text);
5501 if (replacePatterns) {
5502 text = pdoc->SubstituteByPosition(text, &length);
5503 if (!text) {
5504 return 0;
5507 if (targetStart != targetEnd)
5508 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
5509 targetEnd = targetStart;
5510 const int lengthInserted = pdoc->InsertString(targetStart, text, length);
5511 targetEnd = targetStart + lengthInserted;
5512 return length;
5515 bool Editor::IsUnicodeMode() const {
5516 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
5519 int Editor::CodePage() const {
5520 if (pdoc)
5521 return pdoc->dbcsCodePage;
5522 else
5523 return 0;
5526 int Editor::WrapCount(int line) {
5527 AutoSurface surface(this);
5528 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
5530 if (surface && ll) {
5531 view.LayoutLine(*this, line, surface, vs, ll, wrapWidth);
5532 return ll->lines;
5533 } else {
5534 return 1;
5538 void Editor::AddStyledText(char *buffer, int appendLength) {
5539 // The buffer consists of alternating character bytes and style bytes
5540 int textLength = appendLength / 2;
5541 std::string text(textLength, '\0');
5542 int i;
5543 for (i = 0; i < textLength; i++) {
5544 text[i] = buffer[i*2];
5546 const int lengthInserted = pdoc->InsertString(CurrentPosition(), text.c_str(), textLength);
5547 for (i = 0; i < textLength; i++) {
5548 text[i] = buffer[i*2+1];
5550 pdoc->StartStyling(CurrentPosition(), static_cast<unsigned char>(0xff));
5551 pdoc->SetStyles(textLength, text.c_str());
5552 SetEmptySelection(sel.MainCaret() + lengthInserted);
5555 static bool ValidMargin(uptr_t wParam) {
5556 return wParam <= SC_MAX_MARGIN;
5559 static char *CharPtrFromSPtr(sptr_t lParam) {
5560 return reinterpret_cast<char *>(lParam);
5563 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5564 vs.EnsureStyle(wParam);
5565 switch (iMessage) {
5566 case SCI_STYLESETFORE:
5567 vs.styles[wParam].fore = ColourDesired(static_cast<long>(lParam));
5568 break;
5569 case SCI_STYLESETBACK:
5570 vs.styles[wParam].back = ColourDesired(static_cast<long>(lParam));
5571 break;
5572 case SCI_STYLESETBOLD:
5573 vs.styles[wParam].weight = lParam != 0 ? SC_WEIGHT_BOLD : SC_WEIGHT_NORMAL;
5574 break;
5575 case SCI_STYLESETWEIGHT:
5576 vs.styles[wParam].weight = static_cast<int>(lParam);
5577 break;
5578 case SCI_STYLESETITALIC:
5579 vs.styles[wParam].italic = lParam != 0;
5580 break;
5581 case SCI_STYLESETEOLFILLED:
5582 vs.styles[wParam].eolFilled = lParam != 0;
5583 break;
5584 case SCI_STYLESETSIZE:
5585 vs.styles[wParam].size = static_cast<int>(lParam * SC_FONT_SIZE_MULTIPLIER);
5586 break;
5587 case SCI_STYLESETSIZEFRACTIONAL:
5588 vs.styles[wParam].size = static_cast<int>(lParam);
5589 break;
5590 case SCI_STYLESETFONT:
5591 if (lParam != 0) {
5592 vs.SetStyleFontName(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
5594 break;
5595 case SCI_STYLESETUNDERLINE:
5596 vs.styles[wParam].underline = lParam != 0;
5597 break;
5598 case SCI_STYLESETCASE:
5599 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
5600 break;
5601 case SCI_STYLESETCHARACTERSET:
5602 vs.styles[wParam].characterSet = static_cast<int>(lParam);
5603 pdoc->SetCaseFolder(NULL);
5604 break;
5605 case SCI_STYLESETVISIBLE:
5606 vs.styles[wParam].visible = lParam != 0;
5607 break;
5608 case SCI_STYLESETCHANGEABLE:
5609 vs.styles[wParam].changeable = lParam != 0;
5610 break;
5611 case SCI_STYLESETHOTSPOT:
5612 vs.styles[wParam].hotspot = lParam != 0;
5613 break;
5615 InvalidateStyleRedraw();
5618 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5619 vs.EnsureStyle(wParam);
5620 switch (iMessage) {
5621 case SCI_STYLEGETFORE:
5622 return vs.styles[wParam].fore.AsLong();
5623 case SCI_STYLEGETBACK:
5624 return vs.styles[wParam].back.AsLong();
5625 case SCI_STYLEGETBOLD:
5626 return vs.styles[wParam].weight > SC_WEIGHT_NORMAL;
5627 case SCI_STYLEGETWEIGHT:
5628 return vs.styles[wParam].weight;
5629 case SCI_STYLEGETITALIC:
5630 return vs.styles[wParam].italic ? 1 : 0;
5631 case SCI_STYLEGETEOLFILLED:
5632 return vs.styles[wParam].eolFilled ? 1 : 0;
5633 case SCI_STYLEGETSIZE:
5634 return vs.styles[wParam].size / SC_FONT_SIZE_MULTIPLIER;
5635 case SCI_STYLEGETSIZEFRACTIONAL:
5636 return vs.styles[wParam].size;
5637 case SCI_STYLEGETFONT:
5638 return StringResult(lParam, vs.styles[wParam].fontName);
5639 case SCI_STYLEGETUNDERLINE:
5640 return vs.styles[wParam].underline ? 1 : 0;
5641 case SCI_STYLEGETCASE:
5642 return static_cast<int>(vs.styles[wParam].caseForce);
5643 case SCI_STYLEGETCHARACTERSET:
5644 return vs.styles[wParam].characterSet;
5645 case SCI_STYLEGETVISIBLE:
5646 return vs.styles[wParam].visible ? 1 : 0;
5647 case SCI_STYLEGETCHANGEABLE:
5648 return vs.styles[wParam].changeable ? 1 : 0;
5649 case SCI_STYLEGETHOTSPOT:
5650 return vs.styles[wParam].hotspot ? 1 : 0;
5652 return 0;
5655 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
5656 const size_t len = val ? strlen(val) : 0;
5657 if (lParam) {
5658 char *ptr = CharPtrFromSPtr(lParam);
5659 if (val)
5660 memcpy(ptr, val, len+1);
5661 else
5662 *ptr = 0;
5664 return len; // Not including NUL
5667 sptr_t Editor::BytesResult(sptr_t lParam, const unsigned char *val, size_t len) {
5668 // No NUL termination: len is number of valid/displayed bytes
5669 if ((lParam) && (len > 0)) {
5670 char *ptr = CharPtrFromSPtr(lParam);
5671 if (val)
5672 memcpy(ptr, val, len);
5673 else
5674 *ptr = 0;
5676 return val ? len : 0;
5679 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5680 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
5682 // Optional macro recording hook
5683 if (recordingMacro)
5684 NotifyMacroRecord(iMessage, wParam, lParam);
5686 switch (iMessage) {
5688 case SCI_GETTEXT: {
5689 if (lParam == 0)
5690 return pdoc->Length() + 1;
5691 if (wParam == 0)
5692 return 0;
5693 char *ptr = CharPtrFromSPtr(lParam);
5694 unsigned int iChar = 0;
5695 for (; iChar < wParam - 1; iChar++)
5696 ptr[iChar] = pdoc->CharAt(iChar);
5697 ptr[iChar] = '\0';
5698 return iChar;
5701 case SCI_SETTEXT: {
5702 if (lParam == 0)
5703 return 0;
5704 UndoGroup ug(pdoc);
5705 pdoc->DeleteChars(0, pdoc->Length());
5706 SetEmptySelection(0);
5707 const char *text = CharPtrFromSPtr(lParam);
5708 pdoc->InsertString(0, text, istrlen(text));
5709 return 1;
5712 case SCI_GETTEXTLENGTH:
5713 return pdoc->Length();
5715 case SCI_CUT:
5716 Cut();
5717 SetLastXChosen();
5718 break;
5720 case SCI_COPY:
5721 Copy();
5722 break;
5724 case SCI_COPYALLOWLINE:
5725 CopyAllowLine();
5726 break;
5728 case SCI_VERTICALCENTRECARET:
5729 VerticalCentreCaret();
5730 break;
5732 case SCI_MOVESELECTEDLINESUP:
5733 MoveSelectedLinesUp();
5734 break;
5736 case SCI_MOVESELECTEDLINESDOWN:
5737 MoveSelectedLinesDown();
5738 break;
5740 case SCI_COPYRANGE:
5741 CopyRangeToClipboard(static_cast<int>(wParam), static_cast<int>(lParam));
5742 break;
5744 case SCI_COPYTEXT:
5745 CopyText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
5746 break;
5748 case SCI_PASTE:
5749 Paste();
5750 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5751 SetLastXChosen();
5753 EnsureCaretVisible();
5754 break;
5756 case SCI_CLEAR:
5757 Clear();
5758 SetLastXChosen();
5759 EnsureCaretVisible();
5760 break;
5762 case SCI_UNDO:
5763 Undo();
5764 SetLastXChosen();
5765 break;
5767 case SCI_CANUNDO:
5768 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
5770 case SCI_EMPTYUNDOBUFFER:
5771 pdoc->DeleteUndoHistory();
5772 return 0;
5774 case SCI_GETFIRSTVISIBLELINE:
5775 return topLine;
5777 case SCI_SETFIRSTVISIBLELINE:
5778 ScrollTo(static_cast<int>(wParam));
5779 break;
5781 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
5782 int lineStart = pdoc->LineStart(static_cast<int>(wParam));
5783 int lineEnd = pdoc->LineStart(static_cast<int>(wParam + 1));
5784 if (lParam == 0) {
5785 return lineEnd - lineStart;
5787 char *ptr = CharPtrFromSPtr(lParam);
5788 int iPlace = 0;
5789 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
5790 ptr[iPlace++] = pdoc->CharAt(iChar);
5792 return iPlace;
5795 case SCI_GETLINECOUNT:
5796 if (pdoc->LinesTotal() == 0)
5797 return 1;
5798 else
5799 return pdoc->LinesTotal();
5801 case SCI_GETMODIFY:
5802 return !pdoc->IsSavePoint();
5804 case SCI_SETSEL: {
5805 int nStart = static_cast<int>(wParam);
5806 int nEnd = static_cast<int>(lParam);
5807 if (nEnd < 0)
5808 nEnd = pdoc->Length();
5809 if (nStart < 0)
5810 nStart = nEnd; // Remove selection
5811 InvalidateSelection(SelectionRange(nStart, nEnd));
5812 sel.Clear();
5813 sel.selType = Selection::selStream;
5814 SetSelection(nEnd, nStart);
5815 EnsureCaretVisible();
5817 break;
5819 case SCI_GETSELTEXT: {
5820 SelectionText selectedText;
5821 CopySelectionRange(&selectedText);
5822 if (lParam == 0) {
5823 return selectedText.LengthWithTerminator();
5824 } else {
5825 char *ptr = CharPtrFromSPtr(lParam);
5826 unsigned int iChar = 0;
5827 if (selectedText.Length()) {
5828 for (; iChar < selectedText.LengthWithTerminator(); iChar++)
5829 ptr[iChar] = selectedText.Data()[iChar];
5830 } else {
5831 ptr[0] = '\0';
5833 return iChar;
5837 case SCI_LINEFROMPOSITION:
5838 if (static_cast<int>(wParam) < 0)
5839 return 0;
5840 return pdoc->LineFromPosition(static_cast<int>(wParam));
5842 case SCI_POSITIONFROMLINE:
5843 if (static_cast<int>(wParam) < 0)
5844 wParam = pdoc->LineFromPosition(SelectionStart().Position());
5845 if (wParam == 0)
5846 return 0; // Even if there is no text, there is a first line that starts at 0
5847 if (static_cast<int>(wParam) > pdoc->LinesTotal())
5848 return -1;
5849 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
5850 // return -1;
5851 return pdoc->LineStart(static_cast<int>(wParam));
5853 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
5854 case SCI_LINELENGTH:
5855 if ((static_cast<int>(wParam) < 0) ||
5856 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
5857 return 0;
5858 return pdoc->LineStart(static_cast<int>(wParam) + 1) - pdoc->LineStart(static_cast<int>(wParam));
5860 case SCI_REPLACESEL: {
5861 if (lParam == 0)
5862 return 0;
5863 UndoGroup ug(pdoc);
5864 ClearSelection();
5865 char *replacement = CharPtrFromSPtr(lParam);
5866 const int lengthInserted = pdoc->InsertString(
5867 sel.MainCaret(), replacement, istrlen(replacement));
5868 SetEmptySelection(sel.MainCaret() + lengthInserted);
5869 EnsureCaretVisible();
5871 break;
5873 case SCI_SETTARGETSTART:
5874 targetStart = static_cast<int>(wParam);
5875 break;
5877 case SCI_GETTARGETSTART:
5878 return targetStart;
5880 case SCI_SETTARGETEND:
5881 targetEnd = static_cast<int>(wParam);
5882 break;
5884 case SCI_GETTARGETEND:
5885 return targetEnd;
5887 case SCI_SETTARGETRANGE:
5888 targetStart = static_cast<int>(wParam);
5889 targetEnd = static_cast<int>(lParam);
5890 break;
5892 case SCI_TARGETWHOLEDOCUMENT:
5893 targetStart = 0;
5894 targetEnd = pdoc->Length();
5895 break;
5897 case SCI_TARGETFROMSELECTION:
5898 if (sel.MainCaret() < sel.MainAnchor()) {
5899 targetStart = sel.MainCaret();
5900 targetEnd = sel.MainAnchor();
5901 } else {
5902 targetStart = sel.MainAnchor();
5903 targetEnd = sel.MainCaret();
5905 break;
5907 case SCI_GETTARGETTEXT: {
5908 std::string text = RangeText(targetStart, targetEnd);
5909 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(text.c_str()), text.length());
5912 case SCI_REPLACETARGET:
5913 PLATFORM_ASSERT(lParam);
5914 return ReplaceTarget(false, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5916 case SCI_REPLACETARGETRE:
5917 PLATFORM_ASSERT(lParam);
5918 return ReplaceTarget(true, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5920 case SCI_SEARCHINTARGET:
5921 PLATFORM_ASSERT(lParam);
5922 return SearchInTarget(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5924 case SCI_SETSEARCHFLAGS:
5925 searchFlags = static_cast<int>(wParam);
5926 break;
5928 case SCI_GETSEARCHFLAGS:
5929 return searchFlags;
5931 case SCI_GETTAG:
5932 return GetTag(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5934 case SCI_POSITIONBEFORE:
5935 return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) - 1, -1, true);
5937 case SCI_POSITIONAFTER:
5938 return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) + 1, 1, true);
5940 case SCI_POSITIONRELATIVE:
5941 return Platform::Clamp(pdoc->GetRelativePosition(static_cast<int>(wParam), static_cast<int>(lParam)), 0, pdoc->Length());
5943 case SCI_LINESCROLL:
5944 ScrollTo(topLine + static_cast<int>(lParam));
5945 HorizontalScrollTo(xOffset + static_cast<int>(wParam)* static_cast<int>(vs.spaceWidth));
5946 return 1;
5948 case SCI_SETXOFFSET:
5949 xOffset = static_cast<int>(wParam);
5950 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
5951 SetHorizontalScrollPos();
5952 Redraw();
5953 break;
5955 case SCI_GETXOFFSET:
5956 return xOffset;
5958 case SCI_CHOOSECARETX:
5959 SetLastXChosen();
5960 break;
5962 case SCI_SCROLLCARET:
5963 EnsureCaretVisible();
5964 break;
5966 case SCI_SETREADONLY:
5967 pdoc->SetReadOnly(wParam != 0);
5968 return 1;
5970 case SCI_GETREADONLY:
5971 return pdoc->IsReadOnly();
5973 case SCI_CANPASTE:
5974 return CanPaste();
5976 case SCI_POINTXFROMPOSITION:
5977 if (lParam < 0) {
5978 return 0;
5979 } else {
5980 Point pt = LocationFromPosition(static_cast<int>(lParam));
5981 // Convert to view-relative
5982 return static_cast<int>(pt.x) - vs.textStart + vs.fixedColumnWidth;
5985 case SCI_POINTYFROMPOSITION:
5986 if (lParam < 0) {
5987 return 0;
5988 } else {
5989 Point pt = LocationFromPosition(static_cast<int>(lParam));
5990 return static_cast<int>(pt.y);
5993 case SCI_FINDTEXT:
5994 return FindText(wParam, lParam);
5996 case SCI_GETTEXTRANGE: {
5997 if (lParam == 0)
5998 return 0;
5999 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
6000 int cpMax = static_cast<int>(tr->chrg.cpMax);
6001 if (cpMax == -1)
6002 cpMax = pdoc->Length();
6003 PLATFORM_ASSERT(cpMax <= pdoc->Length());
6004 int len = static_cast<int>(cpMax - tr->chrg.cpMin); // No -1 as cpMin and cpMax are referring to inter character positions
6005 pdoc->GetCharRange(tr->lpstrText, static_cast<int>(tr->chrg.cpMin), len);
6006 // Spec says copied text is terminated with a NUL
6007 tr->lpstrText[len] = '\0';
6008 return len; // Not including NUL
6011 case SCI_HIDESELECTION:
6012 view.hideSelection = wParam != 0;
6013 Redraw();
6014 break;
6016 case SCI_FORMATRANGE:
6017 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
6019 case SCI_GETMARGINLEFT:
6020 return vs.leftMarginWidth;
6022 case SCI_GETMARGINRIGHT:
6023 return vs.rightMarginWidth;
6025 case SCI_SETMARGINLEFT:
6026 lastXChosen += static_cast<int>(lParam) - vs.leftMarginWidth;
6027 vs.leftMarginWidth = static_cast<int>(lParam);
6028 InvalidateStyleRedraw();
6029 break;
6031 case SCI_SETMARGINRIGHT:
6032 vs.rightMarginWidth = static_cast<int>(lParam);
6033 InvalidateStyleRedraw();
6034 break;
6036 // Control specific mesages
6038 case SCI_ADDTEXT: {
6039 if (lParam == 0)
6040 return 0;
6041 const int lengthInserted = pdoc->InsertString(
6042 CurrentPosition(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
6043 SetEmptySelection(sel.MainCaret() + lengthInserted);
6044 return 0;
6047 case SCI_ADDSTYLEDTEXT:
6048 if (lParam)
6049 AddStyledText(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
6050 return 0;
6052 case SCI_INSERTTEXT: {
6053 if (lParam == 0)
6054 return 0;
6055 int insertPos = static_cast<int>(wParam);
6056 if (static_cast<int>(wParam) == -1)
6057 insertPos = CurrentPosition();
6058 int newCurrent = CurrentPosition();
6059 char *sz = CharPtrFromSPtr(lParam);
6060 const int lengthInserted = pdoc->InsertString(insertPos, sz, istrlen(sz));
6061 if (newCurrent > insertPos)
6062 newCurrent += lengthInserted;
6063 SetEmptySelection(newCurrent);
6064 return 0;
6067 case SCI_CHANGEINSERTION:
6068 PLATFORM_ASSERT(lParam);
6069 pdoc->ChangeInsertion(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
6070 return 0;
6072 case SCI_APPENDTEXT:
6073 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
6074 return 0;
6076 case SCI_CLEARALL:
6077 ClearAll();
6078 return 0;
6080 case SCI_DELETERANGE:
6081 pdoc->DeleteChars(static_cast<int>(wParam), static_cast<int>(lParam));
6082 return 0;
6084 case SCI_CLEARDOCUMENTSTYLE:
6085 ClearDocumentStyle();
6086 return 0;
6088 case SCI_SETUNDOCOLLECTION:
6089 pdoc->SetUndoCollection(wParam != 0);
6090 return 0;
6092 case SCI_GETUNDOCOLLECTION:
6093 return pdoc->IsCollectingUndo();
6095 case SCI_BEGINUNDOACTION:
6096 pdoc->BeginUndoAction();
6097 return 0;
6099 case SCI_ENDUNDOACTION:
6100 pdoc->EndUndoAction();
6101 return 0;
6103 case SCI_GETCARETPERIOD:
6104 return caret.period;
6106 case SCI_SETCARETPERIOD:
6107 CaretSetPeriod(static_cast<int>(wParam));
6108 break;
6110 case SCI_GETWORDCHARS:
6111 return pdoc->GetCharsOfClass(CharClassify::ccWord, reinterpret_cast<unsigned char *>(lParam));
6113 case SCI_SETWORDCHARS: {
6114 pdoc->SetDefaultCharClasses(false);
6115 if (lParam == 0)
6116 return 0;
6117 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
6119 break;
6121 case SCI_GETWHITESPACECHARS:
6122 return pdoc->GetCharsOfClass(CharClassify::ccSpace, reinterpret_cast<unsigned char *>(lParam));
6124 case SCI_SETWHITESPACECHARS: {
6125 if (lParam == 0)
6126 return 0;
6127 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
6129 break;
6131 case SCI_GETPUNCTUATIONCHARS:
6132 return pdoc->GetCharsOfClass(CharClassify::ccPunctuation, reinterpret_cast<unsigned char *>(lParam));
6134 case SCI_SETPUNCTUATIONCHARS: {
6135 if (lParam == 0)
6136 return 0;
6137 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccPunctuation);
6139 break;
6141 case SCI_SETCHARSDEFAULT:
6142 pdoc->SetDefaultCharClasses(true);
6143 break;
6145 case SCI_GETLENGTH:
6146 return pdoc->Length();
6148 case SCI_ALLOCATE:
6149 pdoc->Allocate(static_cast<int>(wParam));
6150 break;
6152 case SCI_GETCHARAT:
6153 return pdoc->CharAt(static_cast<int>(wParam));
6155 case SCI_SETCURRENTPOS:
6156 if (sel.IsRectangular()) {
6157 sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
6158 SetRectangularRange();
6159 Redraw();
6160 } else {
6161 SetSelection(static_cast<int>(wParam), sel.MainAnchor());
6163 break;
6165 case SCI_GETCURRENTPOS:
6166 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
6168 case SCI_SETANCHOR:
6169 if (sel.IsRectangular()) {
6170 sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
6171 SetRectangularRange();
6172 Redraw();
6173 } else {
6174 SetSelection(sel.MainCaret(), static_cast<int>(wParam));
6176 break;
6178 case SCI_GETANCHOR:
6179 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
6181 case SCI_SETSELECTIONSTART:
6182 SetSelection(Platform::Maximum(sel.MainCaret(), static_cast<int>(wParam)), static_cast<int>(wParam));
6183 break;
6185 case SCI_GETSELECTIONSTART:
6186 return sel.LimitsForRectangularElseMain().start.Position();
6188 case SCI_SETSELECTIONEND:
6189 SetSelection(static_cast<int>(wParam), Platform::Minimum(sel.MainAnchor(), static_cast<int>(wParam)));
6190 break;
6192 case SCI_GETSELECTIONEND:
6193 return sel.LimitsForRectangularElseMain().end.Position();
6195 case SCI_SETEMPTYSELECTION:
6196 SetEmptySelection(static_cast<int>(wParam));
6197 break;
6199 case SCI_SETPRINTMAGNIFICATION:
6200 view.printParameters.magnification = static_cast<int>(wParam);
6201 break;
6203 case SCI_GETPRINTMAGNIFICATION:
6204 return view.printParameters.magnification;
6206 case SCI_SETPRINTCOLOURMODE:
6207 view.printParameters.colourMode = static_cast<int>(wParam);
6208 break;
6210 case SCI_GETPRINTCOLOURMODE:
6211 return view.printParameters.colourMode;
6213 case SCI_SETPRINTWRAPMODE:
6214 view.printParameters.wrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
6215 break;
6217 case SCI_GETPRINTWRAPMODE:
6218 return view.printParameters.wrapState;
6220 case SCI_GETSTYLEAT:
6221 if (static_cast<int>(wParam) >= pdoc->Length())
6222 return 0;
6223 else
6224 return pdoc->StyleAt(static_cast<int>(wParam));
6226 case SCI_REDO:
6227 Redo();
6228 break;
6230 case SCI_SELECTALL:
6231 SelectAll();
6232 break;
6234 case SCI_SETSAVEPOINT:
6235 pdoc->SetSavePoint();
6236 break;
6238 case SCI_GETSTYLEDTEXT: {
6239 if (lParam == 0)
6240 return 0;
6241 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
6242 int iPlace = 0;
6243 for (long iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
6244 tr->lpstrText[iPlace++] = pdoc->CharAt(static_cast<int>(iChar));
6245 tr->lpstrText[iPlace++] = pdoc->StyleAt(static_cast<int>(iChar));
6247 tr->lpstrText[iPlace] = '\0';
6248 tr->lpstrText[iPlace + 1] = '\0';
6249 return iPlace;
6252 case SCI_CANREDO:
6253 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
6255 case SCI_MARKERLINEFROMHANDLE:
6256 return pdoc->LineFromHandle(static_cast<int>(wParam));
6258 case SCI_MARKERDELETEHANDLE:
6259 pdoc->DeleteMarkFromHandle(static_cast<int>(wParam));
6260 break;
6262 case SCI_GETVIEWWS:
6263 return vs.viewWhitespace;
6265 case SCI_SETVIEWWS:
6266 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
6267 Redraw();
6268 break;
6270 case SCI_GETWHITESPACESIZE:
6271 return vs.whitespaceSize;
6273 case SCI_SETWHITESPACESIZE:
6274 vs.whitespaceSize = static_cast<int>(wParam);
6275 Redraw();
6276 break;
6278 case SCI_POSITIONFROMPOINT:
6279 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6280 false, false);
6282 case SCI_POSITIONFROMPOINTCLOSE:
6283 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6284 true, false);
6286 case SCI_CHARPOSITIONFROMPOINT:
6287 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6288 false, true);
6290 case SCI_CHARPOSITIONFROMPOINTCLOSE:
6291 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6292 true, true);
6294 case SCI_GOTOLINE:
6295 GoToLine(static_cast<int>(wParam));
6296 break;
6298 case SCI_GOTOPOS:
6299 SetEmptySelection(static_cast<int>(wParam));
6300 EnsureCaretVisible();
6301 break;
6303 case SCI_GETCURLINE: {
6304 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
6305 int lineStart = pdoc->LineStart(lineCurrentPos);
6306 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
6307 if (lParam == 0) {
6308 return 1 + lineEnd - lineStart;
6310 PLATFORM_ASSERT(wParam > 0);
6311 char *ptr = CharPtrFromSPtr(lParam);
6312 unsigned int iPlace = 0;
6313 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
6314 ptr[iPlace++] = pdoc->CharAt(iChar);
6316 ptr[iPlace] = '\0';
6317 return sel.MainCaret() - lineStart;
6320 case SCI_GETENDSTYLED:
6321 return pdoc->GetEndStyled();
6323 case SCI_GETEOLMODE:
6324 return pdoc->eolMode;
6326 case SCI_SETEOLMODE:
6327 pdoc->eolMode = static_cast<int>(wParam);
6328 break;
6330 case SCI_SETLINEENDTYPESALLOWED:
6331 if (pdoc->SetLineEndTypesAllowed(static_cast<int>(wParam))) {
6332 cs.Clear();
6333 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6334 SetAnnotationHeights(0, pdoc->LinesTotal());
6335 InvalidateStyleRedraw();
6337 break;
6339 case SCI_GETLINEENDTYPESALLOWED:
6340 return pdoc->GetLineEndTypesAllowed();
6342 case SCI_GETLINEENDTYPESACTIVE:
6343 return pdoc->GetLineEndTypesActive();
6345 case SCI_STARTSTYLING:
6346 pdoc->StartStyling(static_cast<int>(wParam), static_cast<char>(lParam));
6347 break;
6349 case SCI_SETSTYLING:
6350 if (static_cast<int>(wParam) < 0)
6351 errorStatus = SC_STATUS_FAILURE;
6352 else
6353 pdoc->SetStyleFor(static_cast<int>(wParam), static_cast<char>(lParam));
6354 break;
6356 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
6357 if (lParam == 0)
6358 return 0;
6359 pdoc->SetStyles(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
6360 break;
6362 case SCI_SETBUFFEREDDRAW:
6363 view.bufferedDraw = wParam != 0;
6364 break;
6366 case SCI_GETBUFFEREDDRAW:
6367 return view.bufferedDraw;
6369 case SCI_GETTWOPHASEDRAW:
6370 return view.phasesDraw == EditView::phasesTwo;
6372 case SCI_SETTWOPHASEDRAW:
6373 if (view.SetTwoPhaseDraw(wParam != 0))
6374 InvalidateStyleRedraw();
6375 break;
6377 case SCI_GETPHASESDRAW:
6378 return view.phasesDraw;
6380 case SCI_SETPHASESDRAW:
6381 if (view.SetPhasesDraw(static_cast<int>(wParam)))
6382 InvalidateStyleRedraw();
6383 break;
6385 case SCI_SETFONTQUALITY:
6386 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
6387 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
6388 InvalidateStyleRedraw();
6389 break;
6391 case SCI_GETFONTQUALITY:
6392 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
6394 case SCI_SETTABWIDTH:
6395 if (wParam > 0) {
6396 pdoc->tabInChars = static_cast<int>(wParam);
6397 if (pdoc->indentInChars == 0)
6398 pdoc->actualIndentInChars = pdoc->tabInChars;
6400 InvalidateStyleRedraw();
6401 break;
6403 case SCI_GETTABWIDTH:
6404 return pdoc->tabInChars;
6406 case SCI_CLEARTABSTOPS:
6407 if (view.ClearTabstops(static_cast<int>(wParam))) {
6408 DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, 0, static_cast<int>(wParam));
6409 NotifyModified(pdoc, mh, NULL);
6411 break;
6413 case SCI_ADDTABSTOP:
6414 if (view.AddTabstop(static_cast<int>(wParam), static_cast<int>(lParam))) {
6415 DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, 0, static_cast<int>(wParam));
6416 NotifyModified(pdoc, mh, NULL);
6418 break;
6420 case SCI_GETNEXTTABSTOP:
6421 return view.GetNextTabstop(static_cast<int>(wParam), static_cast<int>(lParam));
6423 case SCI_SETINDENT:
6424 pdoc->indentInChars = static_cast<int>(wParam);
6425 if (pdoc->indentInChars != 0)
6426 pdoc->actualIndentInChars = pdoc->indentInChars;
6427 else
6428 pdoc->actualIndentInChars = pdoc->tabInChars;
6429 InvalidateStyleRedraw();
6430 break;
6432 case SCI_GETINDENT:
6433 return pdoc->indentInChars;
6435 case SCI_SETUSETABS:
6436 pdoc->useTabs = wParam != 0;
6437 InvalidateStyleRedraw();
6438 break;
6440 case SCI_GETUSETABS:
6441 return pdoc->useTabs;
6443 case SCI_SETLINEINDENTATION:
6444 pdoc->SetLineIndentation(static_cast<int>(wParam), static_cast<int>(lParam));
6445 break;
6447 case SCI_GETLINEINDENTATION:
6448 return pdoc->GetLineIndentation(static_cast<int>(wParam));
6450 case SCI_GETLINEINDENTPOSITION:
6451 return pdoc->GetLineIndentPosition(static_cast<int>(wParam));
6453 case SCI_SETTABINDENTS:
6454 pdoc->tabIndents = wParam != 0;
6455 break;
6457 case SCI_GETTABINDENTS:
6458 return pdoc->tabIndents;
6460 case SCI_SETBACKSPACEUNINDENTS:
6461 pdoc->backspaceUnindents = wParam != 0;
6462 break;
6464 case SCI_GETBACKSPACEUNINDENTS:
6465 return pdoc->backspaceUnindents;
6467 case SCI_SETMOUSEDWELLTIME:
6468 dwellDelay = static_cast<int>(wParam);
6469 ticksToDwell = dwellDelay;
6470 break;
6472 case SCI_GETMOUSEDWELLTIME:
6473 return dwellDelay;
6475 case SCI_WORDSTARTPOSITION:
6476 return pdoc->ExtendWordSelect(static_cast<int>(wParam), -1, lParam != 0);
6478 case SCI_WORDENDPOSITION:
6479 return pdoc->ExtendWordSelect(static_cast<int>(wParam), 1, lParam != 0);
6481 case SCI_ISRANGEWORD:
6482 return pdoc->IsWordAt(static_cast<int>(wParam), static_cast<int>(lParam));
6484 case SCI_SETIDLESTYLING:
6485 idleStyling = static_cast<int>(wParam);
6486 break;
6488 case SCI_GETIDLESTYLING:
6489 return idleStyling;
6491 case SCI_SETWRAPMODE:
6492 if (vs.SetWrapState(static_cast<int>(wParam))) {
6493 xOffset = 0;
6494 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
6495 InvalidateStyleRedraw();
6496 ReconfigureScrollBars();
6498 break;
6500 case SCI_GETWRAPMODE:
6501 return vs.wrapState;
6503 case SCI_SETWRAPVISUALFLAGS:
6504 if (vs.SetWrapVisualFlags(static_cast<int>(wParam))) {
6505 InvalidateStyleRedraw();
6506 ReconfigureScrollBars();
6508 break;
6510 case SCI_GETWRAPVISUALFLAGS:
6511 return vs.wrapVisualFlags;
6513 case SCI_SETWRAPVISUALFLAGSLOCATION:
6514 if (vs.SetWrapVisualFlagsLocation(static_cast<int>(wParam))) {
6515 InvalidateStyleRedraw();
6517 break;
6519 case SCI_GETWRAPVISUALFLAGSLOCATION:
6520 return vs.wrapVisualFlagsLocation;
6522 case SCI_SETWRAPSTARTINDENT:
6523 if (vs.SetWrapVisualStartIndent(static_cast<int>(wParam))) {
6524 InvalidateStyleRedraw();
6525 ReconfigureScrollBars();
6527 break;
6529 case SCI_GETWRAPSTARTINDENT:
6530 return vs.wrapVisualStartIndent;
6532 case SCI_SETWRAPINDENTMODE:
6533 if (vs.SetWrapIndentMode(static_cast<int>(wParam))) {
6534 InvalidateStyleRedraw();
6535 ReconfigureScrollBars();
6537 break;
6539 case SCI_GETWRAPINDENTMODE:
6540 return vs.wrapIndentMode;
6542 case SCI_SETLAYOUTCACHE:
6543 view.llc.SetLevel(static_cast<int>(wParam));
6544 break;
6546 case SCI_GETLAYOUTCACHE:
6547 return view.llc.GetLevel();
6549 case SCI_SETPOSITIONCACHE:
6550 view.posCache.SetSize(wParam);
6551 break;
6553 case SCI_GETPOSITIONCACHE:
6554 return view.posCache.GetSize();
6556 case SCI_SETSCROLLWIDTH:
6557 PLATFORM_ASSERT(wParam > 0);
6558 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
6559 view.lineWidthMaxSeen = 0;
6560 scrollWidth = static_cast<int>(wParam);
6561 SetScrollBars();
6563 break;
6565 case SCI_GETSCROLLWIDTH:
6566 return scrollWidth;
6568 case SCI_SETSCROLLWIDTHTRACKING:
6569 trackLineWidth = wParam != 0;
6570 break;
6572 case SCI_GETSCROLLWIDTHTRACKING:
6573 return trackLineWidth;
6575 case SCI_LINESJOIN:
6576 LinesJoin();
6577 break;
6579 case SCI_LINESSPLIT:
6580 LinesSplit(static_cast<int>(wParam));
6581 break;
6583 case SCI_TEXTWIDTH:
6584 PLATFORM_ASSERT(wParam < vs.styles.size());
6585 PLATFORM_ASSERT(lParam);
6586 return TextWidth(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
6588 case SCI_TEXTHEIGHT:
6589 RefreshStyleData();
6590 return vs.lineHeight;
6592 case SCI_SETENDATLASTLINE:
6593 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
6594 if (endAtLastLine != (wParam != 0)) {
6595 endAtLastLine = wParam != 0;
6596 SetScrollBars();
6598 break;
6600 case SCI_GETENDATLASTLINE:
6601 return endAtLastLine;
6603 case SCI_SETCARETSTICKY:
6604 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
6605 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
6606 caretSticky = static_cast<int>(wParam);
6608 break;
6610 case SCI_GETCARETSTICKY:
6611 return caretSticky;
6613 case SCI_TOGGLECARETSTICKY:
6614 caretSticky = !caretSticky;
6615 break;
6617 case SCI_GETCOLUMN:
6618 return pdoc->GetColumn(static_cast<int>(wParam));
6620 case SCI_FINDCOLUMN:
6621 return pdoc->FindColumn(static_cast<int>(wParam), static_cast<int>(lParam));
6623 case SCI_SETHSCROLLBAR :
6624 if (horizontalScrollBarVisible != (wParam != 0)) {
6625 horizontalScrollBarVisible = wParam != 0;
6626 SetScrollBars();
6627 ReconfigureScrollBars();
6629 break;
6631 case SCI_GETHSCROLLBAR:
6632 return horizontalScrollBarVisible;
6634 case SCI_SETVSCROLLBAR:
6635 if (verticalScrollBarVisible != (wParam != 0)) {
6636 verticalScrollBarVisible = wParam != 0;
6637 SetScrollBars();
6638 ReconfigureScrollBars();
6639 if (verticalScrollBarVisible)
6640 SetVerticalScrollPos();
6642 break;
6644 case SCI_GETVSCROLLBAR:
6645 return verticalScrollBarVisible;
6647 case SCI_SETINDENTATIONGUIDES:
6648 vs.viewIndentationGuides = IndentView(wParam);
6649 Redraw();
6650 break;
6652 case SCI_GETINDENTATIONGUIDES:
6653 return vs.viewIndentationGuides;
6655 case SCI_SETHIGHLIGHTGUIDE:
6656 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
6657 highlightGuideColumn = static_cast<int>(wParam);
6658 Redraw();
6660 break;
6662 case SCI_GETHIGHLIGHTGUIDE:
6663 return highlightGuideColumn;
6665 case SCI_GETLINEENDPOSITION:
6666 return pdoc->LineEnd(static_cast<int>(wParam));
6668 case SCI_SETCODEPAGE:
6669 if (ValidCodePage(static_cast<int>(wParam))) {
6670 if (pdoc->SetDBCSCodePage(static_cast<int>(wParam))) {
6671 cs.Clear();
6672 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6673 SetAnnotationHeights(0, pdoc->LinesTotal());
6674 InvalidateStyleRedraw();
6675 SetRepresentations();
6678 break;
6680 case SCI_GETCODEPAGE:
6681 return pdoc->dbcsCodePage;
6683 case SCI_SETIMEINTERACTION:
6684 imeInteraction = static_cast<EditModel::IMEInteraction>(wParam);
6685 break;
6687 case SCI_GETIMEINTERACTION:
6688 return imeInteraction;
6690 #ifdef INCLUDE_DEPRECATED_FEATURES
6691 case SCI_SETUSEPALETTE:
6692 InvalidateStyleRedraw();
6693 break;
6695 case SCI_GETUSEPALETTE:
6696 return 0;
6697 #endif
6699 // Marker definition and setting
6700 case SCI_MARKERDEFINE:
6701 if (wParam <= MARKER_MAX) {
6702 vs.markers[wParam].markType = static_cast<int>(lParam);
6703 vs.CalcLargestMarkerHeight();
6705 InvalidateStyleData();
6706 RedrawSelMargin();
6707 break;
6709 case SCI_MARKERSYMBOLDEFINED:
6710 if (wParam <= MARKER_MAX)
6711 return vs.markers[wParam].markType;
6712 else
6713 return 0;
6715 case SCI_MARKERSETFORE:
6716 if (wParam <= MARKER_MAX)
6717 vs.markers[wParam].fore = ColourDesired(static_cast<long>(lParam));
6718 InvalidateStyleData();
6719 RedrawSelMargin();
6720 break;
6721 case SCI_MARKERSETBACKSELECTED:
6722 if (wParam <= MARKER_MAX)
6723 vs.markers[wParam].backSelected = ColourDesired(static_cast<long>(lParam));
6724 InvalidateStyleData();
6725 RedrawSelMargin();
6726 break;
6727 case SCI_MARKERENABLEHIGHLIGHT:
6728 marginView.highlightDelimiter.isEnabled = wParam == 1;
6729 RedrawSelMargin();
6730 break;
6731 case SCI_MARKERSETBACK:
6732 if (wParam <= MARKER_MAX)
6733 vs.markers[wParam].back = ColourDesired(static_cast<long>(lParam));
6734 InvalidateStyleData();
6735 RedrawSelMargin();
6736 break;
6737 case SCI_MARKERSETALPHA:
6738 if (wParam <= MARKER_MAX)
6739 vs.markers[wParam].alpha = static_cast<int>(lParam);
6740 InvalidateStyleRedraw();
6741 break;
6742 case SCI_MARKERADD: {
6743 int markerID = pdoc->AddMark(static_cast<int>(wParam), static_cast<int>(lParam));
6744 return markerID;
6746 case SCI_MARKERADDSET:
6747 if (lParam != 0)
6748 pdoc->AddMarkSet(static_cast<int>(wParam), static_cast<int>(lParam));
6749 break;
6751 case SCI_MARKERDELETE:
6752 pdoc->DeleteMark(static_cast<int>(wParam), static_cast<int>(lParam));
6753 break;
6755 case SCI_MARKERDELETEALL:
6756 pdoc->DeleteAllMarks(static_cast<int>(wParam));
6757 break;
6759 case SCI_MARKERGET:
6760 return pdoc->GetMark(static_cast<int>(wParam));
6762 case SCI_MARKERNEXT:
6763 return pdoc->MarkerNext(static_cast<int>(wParam), static_cast<int>(lParam));
6765 case SCI_MARKERPREVIOUS: {
6766 for (int iLine = static_cast<int>(wParam); iLine >= 0; iLine--) {
6767 if ((pdoc->GetMark(iLine) & lParam) != 0)
6768 return iLine;
6771 return -1;
6773 case SCI_MARKERDEFINEPIXMAP:
6774 if (wParam <= MARKER_MAX) {
6775 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
6776 vs.CalcLargestMarkerHeight();
6778 InvalidateStyleData();
6779 RedrawSelMargin();
6780 break;
6782 case SCI_RGBAIMAGESETWIDTH:
6783 sizeRGBAImage.x = static_cast<XYPOSITION>(wParam);
6784 break;
6786 case SCI_RGBAIMAGESETHEIGHT:
6787 sizeRGBAImage.y = static_cast<XYPOSITION>(wParam);
6788 break;
6790 case SCI_RGBAIMAGESETSCALE:
6791 scaleRGBAImage = static_cast<float>(wParam);
6792 break;
6794 case SCI_MARKERDEFINERGBAIMAGE:
6795 if (wParam <= MARKER_MAX) {
6796 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0f, reinterpret_cast<unsigned char *>(lParam));
6797 vs.CalcLargestMarkerHeight();
6799 InvalidateStyleData();
6800 RedrawSelMargin();
6801 break;
6803 case SCI_SETMARGINTYPEN:
6804 if (ValidMargin(wParam)) {
6805 vs.ms[wParam].style = static_cast<int>(lParam);
6806 InvalidateStyleRedraw();
6808 break;
6810 case SCI_GETMARGINTYPEN:
6811 if (ValidMargin(wParam))
6812 return vs.ms[wParam].style;
6813 else
6814 return 0;
6816 case SCI_SETMARGINWIDTHN:
6817 if (ValidMargin(wParam)) {
6818 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
6819 if (vs.ms[wParam].width != lParam) {
6820 lastXChosen += static_cast<int>(lParam) - vs.ms[wParam].width;
6821 vs.ms[wParam].width = static_cast<int>(lParam);
6822 InvalidateStyleRedraw();
6825 break;
6827 case SCI_GETMARGINWIDTHN:
6828 if (ValidMargin(wParam))
6829 return vs.ms[wParam].width;
6830 else
6831 return 0;
6833 case SCI_SETMARGINMASKN:
6834 if (ValidMargin(wParam)) {
6835 vs.ms[wParam].mask = static_cast<int>(lParam);
6836 InvalidateStyleRedraw();
6838 break;
6840 case SCI_GETMARGINMASKN:
6841 if (ValidMargin(wParam))
6842 return vs.ms[wParam].mask;
6843 else
6844 return 0;
6846 case SCI_SETMARGINSENSITIVEN:
6847 if (ValidMargin(wParam)) {
6848 vs.ms[wParam].sensitive = lParam != 0;
6849 InvalidateStyleRedraw();
6851 break;
6853 case SCI_GETMARGINSENSITIVEN:
6854 if (ValidMargin(wParam))
6855 return vs.ms[wParam].sensitive ? 1 : 0;
6856 else
6857 return 0;
6859 case SCI_SETMARGINCURSORN:
6860 if (ValidMargin(wParam))
6861 vs.ms[wParam].cursor = static_cast<int>(lParam);
6862 break;
6864 case SCI_GETMARGINCURSORN:
6865 if (ValidMargin(wParam))
6866 return vs.ms[wParam].cursor;
6867 else
6868 return 0;
6870 case SCI_STYLECLEARALL:
6871 vs.ClearStyles();
6872 InvalidateStyleRedraw();
6873 break;
6875 case SCI_STYLESETFORE:
6876 case SCI_STYLESETBACK:
6877 case SCI_STYLESETBOLD:
6878 case SCI_STYLESETWEIGHT:
6879 case SCI_STYLESETITALIC:
6880 case SCI_STYLESETEOLFILLED:
6881 case SCI_STYLESETSIZE:
6882 case SCI_STYLESETSIZEFRACTIONAL:
6883 case SCI_STYLESETFONT:
6884 case SCI_STYLESETUNDERLINE:
6885 case SCI_STYLESETCASE:
6886 case SCI_STYLESETCHARACTERSET:
6887 case SCI_STYLESETVISIBLE:
6888 case SCI_STYLESETCHANGEABLE:
6889 case SCI_STYLESETHOTSPOT:
6890 StyleSetMessage(iMessage, wParam, lParam);
6891 break;
6893 case SCI_STYLEGETFORE:
6894 case SCI_STYLEGETBACK:
6895 case SCI_STYLEGETBOLD:
6896 case SCI_STYLEGETWEIGHT:
6897 case SCI_STYLEGETITALIC:
6898 case SCI_STYLEGETEOLFILLED:
6899 case SCI_STYLEGETSIZE:
6900 case SCI_STYLEGETSIZEFRACTIONAL:
6901 case SCI_STYLEGETFONT:
6902 case SCI_STYLEGETUNDERLINE:
6903 case SCI_STYLEGETCASE:
6904 case SCI_STYLEGETCHARACTERSET:
6905 case SCI_STYLEGETVISIBLE:
6906 case SCI_STYLEGETCHANGEABLE:
6907 case SCI_STYLEGETHOTSPOT:
6908 return StyleGetMessage(iMessage, wParam, lParam);
6910 case SCI_STYLERESETDEFAULT:
6911 vs.ResetDefaultStyle();
6912 InvalidateStyleRedraw();
6913 break;
6914 case SCI_SETSTYLEBITS:
6915 vs.EnsureStyle(0xff);
6916 break;
6918 case SCI_GETSTYLEBITS:
6919 return 8;
6921 case SCI_SETLINESTATE:
6922 return pdoc->SetLineState(static_cast<int>(wParam), static_cast<int>(lParam));
6924 case SCI_GETLINESTATE:
6925 return pdoc->GetLineState(static_cast<int>(wParam));
6927 case SCI_GETMAXLINESTATE:
6928 return pdoc->GetMaxLineState();
6930 case SCI_GETCARETLINEVISIBLE:
6931 return vs.showCaretLineBackground;
6932 case SCI_SETCARETLINEVISIBLE:
6933 vs.showCaretLineBackground = wParam != 0;
6934 InvalidateStyleRedraw();
6935 break;
6936 case SCI_GETCARETLINEVISIBLEALWAYS:
6937 return vs.alwaysShowCaretLineBackground;
6938 case SCI_SETCARETLINEVISIBLEALWAYS:
6939 vs.alwaysShowCaretLineBackground = wParam != 0;
6940 InvalidateStyleRedraw();
6941 break;
6943 case SCI_GETCARETLINEBACK:
6944 return vs.caretLineBackground.AsLong();
6945 case SCI_SETCARETLINEBACK:
6946 vs.caretLineBackground = static_cast<int>(wParam);
6947 InvalidateStyleRedraw();
6948 break;
6949 case SCI_GETCARETLINEBACKALPHA:
6950 return vs.caretLineAlpha;
6951 case SCI_SETCARETLINEBACKALPHA:
6952 vs.caretLineAlpha = static_cast<int>(wParam);
6953 InvalidateStyleRedraw();
6954 break;
6956 // Folding messages
6958 case SCI_VISIBLEFROMDOCLINE:
6959 return cs.DisplayFromDoc(static_cast<int>(wParam));
6961 case SCI_DOCLINEFROMVISIBLE:
6962 return cs.DocFromDisplay(static_cast<int>(wParam));
6964 case SCI_WRAPCOUNT:
6965 return WrapCount(static_cast<int>(wParam));
6967 case SCI_SETFOLDLEVEL: {
6968 int prev = pdoc->SetLevel(static_cast<int>(wParam), static_cast<int>(lParam));
6969 if (prev != static_cast<int>(lParam))
6970 RedrawSelMargin();
6971 return prev;
6974 case SCI_GETFOLDLEVEL:
6975 return pdoc->GetLevel(static_cast<int>(wParam));
6977 case SCI_GETLASTCHILD:
6978 return pdoc->GetLastChild(static_cast<int>(wParam), static_cast<int>(lParam));
6980 case SCI_GETFOLDPARENT:
6981 return pdoc->GetFoldParent(static_cast<int>(wParam));
6983 case SCI_SHOWLINES:
6984 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), true);
6985 SetScrollBars();
6986 Redraw();
6987 break;
6989 case SCI_HIDELINES:
6990 if (wParam > 0)
6991 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), false);
6992 SetScrollBars();
6993 Redraw();
6994 break;
6996 case SCI_GETLINEVISIBLE:
6997 return cs.GetVisible(static_cast<int>(wParam));
6999 case SCI_GETALLLINESVISIBLE:
7000 return cs.HiddenLines() ? 0 : 1;
7002 case SCI_SETFOLDEXPANDED:
7003 SetFoldExpanded(static_cast<int>(wParam), lParam != 0);
7004 break;
7006 case SCI_GETFOLDEXPANDED:
7007 return cs.GetExpanded(static_cast<int>(wParam));
7009 case SCI_SETAUTOMATICFOLD:
7010 foldAutomatic = static_cast<int>(wParam);
7011 break;
7013 case SCI_GETAUTOMATICFOLD:
7014 return foldAutomatic;
7016 case SCI_SETFOLDFLAGS:
7017 foldFlags = static_cast<int>(wParam);
7018 Redraw();
7019 break;
7021 case SCI_TOGGLEFOLD:
7022 FoldLine(static_cast<int>(wParam), SC_FOLDACTION_TOGGLE);
7023 break;
7025 case SCI_FOLDLINE:
7026 FoldLine(static_cast<int>(wParam), static_cast<int>(lParam));
7027 break;
7029 case SCI_FOLDCHILDREN:
7030 FoldExpand(static_cast<int>(wParam), static_cast<int>(lParam), pdoc->GetLevel(static_cast<int>(wParam)));
7031 break;
7033 case SCI_FOLDALL:
7034 FoldAll(static_cast<int>(wParam));
7035 break;
7037 case SCI_EXPANDCHILDREN:
7038 FoldExpand(static_cast<int>(wParam), SC_FOLDACTION_EXPAND, static_cast<int>(lParam));
7039 break;
7041 case SCI_CONTRACTEDFOLDNEXT:
7042 return ContractedFoldNext(static_cast<int>(wParam));
7044 case SCI_ENSUREVISIBLE:
7045 EnsureLineVisible(static_cast<int>(wParam), false);
7046 break;
7048 case SCI_ENSUREVISIBLEENFORCEPOLICY:
7049 EnsureLineVisible(static_cast<int>(wParam), true);
7050 break;
7052 case SCI_SCROLLRANGE:
7053 ScrollRange(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
7054 break;
7056 case SCI_SEARCHANCHOR:
7057 SearchAnchor();
7058 break;
7060 case SCI_SEARCHNEXT:
7061 case SCI_SEARCHPREV:
7062 return SearchText(iMessage, wParam, lParam);
7064 case SCI_SETXCARETPOLICY:
7065 caretXPolicy = static_cast<int>(wParam);
7066 caretXSlop = static_cast<int>(lParam);
7067 break;
7069 case SCI_SETYCARETPOLICY:
7070 caretYPolicy = static_cast<int>(wParam);
7071 caretYSlop = static_cast<int>(lParam);
7072 break;
7074 case SCI_SETVISIBLEPOLICY:
7075 visiblePolicy = static_cast<int>(wParam);
7076 visibleSlop = static_cast<int>(lParam);
7077 break;
7079 case SCI_LINESONSCREEN:
7080 return LinesOnScreen();
7082 case SCI_SETSELFORE:
7083 vs.selColours.fore = ColourOptional(wParam, lParam);
7084 vs.selAdditionalForeground = ColourDesired(static_cast<long>(lParam));
7085 InvalidateStyleRedraw();
7086 break;
7088 case SCI_SETSELBACK:
7089 vs.selColours.back = ColourOptional(wParam, lParam);
7090 vs.selAdditionalBackground = ColourDesired(static_cast<long>(lParam));
7091 InvalidateStyleRedraw();
7092 break;
7094 case SCI_SETSELALPHA:
7095 vs.selAlpha = static_cast<int>(wParam);
7096 vs.selAdditionalAlpha = static_cast<int>(wParam);
7097 InvalidateStyleRedraw();
7098 break;
7100 case SCI_GETSELALPHA:
7101 return vs.selAlpha;
7103 case SCI_GETSELEOLFILLED:
7104 return vs.selEOLFilled;
7106 case SCI_SETSELEOLFILLED:
7107 vs.selEOLFilled = wParam != 0;
7108 InvalidateStyleRedraw();
7109 break;
7111 case SCI_SETWHITESPACEFORE:
7112 vs.whitespaceColours.fore = ColourOptional(wParam, lParam);
7113 InvalidateStyleRedraw();
7114 break;
7116 case SCI_SETWHITESPACEBACK:
7117 vs.whitespaceColours.back = ColourOptional(wParam, lParam);
7118 InvalidateStyleRedraw();
7119 break;
7121 case SCI_SETCARETFORE:
7122 vs.caretcolour = ColourDesired(static_cast<long>(wParam));
7123 InvalidateStyleRedraw();
7124 break;
7126 case SCI_GETCARETFORE:
7127 return vs.caretcolour.AsLong();
7129 case SCI_SETCARETSTYLE:
7130 if (wParam <= CARETSTYLE_BLOCK)
7131 vs.caretStyle = static_cast<int>(wParam);
7132 else
7133 /* Default to the line caret */
7134 vs.caretStyle = CARETSTYLE_LINE;
7135 InvalidateStyleRedraw();
7136 break;
7138 case SCI_GETCARETSTYLE:
7139 return vs.caretStyle;
7141 case SCI_SETCARETWIDTH:
7142 if (static_cast<int>(wParam) <= 0)
7143 vs.caretWidth = 0;
7144 else if (wParam >= 3)
7145 vs.caretWidth = 3;
7146 else
7147 vs.caretWidth = static_cast<int>(wParam);
7148 InvalidateStyleRedraw();
7149 break;
7151 case SCI_GETCARETWIDTH:
7152 return vs.caretWidth;
7154 case SCI_ASSIGNCMDKEY:
7155 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
7156 Platform::HighShortFromLong(static_cast<long>(wParam)), static_cast<unsigned int>(lParam));
7157 break;
7159 case SCI_CLEARCMDKEY:
7160 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
7161 Platform::HighShortFromLong(static_cast<long>(wParam)), SCI_NULL);
7162 break;
7164 case SCI_CLEARALLCMDKEYS:
7165 kmap.Clear();
7166 break;
7168 case SCI_INDICSETSTYLE:
7169 if (wParam <= INDIC_MAX) {
7170 vs.indicators[wParam].sacNormal.style = static_cast<int>(lParam);
7171 vs.indicators[wParam].sacHover.style = static_cast<int>(lParam);
7172 InvalidateStyleRedraw();
7174 break;
7176 case SCI_INDICGETSTYLE:
7177 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacNormal.style : 0;
7179 case SCI_INDICSETFORE:
7180 if (wParam <= INDIC_MAX) {
7181 vs.indicators[wParam].sacNormal.fore = ColourDesired(static_cast<long>(lParam));
7182 vs.indicators[wParam].sacHover.fore = ColourDesired(static_cast<long>(lParam));
7183 InvalidateStyleRedraw();
7185 break;
7187 case SCI_INDICGETFORE:
7188 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacNormal.fore.AsLong() : 0;
7190 case SCI_INDICSETHOVERSTYLE:
7191 if (wParam <= INDIC_MAX) {
7192 vs.indicators[wParam].sacHover.style = static_cast<int>(lParam);
7193 InvalidateStyleRedraw();
7195 break;
7197 case SCI_INDICGETHOVERSTYLE:
7198 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacHover.style : 0;
7200 case SCI_INDICSETHOVERFORE:
7201 if (wParam <= INDIC_MAX) {
7202 vs.indicators[wParam].sacHover.fore = ColourDesired(static_cast<long>(lParam));
7203 InvalidateStyleRedraw();
7205 break;
7207 case SCI_INDICGETHOVERFORE:
7208 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacHover.fore.AsLong() : 0;
7210 case SCI_INDICSETFLAGS:
7211 if (wParam <= INDIC_MAX) {
7212 vs.indicators[wParam].SetFlags(static_cast<int>(lParam));
7213 InvalidateStyleRedraw();
7215 break;
7217 case SCI_INDICGETFLAGS:
7218 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].Flags() : 0;
7220 case SCI_INDICSETUNDER:
7221 if (wParam <= INDIC_MAX) {
7222 vs.indicators[wParam].under = lParam != 0;
7223 InvalidateStyleRedraw();
7225 break;
7227 case SCI_INDICGETUNDER:
7228 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
7230 case SCI_INDICSETALPHA:
7231 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
7232 vs.indicators[wParam].fillAlpha = static_cast<int>(lParam);
7233 InvalidateStyleRedraw();
7235 break;
7237 case SCI_INDICGETALPHA:
7238 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
7240 case SCI_INDICSETOUTLINEALPHA:
7241 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
7242 vs.indicators[wParam].outlineAlpha = static_cast<int>(lParam);
7243 InvalidateStyleRedraw();
7245 break;
7247 case SCI_INDICGETOUTLINEALPHA:
7248 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
7250 case SCI_SETINDICATORCURRENT:
7251 pdoc->decorations.SetCurrentIndicator(static_cast<int>(wParam));
7252 break;
7253 case SCI_GETINDICATORCURRENT:
7254 return pdoc->decorations.GetCurrentIndicator();
7255 case SCI_SETINDICATORVALUE:
7256 pdoc->decorations.SetCurrentValue(static_cast<int>(wParam));
7257 break;
7258 case SCI_GETINDICATORVALUE:
7259 return pdoc->decorations.GetCurrentValue();
7261 case SCI_INDICATORFILLRANGE:
7262 pdoc->DecorationFillRange(static_cast<int>(wParam), pdoc->decorations.GetCurrentValue(), static_cast<int>(lParam));
7263 break;
7265 case SCI_INDICATORCLEARRANGE:
7266 pdoc->DecorationFillRange(static_cast<int>(wParam), 0, static_cast<int>(lParam));
7267 break;
7269 case SCI_INDICATORALLONFOR:
7270 return pdoc->decorations.AllOnFor(static_cast<int>(wParam));
7272 case SCI_INDICATORVALUEAT:
7273 return pdoc->decorations.ValueAt(static_cast<int>(wParam), static_cast<int>(lParam));
7275 case SCI_INDICATORSTART:
7276 return pdoc->decorations.Start(static_cast<int>(wParam), static_cast<int>(lParam));
7278 case SCI_INDICATOREND:
7279 return pdoc->decorations.End(static_cast<int>(wParam), static_cast<int>(lParam));
7281 case SCI_LINEDOWN:
7282 case SCI_LINEDOWNEXTEND:
7283 case SCI_PARADOWN:
7284 case SCI_PARADOWNEXTEND:
7285 case SCI_LINEUP:
7286 case SCI_LINEUPEXTEND:
7287 case SCI_PARAUP:
7288 case SCI_PARAUPEXTEND:
7289 case SCI_CHARLEFT:
7290 case SCI_CHARLEFTEXTEND:
7291 case SCI_CHARRIGHT:
7292 case SCI_CHARRIGHTEXTEND:
7293 case SCI_WORDLEFT:
7294 case SCI_WORDLEFTEXTEND:
7295 case SCI_WORDRIGHT:
7296 case SCI_WORDRIGHTEXTEND:
7297 case SCI_WORDLEFTEND:
7298 case SCI_WORDLEFTENDEXTEND:
7299 case SCI_WORDRIGHTEND:
7300 case SCI_WORDRIGHTENDEXTEND:
7301 case SCI_HOME:
7302 case SCI_HOMEEXTEND:
7303 case SCI_LINEEND:
7304 case SCI_LINEENDEXTEND:
7305 case SCI_HOMEWRAP:
7306 case SCI_HOMEWRAPEXTEND:
7307 case SCI_LINEENDWRAP:
7308 case SCI_LINEENDWRAPEXTEND:
7309 case SCI_DOCUMENTSTART:
7310 case SCI_DOCUMENTSTARTEXTEND:
7311 case SCI_DOCUMENTEND:
7312 case SCI_DOCUMENTENDEXTEND:
7313 case SCI_SCROLLTOSTART:
7314 case SCI_SCROLLTOEND:
7316 case SCI_STUTTEREDPAGEUP:
7317 case SCI_STUTTEREDPAGEUPEXTEND:
7318 case SCI_STUTTEREDPAGEDOWN:
7319 case SCI_STUTTEREDPAGEDOWNEXTEND:
7321 case SCI_PAGEUP:
7322 case SCI_PAGEUPEXTEND:
7323 case SCI_PAGEDOWN:
7324 case SCI_PAGEDOWNEXTEND:
7325 case SCI_EDITTOGGLEOVERTYPE:
7326 case SCI_CANCEL:
7327 case SCI_DELETEBACK:
7328 case SCI_TAB:
7329 case SCI_BACKTAB:
7330 case SCI_NEWLINE:
7331 case SCI_FORMFEED:
7332 case SCI_VCHOME:
7333 case SCI_VCHOMEEXTEND:
7334 case SCI_VCHOMEWRAP:
7335 case SCI_VCHOMEWRAPEXTEND:
7336 case SCI_VCHOMEDISPLAY:
7337 case SCI_VCHOMEDISPLAYEXTEND:
7338 case SCI_ZOOMIN:
7339 case SCI_ZOOMOUT:
7340 case SCI_DELWORDLEFT:
7341 case SCI_DELWORDRIGHT:
7342 case SCI_DELWORDRIGHTEND:
7343 case SCI_DELLINELEFT:
7344 case SCI_DELLINERIGHT:
7345 case SCI_LINECOPY:
7346 case SCI_LINECUT:
7347 case SCI_LINEDELETE:
7348 case SCI_LINETRANSPOSE:
7349 case SCI_LINEDUPLICATE:
7350 case SCI_LOWERCASE:
7351 case SCI_UPPERCASE:
7352 case SCI_LINESCROLLDOWN:
7353 case SCI_LINESCROLLUP:
7354 case SCI_WORDPARTLEFT:
7355 case SCI_WORDPARTLEFTEXTEND:
7356 case SCI_WORDPARTRIGHT:
7357 case SCI_WORDPARTRIGHTEXTEND:
7358 case SCI_DELETEBACKNOTLINE:
7359 case SCI_HOMEDISPLAY:
7360 case SCI_HOMEDISPLAYEXTEND:
7361 case SCI_LINEENDDISPLAY:
7362 case SCI_LINEENDDISPLAYEXTEND:
7363 case SCI_LINEDOWNRECTEXTEND:
7364 case SCI_LINEUPRECTEXTEND:
7365 case SCI_CHARLEFTRECTEXTEND:
7366 case SCI_CHARRIGHTRECTEXTEND:
7367 case SCI_HOMERECTEXTEND:
7368 case SCI_VCHOMERECTEXTEND:
7369 case SCI_LINEENDRECTEXTEND:
7370 case SCI_PAGEUPRECTEXTEND:
7371 case SCI_PAGEDOWNRECTEXTEND:
7372 case SCI_SELECTIONDUPLICATE:
7373 return KeyCommand(iMessage);
7375 case SCI_BRACEHIGHLIGHT:
7376 SetBraceHighlight(static_cast<int>(wParam), static_cast<int>(lParam), STYLE_BRACELIGHT);
7377 break;
7379 case SCI_BRACEHIGHLIGHTINDICATOR:
7380 if (lParam >= 0 && lParam <= INDIC_MAX) {
7381 vs.braceHighlightIndicatorSet = wParam != 0;
7382 vs.braceHighlightIndicator = static_cast<int>(lParam);
7384 break;
7386 case SCI_BRACEBADLIGHT:
7387 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
7388 break;
7390 case SCI_BRACEBADLIGHTINDICATOR:
7391 if (lParam >= 0 && lParam <= INDIC_MAX) {
7392 vs.braceBadLightIndicatorSet = wParam != 0;
7393 vs.braceBadLightIndicator = static_cast<int>(lParam);
7395 break;
7397 case SCI_BRACEMATCH:
7398 // wParam is position of char to find brace for,
7399 // lParam is maximum amount of text to restyle to find it
7400 return pdoc->BraceMatch(static_cast<int>(wParam), static_cast<int>(lParam));
7402 case SCI_GETVIEWEOL:
7403 return vs.viewEOL;
7405 case SCI_SETVIEWEOL:
7406 vs.viewEOL = wParam != 0;
7407 InvalidateStyleRedraw();
7408 break;
7410 case SCI_SETZOOM:
7411 vs.zoomLevel = static_cast<int>(wParam);
7412 InvalidateStyleRedraw();
7413 NotifyZoom();
7414 break;
7416 case SCI_GETZOOM:
7417 return vs.zoomLevel;
7419 case SCI_GETEDGECOLUMN:
7420 return vs.theEdge;
7422 case SCI_SETEDGECOLUMN:
7423 vs.theEdge = static_cast<int>(wParam);
7424 InvalidateStyleRedraw();
7425 break;
7427 case SCI_GETEDGEMODE:
7428 return vs.edgeState;
7430 case SCI_SETEDGEMODE:
7431 vs.edgeState = static_cast<int>(wParam);
7432 InvalidateStyleRedraw();
7433 break;
7435 case SCI_GETEDGECOLOUR:
7436 return vs.edgecolour.AsLong();
7438 case SCI_SETEDGECOLOUR:
7439 vs.edgecolour = ColourDesired(static_cast<long>(wParam));
7440 InvalidateStyleRedraw();
7441 break;
7443 case SCI_GETDOCPOINTER:
7444 return reinterpret_cast<sptr_t>(pdoc);
7446 case SCI_SETDOCPOINTER:
7447 CancelModes();
7448 SetDocPointer(reinterpret_cast<Document *>(lParam));
7449 return 0;
7451 case SCI_CREATEDOCUMENT: {
7452 Document *doc = new Document();
7453 doc->AddRef();
7454 return reinterpret_cast<sptr_t>(doc);
7457 case SCI_ADDREFDOCUMENT:
7458 (reinterpret_cast<Document *>(lParam))->AddRef();
7459 break;
7461 case SCI_RELEASEDOCUMENT:
7462 (reinterpret_cast<Document *>(lParam))->Release();
7463 break;
7465 case SCI_CREATELOADER: {
7466 Document *doc = new Document();
7467 doc->AddRef();
7468 doc->Allocate(static_cast<int>(wParam));
7469 doc->SetUndoCollection(false);
7470 return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
7473 case SCI_SETMODEVENTMASK:
7474 modEventMask = static_cast<int>(wParam);
7475 return 0;
7477 case SCI_GETMODEVENTMASK:
7478 return modEventMask;
7480 case SCI_CONVERTEOLS:
7481 pdoc->ConvertLineEnds(static_cast<int>(wParam));
7482 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
7483 return 0;
7485 case SCI_SETLENGTHFORENCODE:
7486 lengthForEncode = static_cast<int>(wParam);
7487 return 0;
7489 case SCI_SELECTIONISRECTANGLE:
7490 return sel.selType == Selection::selRectangle ? 1 : 0;
7492 case SCI_SETSELECTIONMODE: {
7493 switch (wParam) {
7494 case SC_SEL_STREAM:
7495 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7496 sel.selType = Selection::selStream;
7497 break;
7498 case SC_SEL_RECTANGLE:
7499 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
7500 sel.selType = Selection::selRectangle;
7501 break;
7502 case SC_SEL_LINES:
7503 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
7504 sel.selType = Selection::selLines;
7505 break;
7506 case SC_SEL_THIN:
7507 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
7508 sel.selType = Selection::selThin;
7509 break;
7510 default:
7511 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7512 sel.selType = Selection::selStream;
7514 InvalidateWholeSelection();
7515 break;
7517 case SCI_GETSELECTIONMODE:
7518 switch (sel.selType) {
7519 case Selection::selStream:
7520 return SC_SEL_STREAM;
7521 case Selection::selRectangle:
7522 return SC_SEL_RECTANGLE;
7523 case Selection::selLines:
7524 return SC_SEL_LINES;
7525 case Selection::selThin:
7526 return SC_SEL_THIN;
7527 default: // ?!
7528 return SC_SEL_STREAM;
7530 case SCI_GETLINESELSTARTPOSITION:
7531 case SCI_GETLINESELENDPOSITION: {
7532 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(static_cast<int>(wParam))),
7533 SelectionPosition(pdoc->LineEnd(static_cast<int>(wParam))));
7534 for (size_t r=0; r<sel.Count(); r++) {
7535 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
7536 if (portion.start.IsValid()) {
7537 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
7540 return INVALID_POSITION;
7543 case SCI_SETOVERTYPE:
7544 inOverstrike = wParam != 0;
7545 break;
7547 case SCI_GETOVERTYPE:
7548 return inOverstrike ? 1 : 0;
7550 case SCI_SETFOCUS:
7551 SetFocusState(wParam != 0);
7552 break;
7554 case SCI_GETFOCUS:
7555 return hasFocus;
7557 case SCI_SETSTATUS:
7558 errorStatus = static_cast<int>(wParam);
7559 break;
7561 case SCI_GETSTATUS:
7562 return errorStatus;
7564 case SCI_SETMOUSEDOWNCAPTURES:
7565 mouseDownCaptures = wParam != 0;
7566 break;
7568 case SCI_GETMOUSEDOWNCAPTURES:
7569 return mouseDownCaptures;
7571 case SCI_SETCURSOR:
7572 cursorMode = static_cast<int>(wParam);
7573 DisplayCursor(Window::cursorText);
7574 break;
7576 case SCI_GETCURSOR:
7577 return cursorMode;
7579 case SCI_SETCONTROLCHARSYMBOL:
7580 vs.controlCharSymbol = static_cast<int>(wParam);
7581 InvalidateStyleRedraw();
7582 break;
7584 case SCI_GETCONTROLCHARSYMBOL:
7585 return vs.controlCharSymbol;
7587 case SCI_SETREPRESENTATION:
7588 reprs.SetRepresentation(reinterpret_cast<const char *>(wParam), CharPtrFromSPtr(lParam));
7589 break;
7591 case SCI_GETREPRESENTATION: {
7592 const Representation *repr = reprs.RepresentationFromCharacter(
7593 reinterpret_cast<const char *>(wParam), UTF8MaxBytes);
7594 if (repr) {
7595 return StringResult(lParam, repr->stringRep.c_str());
7597 return 0;
7600 case SCI_CLEARREPRESENTATION:
7601 reprs.ClearRepresentation(reinterpret_cast<const char *>(wParam));
7602 break;
7604 case SCI_STARTRECORD:
7605 recordingMacro = true;
7606 return 0;
7608 case SCI_STOPRECORD:
7609 recordingMacro = false;
7610 return 0;
7612 case SCI_MOVECARETINSIDEVIEW:
7613 MoveCaretInsideView();
7614 break;
7616 case SCI_SETFOLDMARGINCOLOUR:
7617 vs.foldmarginColour = ColourOptional(wParam, lParam);
7618 InvalidateStyleRedraw();
7619 break;
7621 case SCI_SETFOLDMARGINHICOLOUR:
7622 vs.foldmarginHighlightColour = ColourOptional(wParam, lParam);
7623 InvalidateStyleRedraw();
7624 break;
7626 case SCI_SETHOTSPOTACTIVEFORE:
7627 vs.hotspotColours.fore = ColourOptional(wParam, lParam);
7628 InvalidateStyleRedraw();
7629 break;
7631 case SCI_GETHOTSPOTACTIVEFORE:
7632 return vs.hotspotColours.fore.AsLong();
7634 case SCI_SETHOTSPOTACTIVEBACK:
7635 vs.hotspotColours.back = ColourOptional(wParam, lParam);
7636 InvalidateStyleRedraw();
7637 break;
7639 case SCI_GETHOTSPOTACTIVEBACK:
7640 return vs.hotspotColours.back.AsLong();
7642 case SCI_SETHOTSPOTACTIVEUNDERLINE:
7643 vs.hotspotUnderline = wParam != 0;
7644 InvalidateStyleRedraw();
7645 break;
7647 case SCI_GETHOTSPOTACTIVEUNDERLINE:
7648 return vs.hotspotUnderline ? 1 : 0;
7650 case SCI_SETHOTSPOTSINGLELINE:
7651 vs.hotspotSingleLine = wParam != 0;
7652 InvalidateStyleRedraw();
7653 break;
7655 case SCI_GETHOTSPOTSINGLELINE:
7656 return vs.hotspotSingleLine ? 1 : 0;
7658 case SCI_SETPASTECONVERTENDINGS:
7659 convertPastes = wParam != 0;
7660 break;
7662 case SCI_GETPASTECONVERTENDINGS:
7663 return convertPastes ? 1 : 0;
7665 case SCI_GETCHARACTERPOINTER:
7666 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
7668 case SCI_GETRANGEPOINTER:
7669 return reinterpret_cast<sptr_t>(pdoc->RangePointer(static_cast<int>(wParam), static_cast<int>(lParam)));
7671 case SCI_GETGAPPOSITION:
7672 return pdoc->GapPosition();
7674 case SCI_SETEXTRAASCENT:
7675 vs.extraAscent = static_cast<int>(wParam);
7676 InvalidateStyleRedraw();
7677 break;
7679 case SCI_GETEXTRAASCENT:
7680 return vs.extraAscent;
7682 case SCI_SETEXTRADESCENT:
7683 vs.extraDescent = static_cast<int>(wParam);
7684 InvalidateStyleRedraw();
7685 break;
7687 case SCI_GETEXTRADESCENT:
7688 return vs.extraDescent;
7690 case SCI_MARGINSETSTYLEOFFSET:
7691 vs.marginStyleOffset = static_cast<int>(wParam);
7692 InvalidateStyleRedraw();
7693 break;
7695 case SCI_MARGINGETSTYLEOFFSET:
7696 return vs.marginStyleOffset;
7698 case SCI_SETMARGINOPTIONS:
7699 marginOptions = static_cast<int>(wParam);
7700 break;
7702 case SCI_GETMARGINOPTIONS:
7703 return marginOptions;
7705 case SCI_MARGINSETTEXT:
7706 pdoc->MarginSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7707 break;
7709 case SCI_MARGINGETTEXT: {
7710 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7711 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
7714 case SCI_MARGINSETSTYLE:
7715 pdoc->MarginSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
7716 break;
7718 case SCI_MARGINGETSTYLE: {
7719 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7720 return st.style;
7723 case SCI_MARGINSETSTYLES:
7724 pdoc->MarginSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
7725 break;
7727 case SCI_MARGINGETSTYLES: {
7728 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7729 return BytesResult(lParam, st.styles, st.length);
7732 case SCI_MARGINTEXTCLEARALL:
7733 pdoc->MarginClearAll();
7734 break;
7736 case SCI_ANNOTATIONSETTEXT:
7737 pdoc->AnnotationSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7738 break;
7740 case SCI_ANNOTATIONGETTEXT: {
7741 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7742 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
7745 case SCI_ANNOTATIONGETSTYLE: {
7746 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7747 return st.style;
7750 case SCI_ANNOTATIONSETSTYLE:
7751 pdoc->AnnotationSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
7752 break;
7754 case SCI_ANNOTATIONSETSTYLES:
7755 pdoc->AnnotationSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
7756 break;
7758 case SCI_ANNOTATIONGETSTYLES: {
7759 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7760 return BytesResult(lParam, st.styles, st.length);
7763 case SCI_ANNOTATIONGETLINES:
7764 return pdoc->AnnotationLines(static_cast<int>(wParam));
7766 case SCI_ANNOTATIONCLEARALL:
7767 pdoc->AnnotationClearAll();
7768 break;
7770 case SCI_ANNOTATIONSETVISIBLE:
7771 SetAnnotationVisible(static_cast<int>(wParam));
7772 break;
7774 case SCI_ANNOTATIONGETVISIBLE:
7775 return vs.annotationVisible;
7777 case SCI_ANNOTATIONSETSTYLEOFFSET:
7778 vs.annotationStyleOffset = static_cast<int>(wParam);
7779 InvalidateStyleRedraw();
7780 break;
7782 case SCI_ANNOTATIONGETSTYLEOFFSET:
7783 return vs.annotationStyleOffset;
7785 case SCI_RELEASEALLEXTENDEDSTYLES:
7786 vs.ReleaseAllExtendedStyles();
7787 break;
7789 case SCI_ALLOCATEEXTENDEDSTYLES:
7790 return vs.AllocateExtendedStyles(static_cast<int>(wParam));
7792 case SCI_ADDUNDOACTION:
7793 pdoc->AddUndoAction(static_cast<int>(wParam), lParam & UNDO_MAY_COALESCE);
7794 break;
7796 case SCI_SETMOUSESELECTIONRECTANGULARSWITCH:
7797 mouseSelectionRectangularSwitch = wParam != 0;
7798 break;
7800 case SCI_GETMOUSESELECTIONRECTANGULARSWITCH:
7801 return mouseSelectionRectangularSwitch;
7803 case SCI_SETMULTIPLESELECTION:
7804 multipleSelection = wParam != 0;
7805 InvalidateCaret();
7806 break;
7808 case SCI_GETMULTIPLESELECTION:
7809 return multipleSelection;
7811 case SCI_SETADDITIONALSELECTIONTYPING:
7812 additionalSelectionTyping = wParam != 0;
7813 InvalidateCaret();
7814 break;
7816 case SCI_GETADDITIONALSELECTIONTYPING:
7817 return additionalSelectionTyping;
7819 case SCI_SETMULTIPASTE:
7820 multiPasteMode = static_cast<int>(wParam);
7821 break;
7823 case SCI_GETMULTIPASTE:
7824 return multiPasteMode;
7826 case SCI_SETADDITIONALCARETSBLINK:
7827 view.additionalCaretsBlink = wParam != 0;
7828 InvalidateCaret();
7829 break;
7831 case SCI_GETADDITIONALCARETSBLINK:
7832 return view.additionalCaretsBlink;
7834 case SCI_SETADDITIONALCARETSVISIBLE:
7835 view.additionalCaretsVisible = wParam != 0;
7836 InvalidateCaret();
7837 break;
7839 case SCI_GETADDITIONALCARETSVISIBLE:
7840 return view.additionalCaretsVisible;
7842 case SCI_GETSELECTIONS:
7843 return sel.Count();
7845 case SCI_GETSELECTIONEMPTY:
7846 return sel.Empty();
7848 case SCI_CLEARSELECTIONS:
7849 sel.Clear();
7850 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7851 Redraw();
7852 break;
7854 case SCI_SETSELECTION:
7855 sel.SetSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
7856 Redraw();
7857 break;
7859 case SCI_ADDSELECTION:
7860 sel.AddSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
7861 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7862 Redraw();
7863 break;
7865 case SCI_DROPSELECTIONN:
7866 sel.DropSelection(static_cast<int>(wParam));
7867 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7868 Redraw();
7869 break;
7871 case SCI_SETMAINSELECTION:
7872 sel.SetMain(static_cast<int>(wParam));
7873 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7874 Redraw();
7875 break;
7877 case SCI_GETMAINSELECTION:
7878 return sel.Main();
7880 case SCI_SETSELECTIONNCARET:
7881 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
7882 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7883 Redraw();
7884 break;
7886 case SCI_GETSELECTIONNCARET:
7887 return sel.Range(wParam).caret.Position();
7889 case SCI_SETSELECTIONNANCHOR:
7890 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
7891 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7892 Redraw();
7893 break;
7894 case SCI_GETSELECTIONNANCHOR:
7895 return sel.Range(wParam).anchor.Position();
7897 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
7898 sel.Range(wParam).caret.SetVirtualSpace(static_cast<int>(lParam));
7899 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7900 Redraw();
7901 break;
7903 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
7904 return sel.Range(wParam).caret.VirtualSpace();
7906 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
7907 sel.Range(wParam).anchor.SetVirtualSpace(static_cast<int>(lParam));
7908 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7909 Redraw();
7910 break;
7912 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
7913 return sel.Range(wParam).anchor.VirtualSpace();
7915 case SCI_SETSELECTIONNSTART:
7916 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
7917 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7918 Redraw();
7919 break;
7921 case SCI_GETSELECTIONNSTART:
7922 return sel.Range(wParam).Start().Position();
7924 case SCI_SETSELECTIONNEND:
7925 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
7926 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7927 Redraw();
7928 break;
7930 case SCI_GETSELECTIONNEND:
7931 return sel.Range(wParam).End().Position();
7933 case SCI_SETRECTANGULARSELECTIONCARET:
7934 if (!sel.IsRectangular())
7935 sel.Clear();
7936 sel.selType = Selection::selRectangle;
7937 sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
7938 SetRectangularRange();
7939 Redraw();
7940 break;
7942 case SCI_GETRECTANGULARSELECTIONCARET:
7943 return sel.Rectangular().caret.Position();
7945 case SCI_SETRECTANGULARSELECTIONANCHOR:
7946 if (!sel.IsRectangular())
7947 sel.Clear();
7948 sel.selType = Selection::selRectangle;
7949 sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
7950 SetRectangularRange();
7951 Redraw();
7952 break;
7954 case SCI_GETRECTANGULARSELECTIONANCHOR:
7955 return sel.Rectangular().anchor.Position();
7957 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
7958 if (!sel.IsRectangular())
7959 sel.Clear();
7960 sel.selType = Selection::selRectangle;
7961 sel.Rectangular().caret.SetVirtualSpace(static_cast<int>(wParam));
7962 SetRectangularRange();
7963 Redraw();
7964 break;
7966 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
7967 return sel.Rectangular().caret.VirtualSpace();
7969 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
7970 if (!sel.IsRectangular())
7971 sel.Clear();
7972 sel.selType = Selection::selRectangle;
7973 sel.Rectangular().anchor.SetVirtualSpace(static_cast<int>(wParam));
7974 SetRectangularRange();
7975 Redraw();
7976 break;
7978 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
7979 return sel.Rectangular().anchor.VirtualSpace();
7981 case SCI_SETVIRTUALSPACEOPTIONS:
7982 virtualSpaceOptions = static_cast<int>(wParam);
7983 break;
7985 case SCI_GETVIRTUALSPACEOPTIONS:
7986 return virtualSpaceOptions;
7988 case SCI_SETADDITIONALSELFORE:
7989 vs.selAdditionalForeground = ColourDesired(static_cast<long>(wParam));
7990 InvalidateStyleRedraw();
7991 break;
7993 case SCI_SETADDITIONALSELBACK:
7994 vs.selAdditionalBackground = ColourDesired(static_cast<long>(wParam));
7995 InvalidateStyleRedraw();
7996 break;
7998 case SCI_SETADDITIONALSELALPHA:
7999 vs.selAdditionalAlpha = static_cast<int>(wParam);
8000 InvalidateStyleRedraw();
8001 break;
8003 case SCI_GETADDITIONALSELALPHA:
8004 return vs.selAdditionalAlpha;
8006 case SCI_SETADDITIONALCARETFORE:
8007 vs.additionalCaretColour = ColourDesired(static_cast<long>(wParam));
8008 InvalidateStyleRedraw();
8009 break;
8011 case SCI_GETADDITIONALCARETFORE:
8012 return vs.additionalCaretColour.AsLong();
8014 case SCI_ROTATESELECTION:
8015 sel.RotateMain();
8016 InvalidateWholeSelection();
8017 break;
8019 case SCI_SWAPMAINANCHORCARET:
8020 InvalidateSelection(sel.RangeMain());
8021 sel.RangeMain().Swap();
8022 break;
8024 case SCI_MULTIPLESELECTADDNEXT:
8025 MultipleSelectAdd(addOne);
8026 break;
8028 case SCI_MULTIPLESELECTADDEACH:
8029 MultipleSelectAdd(addEach);
8030 break;
8032 case SCI_CHANGELEXERSTATE:
8033 pdoc->ChangeLexerState(static_cast<int>(wParam), static_cast<int>(lParam));
8034 break;
8036 case SCI_SETIDENTIFIER:
8037 SetCtrlID(static_cast<int>(wParam));
8038 break;
8040 case SCI_GETIDENTIFIER:
8041 return GetCtrlID();
8043 case SCI_SETTECHNOLOGY:
8044 // No action by default
8045 break;
8047 case SCI_GETTECHNOLOGY:
8048 return technology;
8050 case SCI_COUNTCHARACTERS:
8051 return pdoc->CountCharacters(static_cast<int>(wParam), static_cast<int>(lParam));
8053 default:
8054 return DefWndProc(iMessage, wParam, lParam);
8056 //Platform::DebugPrintf("end wnd proc\n");
8057 return 0l;