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