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