Upgrade scintilla to 3.6.7
[TortoiseGit.git] / ext / scintilla / src / Editor.cxx
blob5f5262f40a131ada90fccd5b3a5d1045f0c5a480
1 // Scintilla source code edit control
2 /** @file Editor.cxx
3 ** Main code for the edit control.
4 **/
5 // Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <assert.h>
12 #include <ctype.h>
14 #include <cmath>
15 #include <stdexcept>
16 #include <string>
17 #include <vector>
18 #include <map>
19 #include <algorithm>
20 #include <memory>
22 #include "Platform.h"
24 #include "ILexer.h"
25 #include "Scintilla.h"
27 #include "StringCopy.h"
28 #include "Position.h"
29 #include "SplitVector.h"
30 #include "Partitioning.h"
31 #include "RunStyles.h"
32 #include "ContractionState.h"
33 #include "CellBuffer.h"
34 #include "PerLine.h"
35 #include "KeyMap.h"
36 #include "Indicator.h"
37 #include "XPM.h"
38 #include "LineMarker.h"
39 #include "Style.h"
40 #include "ViewStyle.h"
41 #include "CharClassify.h"
42 #include "Decoration.h"
43 #include "CaseFolder.h"
44 #include "Document.h"
45 #include "UniConversion.h"
46 #include "Selection.h"
47 #include "PositionCache.h"
48 #include "EditModel.h"
49 #include "MarginView.h"
50 #include "EditView.h"
51 #include "Editor.h"
53 #ifdef SCI_NAMESPACE
54 using namespace Scintilla;
55 #endif
58 return whether this modification represents an operation that
59 may reasonably be deferred (not done now OR [possibly] at all)
61 static bool CanDeferToLastStep(const DocModification &mh) {
62 if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
63 return true; // CAN skip
64 if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)))
65 return false; // MUST do
66 if (mh.modificationType & SC_MULTISTEPUNDOREDO)
67 return true; // CAN skip
68 return false; // PRESUMABLY must do
71 static bool CanEliminate(const DocModification &mh) {
72 return
73 (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
77 return whether this modification represents the FINAL step
78 in a [possibly lengthy] multi-step Undo/Redo sequence
80 static bool IsLastStep(const DocModification &mh) {
81 return
82 (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0
83 && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
84 && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
85 && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
88 Timer::Timer() :
89 ticking(false), ticksToWait(0), tickerID(0) {}
91 Idler::Idler() :
92 state(false), idlerID(0) {}
94 static inline bool IsAllSpacesOrTabs(const char *s, unsigned int len) {
95 for (unsigned int i = 0; i < len; i++) {
96 // This is safe because IsSpaceOrTab() will return false for null terminators
97 if (!IsSpaceOrTab(s[i]))
98 return false;
100 return true;
103 Editor::Editor() {
104 ctrlID = 0;
106 stylesValid = false;
107 technology = SC_TECHNOLOGY_DEFAULT;
108 scaleRGBAImage = 100.0f;
110 cursorMode = SC_CURSORNORMAL;
112 hasFocus = false;
113 errorStatus = 0;
114 mouseDownCaptures = true;
116 lastClickTime = 0;
117 doubleClickCloseThreshold = Point(3, 3);
118 dwellDelay = SC_TIME_FOREVER;
119 ticksToDwell = SC_TIME_FOREVER;
120 dwelling = false;
121 ptMouseLast.x = 0;
122 ptMouseLast.y = 0;
123 inDragDrop = ddNone;
124 dropWentOutside = false;
125 posDrop = SelectionPosition(invalidPosition);
126 hotSpotClickPos = INVALID_POSITION;
127 selectionType = selChar;
129 lastXChosen = 0;
130 lineAnchorPos = 0;
131 originalAnchorPos = 0;
132 wordSelectAnchorStartPos = 0;
133 wordSelectAnchorEndPos = 0;
134 wordSelectInitialCaretPos = -1;
136 caretXPolicy = CARET_SLOP | CARET_EVEN;
137 caretXSlop = 50;
139 caretYPolicy = CARET_EVEN;
140 caretYSlop = 0;
142 visiblePolicy = 0;
143 visibleSlop = 0;
145 searchAnchor = 0;
147 xCaretMargin = 50;
148 horizontalScrollBarVisible = true;
149 scrollWidth = 2000;
150 verticalScrollBarVisible = true;
151 endAtLastLine = true;
152 caretSticky = SC_CARETSTICKY_OFF;
153 marginOptions = SC_MARGINOPTION_NONE;
154 mouseSelectionRectangularSwitch = false;
155 multipleSelection = false;
156 additionalSelectionTyping = false;
157 multiPasteMode = SC_MULTIPASTE_ONCE;
158 virtualSpaceOptions = SCVS_NONE;
160 targetStart = 0;
161 targetEnd = 0;
162 searchFlags = 0;
164 topLine = 0;
165 posTopLine = 0;
167 lengthForEncode = -1;
169 needUpdateUI = 0;
170 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
172 paintState = notPainting;
173 paintAbandonedByStyling = false;
174 paintingAllText = false;
175 willRedrawAll = false;
176 idleStyling = SC_IDLESTYLING_NONE;
177 needIdleStyling = false;
179 modEventMask = SC_MODEVENTMASKALL;
181 pdoc->AddWatcher(this, 0);
183 recordingMacro = false;
184 foldAutomatic = 0;
186 convertPastes = true;
188 SetRepresentations();
191 Editor::~Editor() {
192 pdoc->RemoveWatcher(this, 0);
193 DropGraphics(true);
196 void Editor::Finalise() {
197 SetIdle(false);
198 CancelModes();
201 void Editor::SetRepresentations() {
202 reprs.Clear();
204 // C0 control set
205 const char *reps[] = {
206 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
207 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
208 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
209 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
211 for (size_t j=0; j < ELEMENTS(reps); j++) {
212 char c[2] = { static_cast<char>(j), 0 };
213 reprs.SetRepresentation(c, reps[j]);
216 // C1 control set
217 // As well as Unicode mode, ISO-8859-1 should use these
218 if (IsUnicodeMode()) {
219 const char *repsC1[] = {
220 "PAD", "HOP", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
221 "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
222 "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
223 "SOS", "SGCI", "SCI", "CSI", "ST", "OSC", "PM", "APC"
225 for (size_t j=0; j < ELEMENTS(repsC1); j++) {
226 char c1[3] = { '\xc2', static_cast<char>(0x80+j), 0 };
227 reprs.SetRepresentation(c1, repsC1[j]);
229 reprs.SetRepresentation("\xe2\x80\xa8", "LS");
230 reprs.SetRepresentation("\xe2\x80\xa9", "PS");
233 // UTF-8 invalid bytes
234 if (IsUnicodeMode()) {
235 for (int k=0x80; k < 0x100; k++) {
236 char hiByte[2] = { static_cast<char>(k), 0 };
237 char hexits[4];
238 sprintf(hexits, "x%2X", k);
239 reprs.SetRepresentation(hiByte, hexits);
244 void Editor::DropGraphics(bool freeObjects) {
245 marginView.DropGraphics(freeObjects);
246 view.DropGraphics(freeObjects);
249 void Editor::AllocateGraphics() {
250 marginView.AllocateGraphics(vs);
251 view.AllocateGraphics(vs);
254 void Editor::InvalidateStyleData() {
255 stylesValid = false;
256 vs.technology = technology;
257 DropGraphics(false);
258 AllocateGraphics();
259 view.llc.Invalidate(LineLayout::llInvalid);
260 view.posCache.Clear();
263 void Editor::InvalidateStyleRedraw() {
264 NeedWrapping();
265 InvalidateStyleData();
266 Redraw();
269 void Editor::RefreshStyleData() {
270 if (!stylesValid) {
271 stylesValid = true;
272 AutoSurface surface(this);
273 if (surface) {
274 vs.Refresh(*surface, pdoc->tabInChars);
276 SetScrollBars();
277 SetRectangularRange();
281 Point Editor::GetVisibleOriginInMain() const {
282 return Point(0,0);
285 Point Editor::DocumentPointFromView(Point ptView) const {
286 Point ptDocument = ptView;
287 if (wMargin.GetID()) {
288 Point ptOrigin = GetVisibleOriginInMain();
289 ptDocument.x += ptOrigin.x;
290 ptDocument.y += ptOrigin.y;
291 } else {
292 ptDocument.x += xOffset;
293 ptDocument.y += topLine * vs.lineHeight;
295 return ptDocument;
298 int Editor::TopLineOfMain() const {
299 if (wMargin.GetID())
300 return 0;
301 else
302 return topLine;
305 PRectangle Editor::GetClientRectangle() const {
306 Window win = wMain;
307 return win.GetClientPosition();
310 PRectangle Editor::GetClientDrawingRectangle() {
311 return GetClientRectangle();
314 PRectangle Editor::GetTextRectangle() const {
315 PRectangle rc = GetClientRectangle();
316 rc.left += vs.textStart;
317 rc.right -= vs.rightMarginWidth;
318 return rc;
321 int Editor::LinesOnScreen() const {
322 PRectangle rcClient = GetClientRectangle();
323 int htClient = static_cast<int>(rcClient.bottom - rcClient.top);
324 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
325 return htClient / vs.lineHeight;
328 int Editor::LinesToScroll() const {
329 int retVal = LinesOnScreen() - 1;
330 if (retVal < 1)
331 return 1;
332 else
333 return retVal;
336 int Editor::MaxScrollPos() const {
337 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
338 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
339 int retVal = cs.LinesDisplayed();
340 if (endAtLastLine) {
341 retVal -= LinesOnScreen();
342 } else {
343 retVal--;
345 if (retVal < 0) {
346 return 0;
347 } else {
348 return retVal;
352 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
353 if (sp.Position() < 0) {
354 return SelectionPosition(0);
355 } else if (sp.Position() > pdoc->Length()) {
356 return SelectionPosition(pdoc->Length());
357 } else {
358 // If not at end of line then set offset to 0
359 if (!pdoc->IsLineEndPosition(sp.Position()))
360 sp.SetVirtualSpace(0);
361 return sp;
365 Point Editor::LocationFromPosition(SelectionPosition pos) {
366 RefreshStyleData();
367 AutoSurface surface(this);
368 return view.LocationFromPosition(surface, *this, pos, topLine, vs);
371 Point Editor::LocationFromPosition(int pos) {
372 return LocationFromPosition(SelectionPosition(pos));
375 int Editor::XFromPosition(int pos) {
376 Point pt = LocationFromPosition(pos);
377 return static_cast<int>(pt.x) - vs.textStart + xOffset;
380 int Editor::XFromPosition(SelectionPosition sp) {
381 Point pt = LocationFromPosition(sp);
382 return static_cast<int>(pt.x) - vs.textStart + xOffset;
385 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
386 RefreshStyleData();
387 AutoSurface surface(this);
389 if (canReturnInvalid) {
390 PRectangle rcClient = GetTextRectangle();
391 // May be in scroll view coordinates so translate back to main view
392 Point ptOrigin = GetVisibleOriginInMain();
393 rcClient.Move(-ptOrigin.x, -ptOrigin.y);
394 if (!rcClient.Contains(pt))
395 return SelectionPosition(INVALID_POSITION);
396 if (pt.x < vs.textStart)
397 return SelectionPosition(INVALID_POSITION);
398 if (pt.y < 0)
399 return SelectionPosition(INVALID_POSITION);
401 pt = DocumentPointFromView(pt);
402 return view.SPositionFromLocation(surface, *this, pt, canReturnInvalid, charPosition, virtualSpace, vs);
405 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
406 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
410 * Find the document position corresponding to an x coordinate on a particular document line.
411 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
412 * This method is used for rectangular selections and does not work on wrapped lines.
414 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
415 RefreshStyleData();
416 if (lineDoc >= pdoc->LinesTotal())
417 return SelectionPosition(pdoc->Length());
418 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
419 AutoSurface surface(this);
420 return view.SPositionFromLineX(surface, *this, lineDoc, x, vs);
423 int Editor::PositionFromLineX(int lineDoc, int x) {
424 return SPositionFromLineX(lineDoc, x).Position();
427 int Editor::LineFromLocation(Point pt) const {
428 return cs.DocFromDisplay(static_cast<int>(pt.y) / vs.lineHeight + topLine);
431 void Editor::SetTopLine(int topLineNew) {
432 if ((topLine != topLineNew) && (topLineNew >= 0)) {
433 topLine = topLineNew;
434 ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
436 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
440 * If painting then abandon the painting because a wider redraw is needed.
441 * @return true if calling code should stop drawing.
443 bool Editor::AbandonPaint() {
444 if ((paintState == painting) && !paintingAllText) {
445 paintState = paintAbandoned;
447 return paintState == paintAbandoned;
450 void Editor::RedrawRect(PRectangle rc) {
451 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
453 // Clip the redraw rectangle into the client area
454 PRectangle rcClient = GetClientRectangle();
455 if (rc.top < rcClient.top)
456 rc.top = rcClient.top;
457 if (rc.bottom > rcClient.bottom)
458 rc.bottom = rcClient.bottom;
459 if (rc.left < rcClient.left)
460 rc.left = rcClient.left;
461 if (rc.right > rcClient.right)
462 rc.right = rcClient.right;
464 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
465 wMain.InvalidateRectangle(rc);
469 void Editor::DiscardOverdraw() {
470 // Overridden on platforms that may draw outside visible area.
473 void Editor::Redraw() {
474 //Platform::DebugPrintf("Redraw all\n");
475 PRectangle rcClient = GetClientRectangle();
476 wMain.InvalidateRectangle(rcClient);
477 if (wMargin.GetID())
478 wMargin.InvalidateAll();
479 //wMain.InvalidateAll();
482 void Editor::RedrawSelMargin(int line, bool allAfter) {
483 const bool markersInText = vs.maskInLine || vs.maskDrawInText;
484 if (!wMargin.GetID() || markersInText) { // May affect text area so may need to abandon and retry
485 if (AbandonPaint()) {
486 return;
489 if (wMargin.GetID() && markersInText) {
490 Redraw();
491 return;
493 PRectangle rcMarkers = GetClientRectangle();
494 if (!markersInText) {
495 // Normal case: just draw the margin
496 rcMarkers.right = rcMarkers.left + vs.fixedColumnWidth;
498 if (line != -1) {
499 PRectangle rcLine = RectangleFromRange(Range(pdoc->LineStart(line)), 0);
501 // Inflate line rectangle if there are image markers with height larger than line height
502 if (vs.largestMarkerHeight > vs.lineHeight) {
503 int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
504 rcLine.top -= delta;
505 rcLine.bottom += delta;
506 if (rcLine.top < rcMarkers.top)
507 rcLine.top = rcMarkers.top;
508 if (rcLine.bottom > rcMarkers.bottom)
509 rcLine.bottom = rcMarkers.bottom;
512 rcMarkers.top = rcLine.top;
513 if (!allAfter)
514 rcMarkers.bottom = rcLine.bottom;
515 if (rcMarkers.Empty())
516 return;
518 if (wMargin.GetID()) {
519 Point ptOrigin = GetVisibleOriginInMain();
520 rcMarkers.Move(-ptOrigin.x, -ptOrigin.y);
521 wMargin.InvalidateRectangle(rcMarkers);
522 } else {
523 wMain.InvalidateRectangle(rcMarkers);
527 PRectangle Editor::RectangleFromRange(Range r, int overlap) {
528 const int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(r.First()));
529 const int maxLine = cs.DisplayLastFromDoc(pdoc->LineFromPosition(r.Last()));
530 const PRectangle rcClientDrawing = GetClientDrawingRectangle();
531 PRectangle rc;
532 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
533 rc.left = static_cast<XYPOSITION>(vs.textStart - leftTextOverlap);
534 rc.top = static_cast<XYPOSITION>((minLine - TopLineOfMain()) * vs.lineHeight - overlap);
535 if (rc.top < rcClientDrawing.top)
536 rc.top = rcClientDrawing.top;
537 // Extend to right of prepared area if any to prevent artifacts from caret line highlight
538 rc.right = rcClientDrawing.right;
539 rc.bottom = static_cast<XYPOSITION>((maxLine - TopLineOfMain() + 1) * vs.lineHeight + overlap);
541 return rc;
544 void Editor::InvalidateRange(int start, int end) {
545 RedrawRect(RectangleFromRange(Range(start, end), view.LinesOverlap() ? vs.lineOverlap : 0));
548 int Editor::CurrentPosition() const {
549 return sel.MainCaret();
552 bool Editor::SelectionEmpty() const {
553 return sel.Empty();
556 SelectionPosition Editor::SelectionStart() {
557 return sel.RangeMain().Start();
560 SelectionPosition Editor::SelectionEnd() {
561 return sel.RangeMain().End();
564 void Editor::SetRectangularRange() {
565 if (sel.IsRectangular()) {
566 int xAnchor = XFromPosition(sel.Rectangular().anchor);
567 int xCaret = XFromPosition(sel.Rectangular().caret);
568 if (sel.selType == Selection::selThin) {
569 xCaret = xAnchor;
571 int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
572 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
573 int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
574 for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
575 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
576 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
577 range.ClearVirtualSpace();
578 if (line == lineAnchorRect)
579 sel.SetSelection(range);
580 else
581 sel.AddSelectionWithoutTrim(range);
586 void Editor::ThinRectangularRange() {
587 if (sel.IsRectangular()) {
588 sel.selType = Selection::selThin;
589 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
590 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
591 } else {
592 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
594 SetRectangularRange();
598 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
599 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
600 invalidateWholeSelection = true;
602 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
603 // +1 for lastAffected ensures caret repainted
604 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
605 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
606 if (invalidateWholeSelection) {
607 for (size_t r=0; r<sel.Count(); r++) {
608 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
609 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
610 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
611 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
614 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
615 InvalidateRange(firstAffected, lastAffected);
618 void Editor::InvalidateWholeSelection() {
619 InvalidateSelection(sel.RangeMain(), true);
622 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
623 currentPos_ = ClampPositionIntoDocument(currentPos_);
624 anchor_ = ClampPositionIntoDocument(anchor_);
625 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
626 /* For Line selection - ensure the anchor and caret are always
627 at the beginning and end of the region lines. */
628 if (sel.selType == Selection::selLines) {
629 if (currentPos_ > anchor_) {
630 anchor_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
631 currentPos_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
632 } else {
633 currentPos_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
634 anchor_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
637 SelectionRange rangeNew(currentPos_, anchor_);
638 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
639 InvalidateSelection(rangeNew);
641 sel.RangeMain() = rangeNew;
642 SetRectangularRange();
643 ClaimSelection();
644 SetHoverIndicatorPosition(sel.MainCaret());
646 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
647 RedrawSelMargin();
649 QueueIdleWork(WorkNeeded::workUpdateUI);
652 void Editor::SetSelection(int currentPos_, int anchor_) {
653 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
656 // Just move the caret on the main selection
657 void Editor::SetSelection(SelectionPosition currentPos_) {
658 currentPos_ = ClampPositionIntoDocument(currentPos_);
659 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
660 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
661 InvalidateSelection(SelectionRange(currentPos_));
663 if (sel.IsRectangular()) {
664 sel.Rectangular() =
665 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
666 SetRectangularRange();
667 } else {
668 sel.RangeMain() =
669 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
671 ClaimSelection();
672 SetHoverIndicatorPosition(sel.MainCaret());
674 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
675 RedrawSelMargin();
677 QueueIdleWork(WorkNeeded::workUpdateUI);
680 void Editor::SetSelection(int currentPos_) {
681 SetSelection(SelectionPosition(currentPos_));
684 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
685 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
686 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
687 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
688 InvalidateSelection(rangeNew);
690 sel.Clear();
691 sel.RangeMain() = rangeNew;
692 SetRectangularRange();
693 ClaimSelection();
694 SetHoverIndicatorPosition(sel.MainCaret());
696 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
697 RedrawSelMargin();
699 QueueIdleWork(WorkNeeded::workUpdateUI);
702 void Editor::SetEmptySelection(int currentPos_) {
703 SetEmptySelection(SelectionPosition(currentPos_));
706 void Editor::MultipleSelectAdd(AddNumber addNumber) {
707 if (SelectionEmpty() || !multipleSelection) {
708 // Select word at caret
709 const int startWord = pdoc->ExtendWordSelect(sel.MainCaret(), -1, true);
710 const int endWord = pdoc->ExtendWordSelect(startWord, 1, true);
711 TrimAndSetSelection(endWord, startWord);
713 } else {
715 if (!pdoc->HasCaseFolder())
716 pdoc->SetCaseFolder(CaseFolderForEncoding());
718 const Range rangeMainSelection(sel.RangeMain().Start().Position(), sel.RangeMain().End().Position());
719 const std::string selectedText = RangeText(rangeMainSelection.start, rangeMainSelection.end);
721 const Range rangeTarget(targetStart, targetEnd);
722 std::vector<Range> searchRanges;
723 // Search should be over the target range excluding the current selection so
724 // may need to search 2 ranges, after the selection then before the selection.
725 if (rangeTarget.Overlaps(rangeMainSelection)) {
726 // Common case is that the selection is completely within the target but
727 // may also have overlap at start or end.
728 if (rangeMainSelection.end < rangeTarget.end)
729 searchRanges.push_back(Range(rangeMainSelection.end, rangeTarget.end));
730 if (rangeTarget.start < rangeMainSelection.start)
731 searchRanges.push_back(Range(rangeTarget.start, rangeMainSelection.start));
732 } else {
733 // No overlap
734 searchRanges.push_back(rangeTarget);
737 for (std::vector<Range>::const_iterator it = searchRanges.begin(); it != searchRanges.end(); ++it) {
738 int searchStart = it->start;
739 const int searchEnd = it->end;
740 for (;;) {
741 int lengthFound = static_cast<int>(selectedText.length());
742 int pos = pdoc->FindText(searchStart, searchEnd, selectedText.c_str(),
743 searchFlags, &lengthFound);
744 if (pos >= 0) {
745 sel.AddSelection(SelectionRange(pos + lengthFound, pos));
746 ScrollRange(sel.RangeMain());
747 Redraw();
748 if (addNumber == addOne)
749 return;
750 searchStart = pos + lengthFound;
751 } else {
752 break;
759 bool Editor::RangeContainsProtected(int start, int end) const {
760 if (vs.ProtectionActive()) {
761 if (start > end) {
762 int t = start;
763 start = end;
764 end = t;
766 for (int pos = start; pos < end; pos++) {
767 if (vs.styles[pdoc->StyleIndexAt(pos)].IsProtected())
768 return true;
771 return false;
774 bool Editor::SelectionContainsProtected() {
775 for (size_t r=0; r<sel.Count(); r++) {
776 if (RangeContainsProtected(sel.Range(r).Start().Position(),
777 sel.Range(r).End().Position())) {
778 return true;
781 return false;
785 * Asks document to find a good position and then moves out of any invisible positions.
787 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
788 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
791 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
792 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
793 if (posMoved != pos.Position())
794 pos.SetPosition(posMoved);
795 if (vs.ProtectionActive()) {
796 if (moveDir > 0) {
797 if ((pos.Position() > 0) && vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()) {
798 while ((pos.Position() < pdoc->Length()) &&
799 (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()))
800 pos.Add(1);
802 } else if (moveDir < 0) {
803 if (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()) {
804 while ((pos.Position() > 0) &&
805 (vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()))
806 pos.Add(-1);
810 return pos;
813 void Editor::MovedCaret(SelectionPosition newPos, SelectionPosition previousPos, bool ensureVisible) {
814 const int currentLine = pdoc->LineFromPosition(newPos.Position());
815 if (ensureVisible) {
816 // In case in need of wrapping to ensure DisplayFromDoc works.
817 if (currentLine >= wrapPending.start)
818 WrapLines(wsAll);
819 XYScrollPosition newXY = XYScrollToMakeVisible(
820 SelectionRange(posDrag.IsValid() ? posDrag : newPos), xysDefault);
821 if (previousPos.IsValid() && (newXY.xOffset == xOffset)) {
822 // simple vertical scroll then invalidate
823 ScrollTo(newXY.topLine);
824 InvalidateSelection(SelectionRange(previousPos), true);
825 } else {
826 SetXYScroll(newXY);
830 ShowCaretAtCurrentPosition();
831 NotifyCaretMove();
833 ClaimSelection();
834 SetHoverIndicatorPosition(sel.MainCaret());
835 QueueIdleWork(WorkNeeded::workUpdateUI);
837 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
838 RedrawSelMargin();
842 void Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
843 const SelectionPosition spCaret = ((sel.Count() == 1) && sel.Empty()) ?
844 sel.Last() : SelectionPosition(INVALID_POSITION);
846 int delta = newPos.Position() - sel.MainCaret();
847 newPos = ClampPositionIntoDocument(newPos);
848 newPos = MovePositionOutsideChar(newPos, delta);
849 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
850 // Can't turn into multiple selection so clear additional selections
851 InvalidateSelection(SelectionRange(newPos), true);
852 sel.DropAdditionalRanges();
854 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
855 // Switching to rectangular
856 InvalidateSelection(sel.RangeMain(), false);
857 SelectionRange rangeMain = sel.RangeMain();
858 sel.Clear();
859 sel.Rectangular() = rangeMain;
861 if (selt != Selection::noSel) {
862 sel.selType = selt;
864 if (selt != Selection::noSel || sel.MoveExtends()) {
865 SetSelection(newPos);
866 } else {
867 SetEmptySelection(newPos);
870 MovedCaret(newPos, spCaret, ensureVisible);
873 void Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
874 MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
877 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
878 pos = ClampPositionIntoDocument(pos);
879 pos = MovePositionOutsideChar(pos, moveDir);
880 int lineDoc = pdoc->LineFromPosition(pos.Position());
881 if (cs.GetVisible(lineDoc)) {
882 return pos;
883 } else {
884 int lineDisplay = cs.DisplayFromDoc(lineDoc);
885 if (moveDir > 0) {
886 // lineDisplay is already line before fold as lines in fold use display line of line after fold
887 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
888 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
889 } else {
890 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
891 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
896 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
897 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
900 Point Editor::PointMainCaret() {
901 return LocationFromPosition(sel.Range(sel.Main()).caret);
905 * Choose the x position that the caret will try to stick to
906 * as it moves up and down.
908 void Editor::SetLastXChosen() {
909 Point pt = PointMainCaret();
910 lastXChosen = static_cast<int>(pt.x) + xOffset;
913 void Editor::ScrollTo(int line, bool moveThumb) {
914 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
915 if (topLineNew != topLine) {
916 // Try to optimise small scrolls
917 #ifndef UNDER_CE
918 int linesToMove = topLine - topLineNew;
919 bool performBlit = (abs(linesToMove) <= 10) && (paintState == notPainting);
920 willRedrawAll = !performBlit;
921 #endif
922 SetTopLine(topLineNew);
923 // Optimize by styling the view as this will invalidate any needed area
924 // which could abort the initial paint if discovered later.
925 StyleAreaBounded(GetClientRectangle(), true);
926 #ifndef UNDER_CE
927 // Perform redraw rather than scroll if many lines would be redrawn anyway.
928 if (performBlit) {
929 ScrollText(linesToMove);
930 } else {
931 Redraw();
933 willRedrawAll = false;
934 #else
935 Redraw();
936 #endif
937 if (moveThumb) {
938 SetVerticalScrollPos();
943 void Editor::ScrollText(int /* linesToMove */) {
944 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
945 Redraw();
948 void Editor::HorizontalScrollTo(int xPos) {
949 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
950 if (xPos < 0)
951 xPos = 0;
952 if (!Wrapping() && (xOffset != xPos)) {
953 xOffset = xPos;
954 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
955 SetHorizontalScrollPos();
956 RedrawRect(GetClientRectangle());
960 void Editor::VerticalCentreCaret() {
961 int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
962 int lineDisplay = cs.DisplayFromDoc(lineDoc);
963 int newTop = lineDisplay - (LinesOnScreen() / 2);
964 if (topLine != newTop) {
965 SetTopLine(newTop > 0 ? newTop : 0);
966 RedrawRect(GetClientRectangle());
970 // Avoid 64 bit compiler warnings.
971 // Scintilla does not support text buffers larger than 2**31
972 static int istrlen(const char *s) {
973 return static_cast<int>(s ? strlen(s) : 0);
976 void Editor::MoveSelectedLines(int lineDelta) {
978 // if selection doesn't start at the beginning of the line, set the new start
979 int selectionStart = SelectionStart().Position();
980 int startLine = pdoc->LineFromPosition(selectionStart);
981 int beginningOfStartLine = pdoc->LineStart(startLine);
982 selectionStart = beginningOfStartLine;
984 // if selection doesn't end at the beginning of a line greater than that of the start,
985 // then set it at the beginning of the next one
986 int selectionEnd = SelectionEnd().Position();
987 int endLine = pdoc->LineFromPosition(selectionEnd);
988 int beginningOfEndLine = pdoc->LineStart(endLine);
989 bool appendEol = false;
990 if (selectionEnd > beginningOfEndLine
991 || selectionStart == selectionEnd) {
992 selectionEnd = pdoc->LineStart(endLine + 1);
993 appendEol = (selectionEnd == pdoc->Length() && pdoc->LineFromPosition(selectionEnd) == endLine);
996 // if there's nowhere for the selection to move
997 // (i.e. at the beginning going up or at the end going down),
998 // stop it right there!
999 if ((selectionStart == 0 && lineDelta < 0)
1000 || (selectionEnd == pdoc->Length() && lineDelta > 0)
1001 || selectionStart == selectionEnd) {
1002 return;
1005 UndoGroup ug(pdoc);
1007 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
1008 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
1009 ClearSelection();
1010 selectionEnd = CurrentPosition();
1012 SetSelection(selectionStart, selectionEnd);
1014 SelectionText selectedText;
1015 CopySelectionRange(&selectedText);
1017 int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
1018 Point currentLocation = LocationFromPosition(CurrentPosition());
1019 int currentLine = LineFromLocation(currentLocation);
1021 if (appendEol)
1022 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
1023 ClearSelection();
1025 const char *eol = StringFromEOLMode(pdoc->eolMode);
1026 if (currentLine + lineDelta >= pdoc->LinesTotal())
1027 pdoc->InsertString(pdoc->Length(), eol, istrlen(eol));
1028 GoToLine(currentLine + lineDelta);
1030 selectionLength = pdoc->InsertString(CurrentPosition(), selectedText.Data(), selectionLength);
1031 if (appendEol) {
1032 const int lengthInserted = pdoc->InsertString(CurrentPosition() + selectionLength, eol, istrlen(eol));
1033 selectionLength += lengthInserted;
1035 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1038 void Editor::MoveSelectedLinesUp() {
1039 MoveSelectedLines(-1);
1042 void Editor::MoveSelectedLinesDown() {
1043 MoveSelectedLines(1);
1046 void Editor::MoveCaretInsideView(bool ensureVisible) {
1047 PRectangle rcClient = GetTextRectangle();
1048 Point pt = PointMainCaret();
1049 if (pt.y < rcClient.top) {
1050 MovePositionTo(SPositionFromLocation(
1051 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top)),
1052 false, false, UserVirtualSpace()),
1053 Selection::noSel, ensureVisible);
1054 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1055 int yOfLastLineFullyDisplayed = static_cast<int>(rcClient.top) + (LinesOnScreen() - 1) * vs.lineHeight;
1056 MovePositionTo(SPositionFromLocation(
1057 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top) + yOfLastLineFullyDisplayed),
1058 false, false, UserVirtualSpace()),
1059 Selection::noSel, ensureVisible);
1063 int Editor::DisplayFromPosition(int pos) {
1064 AutoSurface surface(this);
1065 return view.DisplayFromPosition(surface, *this, pos, vs);
1069 * Ensure the caret is reasonably visible in context.
1071 Caret policy in SciTE
1073 If slop is set, we can define a slop value.
1074 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1075 This zone is defined as a number of pixels near the vertical margins,
1076 and as a number of lines near the horizontal margins.
1077 By keeping the caret away from the edges, it is seen within its context,
1078 so it is likely that the identifier that the caret is on can be completely seen,
1079 and that the current line is seen with some of the lines following it which are
1080 often dependent on that line.
1082 If strict is set, the policy is enforced... strictly.
1083 The caret is centred on the display if slop is not set,
1084 and cannot go in the UZ if slop is set.
1086 If jumps is set, the display is moved more energetically
1087 so the caret can move in the same direction longer before the policy is applied again.
1088 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1090 If even is not set, instead of having symmetrical UZs,
1091 the left and bottom UZs are extended up to right and top UZs respectively.
1092 This way, we favour the displaying of useful information: the beginning of lines,
1093 where most code reside, and the lines after the caret, eg. the body of a function.
1095 | | | | |
1096 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1097 | | | | | visibility or going into the UZ) display is...
1098 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1099 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1100 0 | 0 | 0 | 1 | Yes | moved by one position
1101 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1102 0 | 0 | 1 | 1 | Yes | centred on the caret
1103 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1104 0 | 1 | - | 1 | No, caret is always centred | -
1105 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1106 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1107 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1108 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1109 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1110 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1111 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1114 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange &range, const XYScrollOptions options) {
1115 PRectangle rcClient = GetTextRectangle();
1116 Point pt = LocationFromPosition(range.caret);
1117 Point ptAnchor = LocationFromPosition(range.anchor);
1118 const Point ptOrigin = GetVisibleOriginInMain();
1119 pt.x += ptOrigin.x;
1120 pt.y += ptOrigin.y;
1121 ptAnchor.x += ptOrigin.x;
1122 ptAnchor.y += ptOrigin.y;
1123 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1125 XYScrollPosition newXY(xOffset, topLine);
1126 if (rcClient.Empty()) {
1127 return newXY;
1130 // Vertical positioning
1131 if ((options & xysVertical) && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1132 const int lineCaret = DisplayFromPosition(range.caret.Position());
1133 const int linesOnScreen = LinesOnScreen();
1134 const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1135 const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
1136 const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
1137 const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
1138 const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
1140 // It should be possible to scroll the window to show the caret,
1141 // but this fails to remove the caret on GTK+
1142 if (bSlop) { // A margin is defined
1143 int yMoveT, yMoveB;
1144 if (bStrict) {
1145 int yMarginT, yMarginB;
1146 if (!(options & xysUseMargin)) {
1147 // In drag mode, avoid moves
1148 // otherwise, a double click will select several lines.
1149 yMarginT = yMarginB = 0;
1150 } else {
1151 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1152 // a maximum of slightly less than half the heigth of the text area.
1153 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1154 if (bEven) {
1155 yMarginB = yMarginT;
1156 } else {
1157 yMarginB = linesOnScreen - yMarginT - 1;
1160 yMoveT = yMarginT;
1161 if (bEven) {
1162 if (bJump) {
1163 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1165 yMoveB = yMoveT;
1166 } else {
1167 yMoveB = linesOnScreen - yMoveT - 1;
1169 if (lineCaret < topLine + yMarginT) {
1170 // Caret goes too high
1171 newXY.topLine = lineCaret - yMoveT;
1172 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1173 // Caret goes too low
1174 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1176 } else { // Not strict
1177 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1178 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1179 if (bEven) {
1180 yMoveB = yMoveT;
1181 } else {
1182 yMoveB = linesOnScreen - yMoveT - 1;
1184 if (lineCaret < topLine) {
1185 // Caret goes too high
1186 newXY.topLine = lineCaret - yMoveT;
1187 } else if (lineCaret > topLine + linesOnScreen - 1) {
1188 // Caret goes too low
1189 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1192 } else { // No slop
1193 if (!bStrict && !bJump) {
1194 // Minimal move
1195 if (lineCaret < topLine) {
1196 // Caret goes too high
1197 newXY.topLine = lineCaret;
1198 } else if (lineCaret > topLine + linesOnScreen - 1) {
1199 // Caret goes too low
1200 if (bEven) {
1201 newXY.topLine = lineCaret - linesOnScreen + 1;
1202 } else {
1203 newXY.topLine = lineCaret;
1206 } else { // Strict or going out of display
1207 if (bEven) {
1208 // Always center caret
1209 newXY.topLine = lineCaret - halfScreen;
1210 } else {
1211 // Always put caret on top of display
1212 newXY.topLine = lineCaret;
1216 if (!(range.caret == range.anchor)) {
1217 const int lineAnchor = DisplayFromPosition(range.anchor.Position());
1218 if (lineAnchor < lineCaret) {
1219 // Shift up to show anchor or as much of range as possible
1220 newXY.topLine = std::min(newXY.topLine, lineAnchor);
1221 newXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());
1222 } else {
1223 // Shift down to show anchor or as much of range as possible
1224 newXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());
1225 newXY.topLine = std::min(newXY.topLine, lineCaret);
1228 newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
1231 // Horizontal positioning
1232 if ((options & xysHorizontal) && !Wrapping()) {
1233 const int halfScreen = Platform::Maximum(static_cast<int>(rcClient.Width()) - 4, 4) / 2;
1234 const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
1235 const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
1236 const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
1237 const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
1239 if (bSlop) { // A margin is defined
1240 int xMoveL, xMoveR;
1241 if (bStrict) {
1242 int xMarginL, xMarginR;
1243 if (!(options & xysUseMargin)) {
1244 // In drag mode, avoid moves unless very near of the margin
1245 // otherwise, a simple click will select text.
1246 xMarginL = xMarginR = 2;
1247 } else {
1248 // xMargin must equal to caretXSlop, with a minimum of 2 and
1249 // a maximum of slightly less than half the width of the text area.
1250 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1251 if (bEven) {
1252 xMarginL = xMarginR;
1253 } else {
1254 xMarginL = static_cast<int>(rcClient.Width()) - xMarginR - 4;
1257 if (bJump && bEven) {
1258 // Jump is used only in even mode
1259 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1260 } else {
1261 xMoveL = xMoveR = 0; // Not used, avoid a warning
1263 if (pt.x < rcClient.left + xMarginL) {
1264 // Caret is on the left of the display
1265 if (bJump && bEven) {
1266 newXY.xOffset -= xMoveL;
1267 } else {
1268 // Move just enough to allow to display the caret
1269 newXY.xOffset -= static_cast<int>((rcClient.left + xMarginL) - pt.x);
1271 } else if (pt.x >= rcClient.right - xMarginR) {
1272 // Caret is on the right of the display
1273 if (bJump && bEven) {
1274 newXY.xOffset += xMoveR;
1275 } else {
1276 // Move just enough to allow to display the caret
1277 newXY.xOffset += static_cast<int>(pt.x - (rcClient.right - xMarginR) + 1);
1280 } else { // Not strict
1281 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1282 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1283 if (bEven) {
1284 xMoveL = xMoveR;
1285 } else {
1286 xMoveL = static_cast<int>(rcClient.Width()) - xMoveR - 4;
1288 if (pt.x < rcClient.left) {
1289 // Caret is on the left of the display
1290 newXY.xOffset -= xMoveL;
1291 } else if (pt.x >= rcClient.right) {
1292 // Caret is on the right of the display
1293 newXY.xOffset += xMoveR;
1296 } else { // No slop
1297 if (bStrict ||
1298 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1299 // Strict or going out of display
1300 if (bEven) {
1301 // Center caret
1302 newXY.xOffset += static_cast<int>(pt.x - rcClient.left - halfScreen);
1303 } else {
1304 // Put caret on right
1305 newXY.xOffset += static_cast<int>(pt.x - rcClient.right + 1);
1307 } else {
1308 // Move just enough to allow to display the caret
1309 if (pt.x < rcClient.left) {
1310 // Caret is on the left of the display
1311 if (bEven) {
1312 newXY.xOffset -= static_cast<int>(rcClient.left - pt.x);
1313 } else {
1314 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1316 } else if (pt.x >= rcClient.right) {
1317 // Caret is on the right of the display
1318 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1322 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1323 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1324 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 2;
1325 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1326 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 2;
1327 if ((vs.caretStyle == CARETSTYLE_BLOCK) || view.imeCaretBlockOverride) {
1328 // Ensure we can see a good portion of the block caret
1329 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1332 if (!(range.caret == range.anchor)) {
1333 if (ptAnchor.x < pt.x) {
1334 // Shift to left to show anchor or as much of range as possible
1335 int maxOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.left) - 1;
1336 int minOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 1;
1337 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1338 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1339 } else {
1340 // Shift to right to show anchor or as much of range as possible
1341 int minOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.right) + 1;
1342 int maxOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 1;
1343 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1344 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1347 if (newXY.xOffset < 0) {
1348 newXY.xOffset = 0;
1352 return newXY;
1355 void Editor::SetXYScroll(XYScrollPosition newXY) {
1356 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1357 if (newXY.topLine != topLine) {
1358 SetTopLine(newXY.topLine);
1359 SetVerticalScrollPos();
1361 if (newXY.xOffset != xOffset) {
1362 xOffset = newXY.xOffset;
1363 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1364 if (newXY.xOffset > 0) {
1365 PRectangle rcText = GetTextRectangle();
1366 if (horizontalScrollBarVisible &&
1367 rcText.Width() + xOffset > scrollWidth) {
1368 scrollWidth = xOffset + static_cast<int>(rcText.Width());
1369 SetScrollBars();
1372 SetHorizontalScrollPos();
1374 Redraw();
1375 UpdateSystemCaret();
1379 void Editor::ScrollRange(SelectionRange range) {
1380 SetXYScroll(XYScrollToMakeVisible(range, xysDefault));
1383 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1384 SetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret),
1385 static_cast<XYScrollOptions>((useMargin?xysUseMargin:0)|(vert?xysVertical:0)|(horiz?xysHorizontal:0))));
1388 void Editor::ShowCaretAtCurrentPosition() {
1389 if (hasFocus) {
1390 caret.active = true;
1391 caret.on = true;
1392 if (FineTickerAvailable()) {
1393 FineTickerCancel(tickCaret);
1394 if (caret.period > 0)
1395 FineTickerStart(tickCaret, caret.period, caret.period/10);
1396 } else {
1397 SetTicking(true);
1399 } else {
1400 caret.active = false;
1401 caret.on = false;
1402 if (FineTickerAvailable()) {
1403 FineTickerCancel(tickCaret);
1406 InvalidateCaret();
1409 void Editor::DropCaret() {
1410 caret.active = false;
1411 if (FineTickerAvailable()) {
1412 FineTickerCancel(tickCaret);
1414 InvalidateCaret();
1417 void Editor::CaretSetPeriod(int period) {
1418 if (caret.period != period) {
1419 caret.period = period;
1420 caret.on = true;
1421 if (FineTickerAvailable()) {
1422 FineTickerCancel(tickCaret);
1423 if ((caret.active) && (caret.period > 0))
1424 FineTickerStart(tickCaret, caret.period, caret.period/10);
1426 InvalidateCaret();
1430 void Editor::InvalidateCaret() {
1431 if (posDrag.IsValid()) {
1432 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1433 } else {
1434 for (size_t r=0; r<sel.Count(); r++) {
1435 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1438 UpdateSystemCaret();
1441 void Editor::NotifyCaretMove() {
1444 void Editor::UpdateSystemCaret() {
1447 bool Editor::Wrapping() const {
1448 return vs.wrapState != eWrapNone;
1451 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1452 //Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd);
1453 if (wrapPending.AddRange(docLineStart, docLineEnd)) {
1454 view.llc.Invalidate(LineLayout::llPositions);
1456 // Wrap lines during idle.
1457 if (Wrapping() && wrapPending.NeedsWrap()) {
1458 SetIdle(true);
1462 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1463 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(lineToWrap, *this));
1464 int linesWrapped = 1;
1465 if (ll) {
1466 view.LayoutLine(*this, lineToWrap, surface, vs, ll, wrapWidth);
1467 linesWrapped = ll->lines;
1469 return cs.SetHeight(lineToWrap, linesWrapped +
1470 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1473 // Perform wrapping for a subset of the lines needing wrapping.
1474 // wsAll: wrap all lines which need wrapping in this single call
1475 // wsVisible: wrap currently visible lines
1476 // wsIdle: wrap one page + 100 lines
1477 // Return true if wrapping occurred.
1478 bool Editor::WrapLines(enum wrapScope ws) {
1479 int goodTopLine = topLine;
1480 bool wrapOccurred = false;
1481 if (!Wrapping()) {
1482 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1483 wrapWidth = LineLayout::wrapWidthInfinite;
1484 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1485 cs.SetHeight(lineDoc, 1 +
1486 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1488 wrapOccurred = true;
1490 wrapPending.Reset();
1492 } else if (wrapPending.NeedsWrap()) {
1493 wrapPending.start = std::min(wrapPending.start, pdoc->LinesTotal());
1494 if (!SetIdle(true)) {
1495 // Idle processing not supported so full wrap required.
1496 ws = wsAll;
1498 // Decide where to start wrapping
1499 int lineToWrap = wrapPending.start;
1500 int lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal());
1501 const int lineDocTop = cs.DocFromDisplay(topLine);
1502 const int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1503 if (ws == wsVisible) {
1504 lineToWrap = Platform::Clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal());
1505 // Priority wrap to just after visible area.
1506 // Since wrapping could reduce display lines, treat each
1507 // as taking only one display line.
1508 lineToWrapEnd = lineDocTop;
1509 int lines = LinesOnScreen() + 1;
1510 while ((lineToWrapEnd < cs.LinesInDoc()) && (lines>0)) {
1511 if (cs.GetVisible(lineToWrapEnd))
1512 lines--;
1513 lineToWrapEnd++;
1515 // .. and if the paint window is outside pending wraps
1516 if ((lineToWrap > wrapPending.end) || (lineToWrapEnd < wrapPending.start)) {
1517 // Currently visible text does not need wrapping
1518 return false;
1520 } else if (ws == wsIdle) {
1521 lineToWrapEnd = lineToWrap + LinesOnScreen() + 100;
1523 const int lineEndNeedWrap = std::min(wrapPending.end, pdoc->LinesTotal());
1524 lineToWrapEnd = std::min(lineToWrapEnd, lineEndNeedWrap);
1526 // Ensure all lines being wrapped are styled.
1527 pdoc->EnsureStyledTo(pdoc->LineStart(lineToWrapEnd));
1529 if (lineToWrap < lineToWrapEnd) {
1531 PRectangle rcTextArea = GetClientRectangle();
1532 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1533 rcTextArea.right -= vs.rightMarginWidth;
1534 wrapWidth = static_cast<int>(rcTextArea.Width());
1535 RefreshStyleData();
1536 AutoSurface surface(this);
1537 if (surface) {
1538 //Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
1540 while (lineToWrap < lineToWrapEnd) {
1541 if (WrapOneLine(surface, lineToWrap)) {
1542 wrapOccurred = true;
1544 wrapPending.Wrapped(lineToWrap);
1545 lineToWrap++;
1548 goodTopLine = cs.DisplayFromDoc(lineDocTop) + std::min(subLineTop, cs.GetHeight(lineDocTop)-1);
1552 // If wrapping is done, bring it to resting position
1553 if (wrapPending.start >= lineEndNeedWrap) {
1554 wrapPending.Reset();
1558 if (wrapOccurred) {
1559 SetScrollBars();
1560 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1561 SetVerticalScrollPos();
1564 return wrapOccurred;
1567 void Editor::LinesJoin() {
1568 if (!RangeContainsProtected(targetStart, targetEnd)) {
1569 UndoGroup ug(pdoc);
1570 bool prevNonWS = true;
1571 for (int pos = targetStart; pos < targetEnd; pos++) {
1572 if (pdoc->IsPositionInLineEnd(pos)) {
1573 targetEnd -= pdoc->LenChar(pos);
1574 pdoc->DelChar(pos);
1575 if (prevNonWS) {
1576 // Ensure at least one space separating previous lines
1577 const int lengthInserted = pdoc->InsertString(pos, " ", 1);
1578 targetEnd += lengthInserted;
1580 } else {
1581 prevNonWS = pdoc->CharAt(pos) != ' ';
1587 const char *Editor::StringFromEOLMode(int eolMode) {
1588 if (eolMode == SC_EOL_CRLF) {
1589 return "\r\n";
1590 } else if (eolMode == SC_EOL_CR) {
1591 return "\r";
1592 } else {
1593 return "\n";
1597 void Editor::LinesSplit(int pixelWidth) {
1598 if (!RangeContainsProtected(targetStart, targetEnd)) {
1599 if (pixelWidth == 0) {
1600 PRectangle rcText = GetTextRectangle();
1601 pixelWidth = static_cast<int>(rcText.Width());
1603 int lineStart = pdoc->LineFromPosition(targetStart);
1604 int lineEnd = pdoc->LineFromPosition(targetEnd);
1605 const char *eol = StringFromEOLMode(pdoc->eolMode);
1606 UndoGroup ug(pdoc);
1607 for (int line = lineStart; line <= lineEnd; line++) {
1608 AutoSurface surface(this);
1609 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
1610 if (surface && ll) {
1611 unsigned int posLineStart = pdoc->LineStart(line);
1612 view.LayoutLine(*this, line, surface, vs, ll, pixelWidth);
1613 int lengthInsertedTotal = 0;
1614 for (int subLine = 1; subLine < ll->lines; subLine++) {
1615 const int lengthInserted = pdoc->InsertString(
1616 static_cast<int>(posLineStart + lengthInsertedTotal +
1617 ll->LineStart(subLine)),
1618 eol, istrlen(eol));
1619 targetEnd += lengthInserted;
1620 lengthInsertedTotal += lengthInserted;
1623 lineEnd = pdoc->LineFromPosition(targetEnd);
1628 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1629 if (vs.fixedColumnWidth == 0)
1630 return;
1632 AllocateGraphics();
1633 RefreshStyleData();
1634 RefreshPixMaps(surfWindow);
1636 // On GTK+ with Ubuntu overlay scroll bars, the surface may have been finished
1637 // at this point. The Initialised call checks for this case and sets the status
1638 // to be bad which avoids crashes in following calls.
1639 if (!surfWindow->Initialised()) {
1640 return;
1643 PRectangle rcMargin = GetClientRectangle();
1644 Point ptOrigin = GetVisibleOriginInMain();
1645 rcMargin.Move(0, -ptOrigin.y);
1646 rcMargin.left = 0;
1647 rcMargin.right = static_cast<XYPOSITION>(vs.fixedColumnWidth);
1649 if (!rc.Intersects(rcMargin))
1650 return;
1652 Surface *surface;
1653 if (view.bufferedDraw) {
1654 surface = marginView.pixmapSelMargin;
1655 } else {
1656 surface = surfWindow;
1659 // Clip vertically to paint area to avoid drawing line numbers
1660 if (rcMargin.bottom > rc.bottom)
1661 rcMargin.bottom = rc.bottom;
1662 if (rcMargin.top < rc.top)
1663 rcMargin.top = rc.top;
1665 marginView.PaintMargin(surface, topLine, rc, rcMargin, *this, vs);
1667 if (view.bufferedDraw) {
1668 surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *marginView.pixmapSelMargin);
1672 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
1673 view.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1674 marginView.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1675 if (view.bufferedDraw) {
1676 PRectangle rcClient = GetClientRectangle();
1677 if (!view.pixmapLine->Initialised()) {
1679 view.pixmapLine->InitPixMap(static_cast<int>(rcClient.Width()), vs.lineHeight,
1680 surfaceWindow, wMain.GetID());
1682 if (!marginView.pixmapSelMargin->Initialised()) {
1683 marginView.pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
1684 static_cast<int>(rcClient.Height()), surfaceWindow, wMain.GetID());
1689 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
1690 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
1691 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
1692 AllocateGraphics();
1694 RefreshStyleData();
1695 if (paintState == paintAbandoned)
1696 return; // Scroll bars may have changed so need redraw
1697 RefreshPixMaps(surfaceWindow);
1699 paintAbandonedByStyling = false;
1701 StyleAreaBounded(rcArea, false);
1703 PRectangle rcClient = GetClientRectangle();
1704 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
1705 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1707 if (NotifyUpdateUI()) {
1708 RefreshStyleData();
1709 RefreshPixMaps(surfaceWindow);
1712 // Wrap the visible lines if needed.
1713 if (WrapLines(wsVisible)) {
1714 // The wrapping process has changed the height of some lines so
1715 // abandon this paint for a complete repaint.
1716 if (AbandonPaint()) {
1717 return;
1719 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
1721 PLATFORM_ASSERT(marginView.pixmapSelPattern->Initialised());
1723 if (!view.bufferedDraw)
1724 surfaceWindow->SetClip(rcArea);
1726 if (paintState != paintAbandoned) {
1727 if (vs.marginInside) {
1728 PaintSelMargin(surfaceWindow, rcArea);
1729 PRectangle rcRightMargin = rcClient;
1730 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
1731 if (rcArea.Intersects(rcRightMargin)) {
1732 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
1734 } else { // Else separate view so separate paint event but leftMargin included to allow overlap
1735 PRectangle rcLeftMargin = rcArea;
1736 rcLeftMargin.left = 0;
1737 rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
1738 if (rcArea.Intersects(rcLeftMargin)) {
1739 surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[STYLE_DEFAULT].back);
1744 if (paintState == paintAbandoned) {
1745 // Either styling or NotifyUpdateUI noticed that painting is needed
1746 // outside the current painting rectangle
1747 //Platform::DebugPrintf("Abandoning paint\n");
1748 if (Wrapping()) {
1749 if (paintAbandonedByStyling) {
1750 // Styling has spilled over a line end, such as occurs by starting a multiline
1751 // comment. The width of subsequent text may have changed, so rewrap.
1752 NeedWrapping(cs.DocFromDisplay(topLine));
1755 return;
1758 view.PaintText(surfaceWindow, *this, rcArea, rcClient, vs);
1760 if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) {
1761 if (FineTickerAvailable()) {
1762 scrollWidth = view.lineWidthMaxSeen;
1763 if (!FineTickerRunning(tickWiden)) {
1764 FineTickerStart(tickWiden, 50, 5);
1769 NotifyPainted();
1772 // This is mostly copied from the Paint method but with some things omitted
1773 // such as the margin markers, line numbers, selection and caret
1774 // Should be merged back into a combined Draw method.
1775 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
1776 if (!pfr)
1777 return 0;
1779 AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
1780 if (!surface)
1781 return 0;
1782 AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
1783 if (!surfaceMeasure) {
1784 return 0;
1786 return view.FormatRange(draw, pfr, surface, surfaceMeasure, *this, vs);
1789 int Editor::TextWidth(int style, const char *text) {
1790 RefreshStyleData();
1791 AutoSurface surface(this);
1792 if (surface) {
1793 return static_cast<int>(surface->WidthText(vs.styles[style].font, text, istrlen(text)));
1794 } else {
1795 return 1;
1799 // Empty method is overridden on GTK+ to show / hide scrollbars
1800 void Editor::ReconfigureScrollBars() {}
1802 void Editor::SetScrollBars() {
1803 RefreshStyleData();
1805 int nMax = MaxScrollPos();
1806 int nPage = LinesOnScreen();
1807 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
1808 if (modified) {
1809 DwellEnd(true);
1812 // TODO: ensure always showing as many lines as possible
1813 // May not be, if, for example, window made larger
1814 if (topLine > MaxScrollPos()) {
1815 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
1816 SetVerticalScrollPos();
1817 Redraw();
1819 if (modified) {
1820 if (!AbandonPaint())
1821 Redraw();
1823 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
1826 void Editor::ChangeSize() {
1827 DropGraphics(false);
1828 SetScrollBars();
1829 if (Wrapping()) {
1830 PRectangle rcTextArea = GetClientRectangle();
1831 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1832 rcTextArea.right -= vs.rightMarginWidth;
1833 if (wrapWidth != rcTextArea.Width()) {
1834 NeedWrapping();
1835 Redraw();
1840 int Editor::RealizeVirtualSpace(int position, unsigned int virtualSpace) {
1841 if (virtualSpace > 0) {
1842 const int line = pdoc->LineFromPosition(position);
1843 const int indent = pdoc->GetLineIndentPosition(line);
1844 if (indent == position) {
1845 return pdoc->SetLineIndentation(line, pdoc->GetLineIndentation(line) + virtualSpace);
1846 } else {
1847 std::string spaceText(virtualSpace, ' ');
1848 const int lengthInserted = pdoc->InsertString(position, spaceText.c_str(), virtualSpace);
1849 position += lengthInserted;
1852 return position;
1855 SelectionPosition Editor::RealizeVirtualSpace(const SelectionPosition &position) {
1856 // Return the new position with no virtual space
1857 return SelectionPosition(RealizeVirtualSpace(position.Position(), position.VirtualSpace()));
1860 void Editor::AddChar(char ch) {
1861 char s[2];
1862 s[0] = ch;
1863 s[1] = '\0';
1864 AddCharUTF(s, 1);
1867 void Editor::FilterSelections() {
1868 if (!additionalSelectionTyping && (sel.Count() > 1)) {
1869 InvalidateWholeSelection();
1870 sel.DropAdditionalRanges();
1874 static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
1875 return *a < *b;
1878 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
1879 void Editor::AddCharUTF(const char *s, unsigned int len, bool treatAsDBCS) {
1880 FilterSelections();
1882 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
1884 // Vector elements point into selection in order to change selection.
1885 std::vector<SelectionRange *> selPtrs;
1886 for (size_t r = 0; r < sel.Count(); r++) {
1887 selPtrs.push_back(&sel.Range(r));
1889 // Order selections by position in document.
1890 std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
1892 // Loop in reverse to avoid disturbing positions of selections yet to be processed.
1893 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
1894 rit != selPtrs.rend(); ++rit) {
1895 SelectionRange *currentSel = *rit;
1896 if (!RangeContainsProtected(currentSel->Start().Position(),
1897 currentSel->End().Position())) {
1898 int positionInsert = currentSel->Start().Position();
1899 if (!currentSel->Empty()) {
1900 if (currentSel->Length()) {
1901 pdoc->DeleteChars(positionInsert, currentSel->Length());
1902 currentSel->ClearVirtualSpace();
1903 } else {
1904 // Range is all virtual so collapse to start of virtual space
1905 currentSel->MinimizeVirtualSpace();
1907 } else if (inOverstrike) {
1908 if (positionInsert < pdoc->Length()) {
1909 if (!pdoc->IsPositionInLineEnd(positionInsert)) {
1910 pdoc->DelChar(positionInsert);
1911 currentSel->ClearVirtualSpace();
1915 positionInsert = RealizeVirtualSpace(positionInsert, currentSel->caret.VirtualSpace());
1916 const int lengthInserted = pdoc->InsertString(positionInsert, s, len);
1917 if (lengthInserted > 0) {
1918 currentSel->caret.SetPosition(positionInsert + lengthInserted);
1919 currentSel->anchor.SetPosition(positionInsert + lengthInserted);
1921 currentSel->ClearVirtualSpace();
1922 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1923 if (Wrapping()) {
1924 AutoSurface surface(this);
1925 if (surface) {
1926 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
1927 SetScrollBars();
1928 SetVerticalScrollPos();
1929 Redraw();
1936 if (Wrapping()) {
1937 SetScrollBars();
1939 ThinRectangularRange();
1940 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1941 EnsureCaretVisible();
1942 // Avoid blinking during rapid typing:
1943 ShowCaretAtCurrentPosition();
1944 if ((caretSticky == SC_CARETSTICKY_OFF) ||
1945 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
1946 SetLastXChosen();
1949 if (treatAsDBCS) {
1950 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
1951 static_cast<unsigned char>(s[1]));
1952 } else if (len > 0) {
1953 int byte = static_cast<unsigned char>(s[0]);
1954 if ((byte < 0xC0) || (1 == len)) {
1955 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
1956 // characters when not in UTF-8 mode.
1957 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
1958 // characters representing themselves.
1959 } else {
1960 unsigned int utf32[1] = { 0 };
1961 UTF32FromUTF8(s, len, utf32, ELEMENTS(utf32));
1962 byte = utf32[0];
1964 NotifyChar(byte);
1967 if (recordingMacro) {
1968 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
1972 void Editor::ClearBeforeTentativeStart() {
1973 // Make positions for the first composition string.
1974 FilterSelections();
1975 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
1976 for (size_t r = 0; r<sel.Count(); r++) {
1977 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
1978 sel.Range(r).End().Position())) {
1979 int positionInsert = sel.Range(r).Start().Position();
1980 if (!sel.Range(r).Empty()) {
1981 if (sel.Range(r).Length()) {
1982 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
1983 sel.Range(r).ClearVirtualSpace();
1984 } else {
1985 // Range is all virtual so collapse to start of virtual space
1986 sel.Range(r).MinimizeVirtualSpace();
1989 RealizeVirtualSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
1990 sel.Range(r).ClearVirtualSpace();
1995 void Editor::InsertPaste(const char *text, int len) {
1996 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
1997 SelectionPosition selStart = sel.Start();
1998 selStart = RealizeVirtualSpace(selStart);
1999 const int lengthInserted = pdoc->InsertString(selStart.Position(), text, len);
2000 if (lengthInserted > 0) {
2001 SetEmptySelection(selStart.Position() + lengthInserted);
2003 } else {
2004 // SC_MULTIPASTE_EACH
2005 for (size_t r=0; r<sel.Count(); r++) {
2006 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
2007 sel.Range(r).End().Position())) {
2008 int positionInsert = sel.Range(r).Start().Position();
2009 if (!sel.Range(r).Empty()) {
2010 if (sel.Range(r).Length()) {
2011 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
2012 sel.Range(r).ClearVirtualSpace();
2013 } else {
2014 // Range is all virtual so collapse to start of virtual space
2015 sel.Range(r).MinimizeVirtualSpace();
2018 positionInsert = RealizeVirtualSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
2019 const int lengthInserted = pdoc->InsertString(positionInsert, text, len);
2020 if (lengthInserted > 0) {
2021 sel.Range(r).caret.SetPosition(positionInsert + lengthInserted);
2022 sel.Range(r).anchor.SetPosition(positionInsert + lengthInserted);
2024 sel.Range(r).ClearVirtualSpace();
2030 void Editor::InsertPasteShape(const char *text, int len, PasteShape shape) {
2031 std::string convertedText;
2032 if (convertPastes) {
2033 // Convert line endings of the paste into our local line-endings mode
2034 convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
2035 len = static_cast<int>(convertedText.length());
2036 text = convertedText.c_str();
2038 if (shape == pasteRectangular) {
2039 PasteRectangular(sel.Start(), text, len);
2040 } else {
2041 if (shape == pasteLine) {
2042 int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
2043 int lengthInserted = pdoc->InsertString(insertPos, text, len);
2044 // add the newline if necessary
2045 if ((len > 0) && (text[len - 1] != '\n' && text[len - 1] != '\r')) {
2046 const char *endline = StringFromEOLMode(pdoc->eolMode);
2047 int length = static_cast<int>(strlen(endline));
2048 lengthInserted += pdoc->InsertString(insertPos + lengthInserted, endline, length);
2050 if (sel.MainCaret() == insertPos) {
2051 SetEmptySelection(sel.MainCaret() + lengthInserted);
2053 } else {
2054 InsertPaste(text, len);
2059 void Editor::ClearSelection(bool retainMultipleSelections) {
2060 if (!sel.IsRectangular() && !retainMultipleSelections)
2061 FilterSelections();
2062 UndoGroup ug(pdoc);
2063 for (size_t r=0; r<sel.Count(); r++) {
2064 if (!sel.Range(r).Empty()) {
2065 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
2066 sel.Range(r).End().Position())) {
2067 pdoc->DeleteChars(sel.Range(r).Start().Position(),
2068 sel.Range(r).Length());
2069 sel.Range(r) = SelectionRange(sel.Range(r).Start());
2073 ThinRectangularRange();
2074 sel.RemoveDuplicates();
2075 ClaimSelection();
2076 SetHoverIndicatorPosition(sel.MainCaret());
2079 void Editor::ClearAll() {
2081 UndoGroup ug(pdoc);
2082 if (0 != pdoc->Length()) {
2083 pdoc->DeleteChars(0, pdoc->Length());
2085 if (!pdoc->IsReadOnly()) {
2086 cs.Clear();
2087 pdoc->AnnotationClearAll();
2088 pdoc->MarginClearAll();
2092 view.ClearAllTabstops();
2094 sel.Clear();
2095 SetTopLine(0);
2096 SetVerticalScrollPos();
2097 InvalidateStyleRedraw();
2100 void Editor::ClearDocumentStyle() {
2101 Decoration *deco = pdoc->decorations.root;
2102 while (deco) {
2103 // Save next in case deco deleted
2104 Decoration *decoNext = deco->next;
2105 if (deco->indicator < INDIC_CONTAINER) {
2106 pdoc->decorations.SetCurrentIndicator(deco->indicator);
2107 pdoc->DecorationFillRange(0, 0, pdoc->Length());
2109 deco = decoNext;
2111 pdoc->StartStyling(0, '\377');
2112 pdoc->SetStyleFor(pdoc->Length(), 0);
2113 cs.ShowAll();
2114 SetAnnotationHeights(0, pdoc->LinesTotal());
2115 pdoc->ClearLevels();
2118 void Editor::CopyAllowLine() {
2119 SelectionText selectedText;
2120 CopySelectionRange(&selectedText, true);
2121 CopyToClipboard(selectedText);
2124 void Editor::Cut() {
2125 pdoc->CheckReadOnly();
2126 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
2127 Copy();
2128 ClearSelection();
2132 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
2133 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
2134 return;
2136 sel.Clear();
2137 sel.RangeMain() = SelectionRange(pos);
2138 int line = pdoc->LineFromPosition(sel.MainCaret());
2139 UndoGroup ug(pdoc);
2140 sel.RangeMain().caret = RealizeVirtualSpace(sel.RangeMain().caret);
2141 int xInsert = XFromPosition(sel.RangeMain().caret);
2142 bool prevCr = false;
2143 while ((len > 0) && IsEOLChar(ptr[len-1]))
2144 len--;
2145 for (int i = 0; i < len; i++) {
2146 if (IsEOLChar(ptr[i])) {
2147 if ((ptr[i] == '\r') || (!prevCr))
2148 line++;
2149 if (line >= pdoc->LinesTotal()) {
2150 if (pdoc->eolMode != SC_EOL_LF)
2151 pdoc->InsertString(pdoc->Length(), "\r", 1);
2152 if (pdoc->eolMode != SC_EOL_CR)
2153 pdoc->InsertString(pdoc->Length(), "\n", 1);
2155 // Pad the end of lines with spaces if required
2156 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
2157 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
2158 while (XFromPosition(sel.MainCaret()) < xInsert) {
2159 assert(pdoc);
2160 const int lengthInserted = pdoc->InsertString(sel.MainCaret(), " ", 1);
2161 sel.RangeMain().caret.Add(lengthInserted);
2164 prevCr = ptr[i] == '\r';
2165 } else {
2166 const int lengthInserted = pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
2167 sel.RangeMain().caret.Add(lengthInserted);
2168 prevCr = false;
2171 SetEmptySelection(pos);
2174 bool Editor::CanPaste() {
2175 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
2178 void Editor::Clear() {
2179 // If multiple selections, don't delete EOLS
2180 if (sel.Empty()) {
2181 bool singleVirtual = false;
2182 if ((sel.Count() == 1) &&
2183 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
2184 sel.RangeMain().Start().VirtualSpace()) {
2185 singleVirtual = true;
2187 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
2188 for (size_t r=0; r<sel.Count(); r++) {
2189 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
2190 if (sel.Range(r).Start().VirtualSpace()) {
2191 if (sel.Range(r).anchor < sel.Range(r).caret)
2192 sel.Range(r) = SelectionRange(RealizeVirtualSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
2193 else
2194 sel.Range(r) = SelectionRange(RealizeVirtualSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
2196 if ((sel.Count() == 1) || !pdoc->IsPositionInLineEnd(sel.Range(r).caret.Position())) {
2197 pdoc->DelChar(sel.Range(r).caret.Position());
2198 sel.Range(r).ClearVirtualSpace();
2199 } // else multiple selection so don't eat line ends
2200 } else {
2201 sel.Range(r).ClearVirtualSpace();
2204 } else {
2205 ClearSelection();
2207 sel.RemoveDuplicates();
2208 ShowCaretAtCurrentPosition(); // Avoid blinking
2211 void Editor::SelectAll() {
2212 sel.Clear();
2213 SetSelection(0, pdoc->Length());
2214 Redraw();
2217 void Editor::Undo() {
2218 if (pdoc->CanUndo()) {
2219 InvalidateCaret();
2220 int newPos = pdoc->Undo();
2221 if (newPos >= 0)
2222 SetEmptySelection(newPos);
2223 EnsureCaretVisible();
2227 void Editor::Redo() {
2228 if (pdoc->CanRedo()) {
2229 int newPos = pdoc->Redo();
2230 if (newPos >= 0)
2231 SetEmptySelection(newPos);
2232 EnsureCaretVisible();
2236 void Editor::DelCharBack(bool allowLineStartDeletion) {
2237 RefreshStyleData();
2238 if (!sel.IsRectangular())
2239 FilterSelections();
2240 if (sel.IsRectangular())
2241 allowLineStartDeletion = false;
2242 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
2243 if (sel.Empty()) {
2244 for (size_t r=0; r<sel.Count(); r++) {
2245 if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
2246 if (sel.Range(r).caret.VirtualSpace()) {
2247 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
2248 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
2249 } else {
2250 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
2251 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
2252 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
2253 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
2254 UndoGroup ugInner(pdoc, !ug.Needed());
2255 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
2256 int indentationStep = pdoc->IndentSize();
2257 int indentationChange = indentation % indentationStep;
2258 if (indentationChange == 0)
2259 indentationChange = indentationStep;
2260 const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationChange);
2261 // SetEmptySelection
2262 sel.Range(r) = SelectionRange(posSelect);
2263 } else {
2264 pdoc->DelCharBack(sel.Range(r).caret.Position());
2268 } else {
2269 sel.Range(r).ClearVirtualSpace();
2272 ThinRectangularRange();
2273 } else {
2274 ClearSelection();
2276 sel.RemoveDuplicates();
2277 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
2278 // Avoid blinking during rapid typing:
2279 ShowCaretAtCurrentPosition();
2282 int Editor::ModifierFlags(bool shift, bool ctrl, bool alt, bool meta, bool super) {
2283 return
2284 (shift ? SCI_SHIFT : 0) |
2285 (ctrl ? SCI_CTRL : 0) |
2286 (alt ? SCI_ALT : 0) |
2287 (meta ? SCI_META : 0) |
2288 (super ? SCI_SUPER : 0);
2291 void Editor::NotifyFocus(bool focus) {
2292 SCNotification scn = {};
2293 scn.nmhdr.code = focus ? SCN_FOCUSIN : SCN_FOCUSOUT;
2294 NotifyParent(scn);
2297 void Editor::SetCtrlID(int identifier) {
2298 ctrlID = identifier;
2301 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
2302 SCNotification scn = {};
2303 scn.nmhdr.code = SCN_STYLENEEDED;
2304 scn.position = endStyleNeeded;
2305 NotifyParent(scn);
2308 void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
2309 NotifyStyleToNeeded(endStyleNeeded);
2312 void Editor::NotifyLexerChanged(Document *, void *) {
2315 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
2316 errorStatus = status;
2319 void Editor::NotifyChar(int ch) {
2320 SCNotification scn = {};
2321 scn.nmhdr.code = SCN_CHARADDED;
2322 scn.ch = ch;
2323 NotifyParent(scn);
2326 void Editor::NotifySavePoint(bool isSavePoint) {
2327 SCNotification scn = {};
2328 if (isSavePoint) {
2329 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
2330 } else {
2331 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
2333 NotifyParent(scn);
2336 void Editor::NotifyModifyAttempt() {
2337 SCNotification scn = {};
2338 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
2339 NotifyParent(scn);
2342 void Editor::NotifyDoubleClick(Point pt, int modifiers) {
2343 SCNotification scn = {};
2344 scn.nmhdr.code = SCN_DOUBLECLICK;
2345 scn.line = LineFromLocation(pt);
2346 scn.position = PositionFromLocation(pt, true);
2347 scn.modifiers = modifiers;
2348 NotifyParent(scn);
2351 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
2352 NotifyDoubleClick(pt, ModifierFlags(shift, ctrl, alt));
2355 void Editor::NotifyHotSpotDoubleClicked(int position, int modifiers) {
2356 SCNotification scn = {};
2357 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
2358 scn.position = position;
2359 scn.modifiers = modifiers;
2360 NotifyParent(scn);
2363 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
2364 NotifyHotSpotDoubleClicked(position, ModifierFlags(shift, ctrl, alt));
2367 void Editor::NotifyHotSpotClicked(int position, int modifiers) {
2368 SCNotification scn = {};
2369 scn.nmhdr.code = SCN_HOTSPOTCLICK;
2370 scn.position = position;
2371 scn.modifiers = modifiers;
2372 NotifyParent(scn);
2375 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
2376 NotifyHotSpotClicked(position, ModifierFlags(shift, ctrl, alt));
2379 void Editor::NotifyHotSpotReleaseClick(int position, int modifiers) {
2380 SCNotification scn = {};
2381 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
2382 scn.position = position;
2383 scn.modifiers = modifiers;
2384 NotifyParent(scn);
2387 void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
2388 NotifyHotSpotReleaseClick(position, ModifierFlags(shift, ctrl, alt));
2391 bool Editor::NotifyUpdateUI() {
2392 if (needUpdateUI) {
2393 SCNotification scn = {};
2394 scn.nmhdr.code = SCN_UPDATEUI;
2395 scn.updated = needUpdateUI;
2396 NotifyParent(scn);
2397 needUpdateUI = 0;
2398 return true;
2400 return false;
2403 void Editor::NotifyPainted() {
2404 SCNotification scn = {};
2405 scn.nmhdr.code = SCN_PAINTED;
2406 NotifyParent(scn);
2409 void Editor::NotifyIndicatorClick(bool click, int position, int modifiers) {
2410 int mask = pdoc->decorations.AllOnFor(position);
2411 if ((click && mask) || pdoc->decorations.clickNotified) {
2412 SCNotification scn = {};
2413 pdoc->decorations.clickNotified = click;
2414 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
2415 scn.modifiers = modifiers;
2416 scn.position = position;
2417 NotifyParent(scn);
2421 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
2422 NotifyIndicatorClick(click, position, ModifierFlags(shift, ctrl, alt));
2425 bool Editor::NotifyMarginClick(Point pt, int modifiers) {
2426 int marginClicked = -1;
2427 int x = vs.textStart - vs.fixedColumnWidth;
2428 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
2429 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
2430 marginClicked = margin;
2431 x += vs.ms[margin].width;
2433 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
2434 int position = pdoc->LineStart(LineFromLocation(pt));
2435 if ((vs.ms[marginClicked].mask & SC_MASK_FOLDERS) && (foldAutomatic & SC_AUTOMATICFOLD_CLICK)) {
2436 const bool ctrl = (modifiers & SCI_CTRL) != 0;
2437 const bool shift = (modifiers & SCI_SHIFT) != 0;
2438 int lineClick = pdoc->LineFromPosition(position);
2439 if (shift && ctrl) {
2440 FoldAll(SC_FOLDACTION_TOGGLE);
2441 } else {
2442 int levelClick = pdoc->GetLevel(lineClick);
2443 if (levelClick & SC_FOLDLEVELHEADERFLAG) {
2444 if (shift) {
2445 // Ensure all children visible
2446 FoldExpand(lineClick, SC_FOLDACTION_EXPAND, levelClick);
2447 } else if (ctrl) {
2448 FoldExpand(lineClick, SC_FOLDACTION_TOGGLE, levelClick);
2449 } else {
2450 // Toggle this line
2451 FoldLine(lineClick, SC_FOLDACTION_TOGGLE);
2455 return true;
2457 SCNotification scn = {};
2458 scn.nmhdr.code = SCN_MARGINCLICK;
2459 scn.modifiers = modifiers;
2460 scn.position = position;
2461 scn.margin = marginClicked;
2462 NotifyParent(scn);
2463 return true;
2464 } else {
2465 return false;
2469 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
2470 return NotifyMarginClick(pt, ModifierFlags(shift, ctrl, alt));
2473 void Editor::NotifyNeedShown(int pos, int len) {
2474 SCNotification scn = {};
2475 scn.nmhdr.code = SCN_NEEDSHOWN;
2476 scn.position = pos;
2477 scn.length = len;
2478 NotifyParent(scn);
2481 void Editor::NotifyDwelling(Point pt, bool state) {
2482 SCNotification scn = {};
2483 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
2484 scn.position = PositionFromLocation(pt, true);
2485 scn.x = static_cast<int>(pt.x + vs.ExternalMarginWidth());
2486 scn.y = static_cast<int>(pt.y);
2487 NotifyParent(scn);
2490 void Editor::NotifyZoom() {
2491 SCNotification scn = {};
2492 scn.nmhdr.code = SCN_ZOOM;
2493 NotifyParent(scn);
2496 // Notifications from document
2497 void Editor::NotifyModifyAttempt(Document *, void *) {
2498 //Platform::DebugPrintf("** Modify Attempt\n");
2499 NotifyModifyAttempt();
2502 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
2503 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
2504 NotifySavePoint(atSavePoint);
2507 void Editor::CheckModificationForWrap(DocModification mh) {
2508 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
2509 view.llc.Invalidate(LineLayout::llCheckTextAndStyle);
2510 int lineDoc = pdoc->LineFromPosition(mh.position);
2511 int lines = Platform::Maximum(0, mh.linesAdded);
2512 if (Wrapping()) {
2513 NeedWrapping(lineDoc, lineDoc + lines + 1);
2515 RefreshStyleData();
2516 // Fix up annotation heights
2517 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
2521 // Move a position so it is still after the same character as before the insertion.
2522 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
2523 if (position > startInsertion) {
2524 return position + length;
2526 return position;
2529 // Move a position so it is still after the same character as before the deletion if that
2530 // character is still present else after the previous surviving character.
2531 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
2532 if (position > startDeletion) {
2533 int endDeletion = startDeletion + length;
2534 if (position > endDeletion) {
2535 return position - length;
2536 } else {
2537 return startDeletion;
2539 } else {
2540 return position;
2544 void Editor::NotifyModified(Document *, DocModification mh, void *) {
2545 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
2546 if (paintState == painting) {
2547 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
2549 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
2550 if (paintState == painting) {
2551 CheckForChangeOutsidePaint(
2552 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
2553 } else {
2554 // Could check that change is before last visible line.
2555 Redraw();
2558 if (mh.modificationType & SC_MOD_CHANGETABSTOPS) {
2559 Redraw();
2561 if (mh.modificationType & SC_MOD_LEXERSTATE) {
2562 if (paintState == painting) {
2563 CheckForChangeOutsidePaint(
2564 Range(mh.position, mh.position + mh.length));
2565 } else {
2566 Redraw();
2569 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
2570 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
2571 pdoc->IncrementStyleClock();
2573 if (paintState == notPainting) {
2574 if (mh.position < pdoc->LineStart(topLine)) {
2575 // Styling performed before this view
2576 Redraw();
2577 } else {
2578 InvalidateRange(mh.position, mh.position + mh.length);
2581 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
2582 view.llc.Invalidate(LineLayout::llCheckTextAndStyle);
2584 } else {
2585 // Move selection and brace highlights
2586 if (mh.modificationType & SC_MOD_INSERTTEXT) {
2587 sel.MovePositions(true, mh.position, mh.length);
2588 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
2589 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
2590 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
2591 sel.MovePositions(false, mh.position, mh.length);
2592 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
2593 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
2595 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && cs.HiddenLines()) {
2596 // Some lines are hidden so may need shown.
2597 const int lineOfPos = pdoc->LineFromPosition(mh.position);
2598 int endNeedShown = mh.position;
2599 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
2600 if (pdoc->ContainsLineEnd(mh.text, mh.length) && (mh.position != pdoc->LineStart(lineOfPos)))
2601 endNeedShown = pdoc->LineStart(lineOfPos+1);
2602 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
2603 // Extend the need shown area over any folded lines
2604 endNeedShown = mh.position + mh.length;
2605 int lineLast = pdoc->LineFromPosition(mh.position+mh.length);
2606 for (int line = lineOfPos; line <= lineLast; line++) {
2607 const int lineMaxSubord = pdoc->GetLastChild(line, -1, -1);
2608 if (lineLast < lineMaxSubord) {
2609 lineLast = lineMaxSubord;
2610 endNeedShown = pdoc->LineEnd(lineLast);
2614 NeedShown(mh.position, endNeedShown - mh.position);
2616 if (mh.linesAdded != 0) {
2617 // Update contraction state for inserted and removed lines
2618 // lineOfPos should be calculated in context of state before modification, shouldn't it
2619 int lineOfPos = pdoc->LineFromPosition(mh.position);
2620 if (mh.position > pdoc->LineStart(lineOfPos))
2621 lineOfPos++; // Affecting subsequent lines
2622 if (mh.linesAdded > 0) {
2623 cs.InsertLines(lineOfPos, mh.linesAdded);
2624 } else {
2625 cs.DeleteLines(lineOfPos, -mh.linesAdded);
2627 view.LinesAddedOrRemoved(lineOfPos, mh.linesAdded);
2629 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
2630 int lineDoc = pdoc->LineFromPosition(mh.position);
2631 if (vs.annotationVisible) {
2632 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
2633 Redraw();
2636 CheckModificationForWrap(mh);
2637 if (mh.linesAdded != 0) {
2638 // Avoid scrolling of display if change before current display
2639 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
2640 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
2641 if (newTop != topLine) {
2642 SetTopLine(newTop);
2643 SetVerticalScrollPos();
2647 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
2648 QueueIdleWork(WorkNeeded::workStyle, pdoc->Length());
2649 Redraw();
2651 } else {
2652 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
2653 QueueIdleWork(WorkNeeded::workStyle, mh.position + mh.length);
2654 InvalidateRange(mh.position, mh.position + mh.length);
2659 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
2660 SetScrollBars();
2663 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
2664 if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) {
2665 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
2666 // Fold changes can affect the drawing of following lines so redraw whole margin
2667 RedrawSelMargin(marginView.highlightDelimiter.isEnabled ? -1 : mh.line - 1, true);
2668 } else {
2669 RedrawSelMargin(mh.line);
2673 if ((mh.modificationType & SC_MOD_CHANGEFOLD) && (foldAutomatic & SC_AUTOMATICFOLD_CHANGE)) {
2674 FoldChanged(mh.line, mh.foldLevelNow, mh.foldLevelPrev);
2677 // NOW pay the piper WRT "deferred" visual updates
2678 if (IsLastStep(mh)) {
2679 SetScrollBars();
2680 Redraw();
2683 // If client wants to see this modification
2684 if (mh.modificationType & modEventMask) {
2685 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
2686 // Real modification made to text of document.
2687 NotifyChange(); // Send EN_CHANGE
2690 SCNotification scn = {};
2691 scn.nmhdr.code = SCN_MODIFIED;
2692 scn.position = mh.position;
2693 scn.modificationType = mh.modificationType;
2694 scn.text = mh.text;
2695 scn.length = mh.length;
2696 scn.linesAdded = mh.linesAdded;
2697 scn.line = mh.line;
2698 scn.foldLevelNow = mh.foldLevelNow;
2699 scn.foldLevelPrev = mh.foldLevelPrev;
2700 scn.token = mh.token;
2701 scn.annotationLinesAdded = mh.annotationLinesAdded;
2702 NotifyParent(scn);
2706 void Editor::NotifyDeleted(Document *, void *) {
2707 /* Do nothing */
2710 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2712 // Enumerates all macroable messages
2713 switch (iMessage) {
2714 case SCI_CUT:
2715 case SCI_COPY:
2716 case SCI_PASTE:
2717 case SCI_CLEAR:
2718 case SCI_REPLACESEL:
2719 case SCI_ADDTEXT:
2720 case SCI_INSERTTEXT:
2721 case SCI_APPENDTEXT:
2722 case SCI_CLEARALL:
2723 case SCI_SELECTALL:
2724 case SCI_GOTOLINE:
2725 case SCI_GOTOPOS:
2726 case SCI_SEARCHANCHOR:
2727 case SCI_SEARCHNEXT:
2728 case SCI_SEARCHPREV:
2729 case SCI_LINEDOWN:
2730 case SCI_LINEDOWNEXTEND:
2731 case SCI_PARADOWN:
2732 case SCI_PARADOWNEXTEND:
2733 case SCI_LINEUP:
2734 case SCI_LINEUPEXTEND:
2735 case SCI_PARAUP:
2736 case SCI_PARAUPEXTEND:
2737 case SCI_CHARLEFT:
2738 case SCI_CHARLEFTEXTEND:
2739 case SCI_CHARRIGHT:
2740 case SCI_CHARRIGHTEXTEND:
2741 case SCI_WORDLEFT:
2742 case SCI_WORDLEFTEXTEND:
2743 case SCI_WORDRIGHT:
2744 case SCI_WORDRIGHTEXTEND:
2745 case SCI_WORDPARTLEFT:
2746 case SCI_WORDPARTLEFTEXTEND:
2747 case SCI_WORDPARTRIGHT:
2748 case SCI_WORDPARTRIGHTEXTEND:
2749 case SCI_WORDLEFTEND:
2750 case SCI_WORDLEFTENDEXTEND:
2751 case SCI_WORDRIGHTEND:
2752 case SCI_WORDRIGHTENDEXTEND:
2753 case SCI_HOME:
2754 case SCI_HOMEEXTEND:
2755 case SCI_LINEEND:
2756 case SCI_LINEENDEXTEND:
2757 case SCI_HOMEWRAP:
2758 case SCI_HOMEWRAPEXTEND:
2759 case SCI_LINEENDWRAP:
2760 case SCI_LINEENDWRAPEXTEND:
2761 case SCI_DOCUMENTSTART:
2762 case SCI_DOCUMENTSTARTEXTEND:
2763 case SCI_DOCUMENTEND:
2764 case SCI_DOCUMENTENDEXTEND:
2765 case SCI_STUTTEREDPAGEUP:
2766 case SCI_STUTTEREDPAGEUPEXTEND:
2767 case SCI_STUTTEREDPAGEDOWN:
2768 case SCI_STUTTEREDPAGEDOWNEXTEND:
2769 case SCI_PAGEUP:
2770 case SCI_PAGEUPEXTEND:
2771 case SCI_PAGEDOWN:
2772 case SCI_PAGEDOWNEXTEND:
2773 case SCI_EDITTOGGLEOVERTYPE:
2774 case SCI_CANCEL:
2775 case SCI_DELETEBACK:
2776 case SCI_TAB:
2777 case SCI_BACKTAB:
2778 case SCI_FORMFEED:
2779 case SCI_VCHOME:
2780 case SCI_VCHOMEEXTEND:
2781 case SCI_VCHOMEWRAP:
2782 case SCI_VCHOMEWRAPEXTEND:
2783 case SCI_VCHOMEDISPLAY:
2784 case SCI_VCHOMEDISPLAYEXTEND:
2785 case SCI_DELWORDLEFT:
2786 case SCI_DELWORDRIGHT:
2787 case SCI_DELWORDRIGHTEND:
2788 case SCI_DELLINELEFT:
2789 case SCI_DELLINERIGHT:
2790 case SCI_LINECOPY:
2791 case SCI_LINECUT:
2792 case SCI_LINEDELETE:
2793 case SCI_LINETRANSPOSE:
2794 case SCI_LINEDUPLICATE:
2795 case SCI_LOWERCASE:
2796 case SCI_UPPERCASE:
2797 case SCI_LINESCROLLDOWN:
2798 case SCI_LINESCROLLUP:
2799 case SCI_DELETEBACKNOTLINE:
2800 case SCI_HOMEDISPLAY:
2801 case SCI_HOMEDISPLAYEXTEND:
2802 case SCI_LINEENDDISPLAY:
2803 case SCI_LINEENDDISPLAYEXTEND:
2804 case SCI_SETSELECTIONMODE:
2805 case SCI_LINEDOWNRECTEXTEND:
2806 case SCI_LINEUPRECTEXTEND:
2807 case SCI_CHARLEFTRECTEXTEND:
2808 case SCI_CHARRIGHTRECTEXTEND:
2809 case SCI_HOMERECTEXTEND:
2810 case SCI_VCHOMERECTEXTEND:
2811 case SCI_LINEENDRECTEXTEND:
2812 case SCI_PAGEUPRECTEXTEND:
2813 case SCI_PAGEDOWNRECTEXTEND:
2814 case SCI_SELECTIONDUPLICATE:
2815 case SCI_COPYALLOWLINE:
2816 case SCI_VERTICALCENTRECARET:
2817 case SCI_MOVESELECTEDLINESUP:
2818 case SCI_MOVESELECTEDLINESDOWN:
2819 case SCI_SCROLLTOSTART:
2820 case SCI_SCROLLTOEND:
2821 break;
2823 // Filter out all others like display changes. Also, newlines are redundant
2824 // with char insert messages.
2825 case SCI_NEWLINE:
2826 default:
2827 // printf("Filtered out %ld of macro recording\n", iMessage);
2828 return;
2831 // Send notification
2832 SCNotification scn = {};
2833 scn.nmhdr.code = SCN_MACRORECORD;
2834 scn.message = iMessage;
2835 scn.wParam = wParam;
2836 scn.lParam = lParam;
2837 NotifyParent(scn);
2840 // Something has changed that the container should know about
2841 void Editor::ContainerNeedsUpdate(int flags) {
2842 needUpdateUI |= flags;
2846 * Force scroll and keep position relative to top of window.
2848 * If stuttered = true and not already at first/last row, move to first/last row of window.
2849 * If stuttered = true and already at first/last row, scroll as normal.
2851 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
2852 int topLineNew;
2853 SelectionPosition newPos;
2855 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
2856 int topStutterLine = topLine + caretYSlop;
2857 int bottomStutterLine =
2858 pdoc->LineFromPosition(PositionFromLocation(
2859 Point::FromInts(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
2860 - caretYSlop - 1;
2862 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
2863 topLineNew = topLine;
2864 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
2865 false, false, UserVirtualSpace());
2867 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
2868 topLineNew = topLine;
2869 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
2870 false, false, UserVirtualSpace());
2872 } else {
2873 Point pt = LocationFromPosition(sel.MainCaret());
2875 topLineNew = Platform::Clamp(
2876 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
2877 newPos = SPositionFromLocation(
2878 Point::FromInts(lastXChosen - xOffset, static_cast<int>(pt.y) + direction * (vs.lineHeight * LinesToScroll())),
2879 false, false, UserVirtualSpace());
2882 if (topLineNew != topLine) {
2883 SetTopLine(topLineNew);
2884 MovePositionTo(newPos, selt);
2885 Redraw();
2886 SetVerticalScrollPos();
2887 } else {
2888 MovePositionTo(newPos, selt);
2892 void Editor::ChangeCaseOfSelection(int caseMapping) {
2893 UndoGroup ug(pdoc);
2894 for (size_t r=0; r<sel.Count(); r++) {
2895 SelectionRange current = sel.Range(r);
2896 SelectionRange currentNoVS = current;
2897 currentNoVS.ClearVirtualSpace();
2898 size_t rangeBytes = currentNoVS.Length();
2899 if (rangeBytes > 0) {
2900 std::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position());
2902 std::string sMapped = CaseMapString(sText, caseMapping);
2904 if (sMapped != sText) {
2905 size_t firstDifference = 0;
2906 while (sMapped[firstDifference] == sText[firstDifference])
2907 firstDifference++;
2908 size_t lastDifferenceText = sText.size() - 1;
2909 size_t lastDifferenceMapped = sMapped.size() - 1;
2910 while (sMapped[lastDifferenceMapped] == sText[lastDifferenceText]) {
2911 lastDifferenceText--;
2912 lastDifferenceMapped--;
2914 size_t endDifferenceText = sText.size() - 1 - lastDifferenceText;
2915 pdoc->DeleteChars(
2916 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
2917 static_cast<int>(rangeBytes - firstDifference - endDifferenceText));
2918 const int lengthChange = static_cast<int>(lastDifferenceMapped - firstDifference + 1);
2919 const int lengthInserted = pdoc->InsertString(
2920 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
2921 sMapped.c_str() + firstDifference,
2922 lengthChange);
2923 // Automatic movement changes selection so reset to exactly the same as it was.
2924 int diffSizes = static_cast<int>(sMapped.size() - sText.size()) + lengthInserted - lengthChange;
2925 if (diffSizes != 0) {
2926 if (current.anchor > current.caret)
2927 current.anchor.Add(diffSizes);
2928 else
2929 current.caret.Add(diffSizes);
2931 sel.Range(r) = current;
2937 void Editor::LineTranspose() {
2938 int line = pdoc->LineFromPosition(sel.MainCaret());
2939 if (line > 0) {
2940 UndoGroup ug(pdoc);
2942 const int startPrevious = pdoc->LineStart(line - 1);
2943 const std::string linePrevious = RangeText(startPrevious, pdoc->LineEnd(line - 1));
2945 int startCurrent = pdoc->LineStart(line);
2946 const std::string lineCurrent = RangeText(startCurrent, pdoc->LineEnd(line));
2948 pdoc->DeleteChars(startCurrent, static_cast<int>(lineCurrent.length()));
2949 pdoc->DeleteChars(startPrevious, static_cast<int>(linePrevious.length()));
2950 startCurrent -= static_cast<int>(linePrevious.length());
2952 startCurrent += pdoc->InsertString(startPrevious, lineCurrent.c_str(),
2953 static_cast<int>(lineCurrent.length()));
2954 pdoc->InsertString(startCurrent, linePrevious.c_str(),
2955 static_cast<int>(linePrevious.length()));
2956 // Move caret to start of current line
2957 MovePositionTo(SelectionPosition(startCurrent));
2961 void Editor::Duplicate(bool forLine) {
2962 if (sel.Empty()) {
2963 forLine = true;
2965 UndoGroup ug(pdoc);
2966 const char *eol = "";
2967 int eolLen = 0;
2968 if (forLine) {
2969 eol = StringFromEOLMode(pdoc->eolMode);
2970 eolLen = istrlen(eol);
2972 for (size_t r=0; r<sel.Count(); r++) {
2973 SelectionPosition start = sel.Range(r).Start();
2974 SelectionPosition end = sel.Range(r).End();
2975 if (forLine) {
2976 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
2977 start = SelectionPosition(pdoc->LineStart(line));
2978 end = SelectionPosition(pdoc->LineEnd(line));
2980 std::string text = RangeText(start.Position(), end.Position());
2981 int lengthInserted = eolLen;
2982 if (forLine)
2983 lengthInserted = pdoc->InsertString(end.Position(), eol, eolLen);
2984 pdoc->InsertString(end.Position() + lengthInserted, text.c_str(), static_cast<int>(text.length()));
2986 if (sel.Count() && sel.IsRectangular()) {
2987 SelectionPosition last = sel.Last();
2988 if (forLine) {
2989 int line = pdoc->LineFromPosition(last.Position());
2990 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
2992 if (sel.Rectangular().anchor > sel.Rectangular().caret)
2993 sel.Rectangular().anchor = last;
2994 else
2995 sel.Rectangular().caret = last;
2996 SetRectangularRange();
3000 void Editor::CancelModes() {
3001 sel.SetMoveExtends(false);
3004 void Editor::NewLine() {
3005 InvalidateWholeSelection();
3006 if (sel.IsRectangular() || !additionalSelectionTyping) {
3007 // Remove non-main ranges
3008 sel.DropAdditionalRanges();
3011 UndoGroup ug(pdoc, !sel.Empty() || (sel.Count() > 1));
3013 // Clear each range
3014 if (!sel.Empty()) {
3015 ClearSelection();
3018 // Insert each line end
3019 size_t countInsertions = 0;
3020 for (size_t r = 0; r < sel.Count(); r++) {
3021 sel.Range(r).ClearVirtualSpace();
3022 const char *eol = StringFromEOLMode(pdoc->eolMode);
3023 const int positionInsert = sel.Range(r).caret.Position();
3024 const int insertLength = pdoc->InsertString(positionInsert, eol, istrlen(eol));
3025 if (insertLength > 0) {
3026 sel.Range(r) = SelectionRange(positionInsert + insertLength);
3027 countInsertions++;
3031 // Perform notifications after all the changes as the application may change the
3032 // selections in response to the characters.
3033 for (size_t i = 0; i < countInsertions; i++) {
3034 const char *eol = StringFromEOLMode(pdoc->eolMode);
3035 while (*eol) {
3036 NotifyChar(*eol);
3037 if (recordingMacro) {
3038 char txt[2];
3039 txt[0] = *eol;
3040 txt[1] = '\0';
3041 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
3043 eol++;
3047 SetLastXChosen();
3048 SetScrollBars();
3049 EnsureCaretVisible();
3050 // Avoid blinking during rapid typing:
3051 ShowCaretAtCurrentPosition();
3054 SelectionPosition Editor::PositionUpOrDown(SelectionPosition spStart, int direction, int lastX) {
3055 const Point pt = LocationFromPosition(spStart);
3056 int skipLines = 0;
3058 if (vs.annotationVisible) {
3059 const int lineDoc = pdoc->LineFromPosition(spStart.Position());
3060 const Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
3061 const int subLine = static_cast<int>(pt.y - ptStartLine.y) / vs.lineHeight;
3063 if (direction < 0 && subLine == 0) {
3064 const int lineDisplay = cs.DisplayFromDoc(lineDoc);
3065 if (lineDisplay > 0) {
3066 skipLines = pdoc->AnnotationLines(cs.DocFromDisplay(lineDisplay - 1));
3068 } else if (direction > 0 && subLine >= (cs.GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
3069 skipLines = pdoc->AnnotationLines(lineDoc);
3073 const int newY = static_cast<int>(pt.y) + (1 + skipLines) * direction * vs.lineHeight;
3074 if (lastX < 0) {
3075 lastX = static_cast<int>(pt.x) + xOffset;
3077 SelectionPosition posNew = SPositionFromLocation(
3078 Point::FromInts(lastX - xOffset, newY), false, false, UserVirtualSpace());
3080 if (direction < 0) {
3081 // Line wrapping may lead to a location on the same line, so
3082 // seek back if that is the case.
3083 Point ptNew = LocationFromPosition(posNew.Position());
3084 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
3085 posNew.Add(-1);
3086 posNew.SetVirtualSpace(0);
3087 ptNew = LocationFromPosition(posNew.Position());
3089 } else if (direction > 0 && posNew.Position() != pdoc->Length()) {
3090 // There is an equivalent case when moving down which skips
3091 // over a line.
3092 Point ptNew = LocationFromPosition(posNew.Position());
3093 while ((posNew.Position() > spStart.Position()) && (ptNew.y > newY)) {
3094 posNew.Add(-1);
3095 posNew.SetVirtualSpace(0);
3096 ptNew = LocationFromPosition(posNew.Position());
3099 return posNew;
3102 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
3103 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
3104 if (sel.IsRectangular()) {
3105 if (selt == Selection::noSel) {
3106 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
3107 } else {
3108 caretToUse = sel.Rectangular().caret;
3111 if (selt == Selection::selRectangle) {
3112 const SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();
3113 if (!sel.IsRectangular()) {
3114 InvalidateWholeSelection();
3115 sel.DropAdditionalRanges();
3117 const SelectionPosition posNew = MovePositionSoVisible(
3118 PositionUpOrDown(caretToUse, direction, lastXChosen), direction);
3119 sel.selType = Selection::selRectangle;
3120 sel.Rectangular() = SelectionRange(posNew, rangeBase.anchor);
3121 SetRectangularRange();
3122 MovedCaret(posNew, caretToUse, true);
3123 } else {
3124 InvalidateWholeSelection();
3125 if (!additionalSelectionTyping || (sel.IsRectangular())) {
3126 sel.DropAdditionalRanges();
3128 sel.selType = Selection::selStream;
3129 for (size_t r = 0; r < sel.Count(); r++) {
3130 const int lastX = (r == sel.Main()) ? lastXChosen : -1;
3131 const SelectionPosition spCaretNow = sel.Range(r).caret;
3132 const SelectionPosition posNew = MovePositionSoVisible(
3133 PositionUpOrDown(spCaretNow, direction, lastX), direction);
3134 sel.Range(r) = selt == Selection::selStream ?
3135 SelectionRange(posNew, sel.Range(r).anchor) : SelectionRange(posNew);
3137 sel.RemoveDuplicates();
3138 MovedCaret(sel.RangeMain().caret, caretToUse, true);
3142 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
3143 int lineDoc, savedPos = sel.MainCaret();
3144 do {
3145 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
3146 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
3147 if (direction > 0) {
3148 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
3149 if (selt == Selection::noSel) {
3150 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
3152 break;
3155 } while (!cs.GetVisible(lineDoc));
3158 int Editor::StartEndDisplayLine(int pos, bool start) {
3159 RefreshStyleData();
3160 AutoSurface surface(this);
3161 int posRet = view.StartEndDisplayLine(surface, *this, pos, start, vs);
3162 if (posRet == INVALID_POSITION) {
3163 return pos;
3164 } else {
3165 return posRet;
3169 namespace {
3171 unsigned int WithExtends(unsigned int iMessage) {
3172 switch (iMessage) {
3173 case SCI_CHARLEFT: return SCI_CHARLEFTEXTEND;
3174 case SCI_CHARRIGHT: return SCI_CHARRIGHTEXTEND;
3176 case SCI_WORDLEFT: return SCI_WORDLEFTEXTEND;
3177 case SCI_WORDRIGHT: return SCI_WORDRIGHTEXTEND;
3178 case SCI_WORDLEFTEND: return SCI_WORDLEFTENDEXTEND;
3179 case SCI_WORDRIGHTEND: return SCI_WORDRIGHTENDEXTEND;
3180 case SCI_WORDPARTLEFT: return SCI_WORDPARTLEFTEXTEND;
3181 case SCI_WORDPARTRIGHT: return SCI_WORDPARTRIGHTEXTEND;
3183 case SCI_HOME: return SCI_HOMEEXTEND;
3184 case SCI_HOMEDISPLAY: return SCI_HOMEDISPLAYEXTEND;
3185 case SCI_HOMEWRAP: return SCI_HOMEWRAPEXTEND;
3186 case SCI_VCHOME: return SCI_VCHOMEEXTEND;
3187 case SCI_VCHOMEDISPLAY: return SCI_VCHOMEDISPLAYEXTEND;
3188 case SCI_VCHOMEWRAP: return SCI_VCHOMEWRAPEXTEND;
3190 case SCI_LINEEND: return SCI_LINEENDEXTEND;
3191 case SCI_LINEENDDISPLAY: return SCI_LINEENDDISPLAYEXTEND;
3192 case SCI_LINEENDWRAP: return SCI_LINEENDWRAPEXTEND;
3194 default: return iMessage;
3198 int NaturalDirection(unsigned int iMessage) {
3199 switch (iMessage) {
3200 case SCI_CHARLEFT:
3201 case SCI_CHARLEFTEXTEND:
3202 case SCI_CHARLEFTRECTEXTEND:
3203 case SCI_WORDLEFT:
3204 case SCI_WORDLEFTEXTEND:
3205 case SCI_WORDLEFTEND:
3206 case SCI_WORDLEFTENDEXTEND:
3207 case SCI_WORDPARTLEFT:
3208 case SCI_WORDPARTLEFTEXTEND:
3209 case SCI_HOME:
3210 case SCI_HOMEEXTEND:
3211 case SCI_HOMEDISPLAY:
3212 case SCI_HOMEDISPLAYEXTEND:
3213 case SCI_HOMEWRAP:
3214 case SCI_HOMEWRAPEXTEND:
3215 // VC_HOME* mostly goes back
3216 case SCI_VCHOME:
3217 case SCI_VCHOMEEXTEND:
3218 case SCI_VCHOMEDISPLAY:
3219 case SCI_VCHOMEDISPLAYEXTEND:
3220 case SCI_VCHOMEWRAP:
3221 case SCI_VCHOMEWRAPEXTEND:
3222 return -1;
3224 default:
3225 return 1;
3229 bool IsRectExtend(unsigned int iMessage) {
3230 switch (iMessage) {
3231 case SCI_CHARLEFTRECTEXTEND:
3232 case SCI_CHARRIGHTRECTEXTEND:
3233 case SCI_HOMERECTEXTEND:
3234 case SCI_VCHOMERECTEXTEND:
3235 case SCI_LINEENDRECTEXTEND:
3236 return true;
3237 default:
3238 return false;
3244 int Editor::VCHomeDisplayPosition(int position) {
3245 const int homePos = pdoc->VCHomePosition(position);
3246 const int viewLineStart = StartEndDisplayLine(position, true);
3247 if (viewLineStart > homePos)
3248 return viewLineStart;
3249 else
3250 return homePos;
3253 int Editor::VCHomeWrapPosition(int position) {
3254 const int homePos = pdoc->VCHomePosition(position);
3255 const int viewLineStart = StartEndDisplayLine(position, true);
3256 if ((viewLineStart < position) && (viewLineStart > homePos))
3257 return viewLineStart;
3258 else
3259 return homePos;
3262 int Editor::LineEndWrapPosition(int position) {
3263 const int endPos = StartEndDisplayLine(position, false);
3264 const int realEndPos = pdoc->LineEndPosition(position);
3265 if (endPos > realEndPos // if moved past visible EOLs
3266 || position >= endPos) // if at end of display line already
3267 return realEndPos;
3268 else
3269 return endPos;
3272 int Editor::HorizontalMove(unsigned int iMessage) {
3273 if (sel.MoveExtends()) {
3274 iMessage = WithExtends(iMessage);
3277 if (!multipleSelection && !sel.IsRectangular()) {
3278 // Simplify selection down to 1
3279 sel.SetSelection(sel.RangeMain());
3282 // Invalidate each of the current selections
3283 InvalidateWholeSelection();
3285 if (IsRectExtend(iMessage)) {
3286 const SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();
3287 if (!sel.IsRectangular()) {
3288 sel.DropAdditionalRanges();
3290 // Will change to rectangular if not currently rectangular
3291 SelectionPosition spCaret = rangeBase.caret;
3292 switch (iMessage) {
3293 case SCI_CHARLEFTRECTEXTEND:
3294 if (pdoc->IsLineEndPosition(spCaret.Position()) && spCaret.VirtualSpace()) {
3295 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3296 } else if ((virtualSpaceOptions & SCVS_NOWRAPLINESTART) == 0 || pdoc->GetColumn(spCaret.Position()) > 0) {
3297 spCaret = SelectionPosition(spCaret.Position() - 1);
3299 break;
3300 case SCI_CHARRIGHTRECTEXTEND:
3301 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
3302 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3303 } else {
3304 spCaret = SelectionPosition(spCaret.Position() + 1);
3306 break;
3307 case SCI_HOMERECTEXTEND:
3308 spCaret = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3309 break;
3310 case SCI_VCHOMERECTEXTEND:
3311 spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
3312 break;
3313 case SCI_LINEENDRECTEXTEND:
3314 spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3315 break;
3317 const int directionMove = (spCaret < rangeBase.caret) ? -1 : 1;
3318 spCaret = MovePositionSoVisible(spCaret, directionMove);
3319 sel.selType = Selection::selRectangle;
3320 sel.Rectangular() = SelectionRange(spCaret, rangeBase.anchor);
3321 SetRectangularRange();
3322 } else {
3323 if (sel.IsRectangular()) {
3324 // Not a rectangular extension so switch to stream.
3325 SelectionPosition selAtLimit = (NaturalDirection(iMessage) > 0) ? sel.Limits().end : sel.Limits().start;
3326 sel.selType = Selection::selStream;
3327 sel.SetSelection(SelectionRange(selAtLimit));
3329 if (!additionalSelectionTyping) {
3330 InvalidateWholeSelection();
3331 sel.DropAdditionalRanges();
3333 for (size_t r = 0; r < sel.Count(); r++) {
3334 const SelectionPosition spCaretNow = sel.Range(r).caret;
3335 SelectionPosition spCaret = spCaretNow;
3336 switch (iMessage) {
3337 case SCI_CHARLEFT:
3338 case SCI_CHARLEFTEXTEND:
3339 if (spCaret.VirtualSpace()) {
3340 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3341 } else if ((virtualSpaceOptions & SCVS_NOWRAPLINESTART) == 0 || pdoc->GetColumn(spCaret.Position()) > 0) {
3342 spCaret = SelectionPosition(spCaret.Position() - 1);
3344 break;
3345 case SCI_CHARRIGHT:
3346 case SCI_CHARRIGHTEXTEND:
3347 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(spCaret.Position())) {
3348 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3349 } else {
3350 spCaret = SelectionPosition(spCaret.Position() + 1);
3352 break;
3353 case SCI_WORDLEFT:
3354 case SCI_WORDLEFTEXTEND:
3355 spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), -1));
3356 break;
3357 case SCI_WORDRIGHT:
3358 case SCI_WORDRIGHTEXTEND:
3359 spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), 1));
3360 break;
3361 case SCI_WORDLEFTEND:
3362 case SCI_WORDLEFTENDEXTEND:
3363 spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), -1));
3364 break;
3365 case SCI_WORDRIGHTEND:
3366 case SCI_WORDRIGHTENDEXTEND:
3367 spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), 1));
3368 break;
3369 case SCI_WORDPARTLEFT:
3370 case SCI_WORDPARTLEFTEXTEND:
3371 spCaret = SelectionPosition(pdoc->WordPartLeft(spCaret.Position()));
3372 break;
3373 case SCI_WORDPARTRIGHT:
3374 case SCI_WORDPARTRIGHTEXTEND:
3375 spCaret = SelectionPosition(pdoc->WordPartRight(spCaret.Position()));
3376 break;
3377 case SCI_HOME:
3378 case SCI_HOMEEXTEND:
3379 spCaret = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3380 break;
3381 case SCI_HOMEDISPLAY:
3382 case SCI_HOMEDISPLAYEXTEND:
3383 spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), true));
3384 break;
3385 case SCI_HOMEWRAP:
3386 case SCI_HOMEWRAPEXTEND:
3387 spCaret = MovePositionSoVisible(StartEndDisplayLine(spCaret.Position(), true), -1);
3388 if (spCaretNow <= spCaret)
3389 spCaret = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3390 break;
3391 case SCI_VCHOME:
3392 case SCI_VCHOMEEXTEND:
3393 // VCHome alternates between beginning of line and beginning of text so may move back or forwards
3394 spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
3395 break;
3396 case SCI_VCHOMEDISPLAY:
3397 case SCI_VCHOMEDISPLAYEXTEND:
3398 spCaret = SelectionPosition(VCHomeDisplayPosition(spCaret.Position()));
3399 break;
3400 case SCI_VCHOMEWRAP:
3401 case SCI_VCHOMEWRAPEXTEND:
3402 spCaret = SelectionPosition(VCHomeWrapPosition(spCaret.Position()));
3403 break;
3404 case SCI_LINEEND:
3405 case SCI_LINEENDEXTEND:
3406 spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3407 break;
3408 case SCI_LINEENDDISPLAY:
3409 case SCI_LINEENDDISPLAYEXTEND:
3410 spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), false));
3411 break;
3412 case SCI_LINEENDWRAP:
3413 case SCI_LINEENDWRAPEXTEND:
3414 spCaret = SelectionPosition(LineEndWrapPosition(spCaret.Position()));
3415 break;
3417 default:
3418 PLATFORM_ASSERT(false);
3421 const int directionMove = (spCaret < spCaretNow) ? -1 : 1;
3422 spCaret = MovePositionSoVisible(spCaret, directionMove);
3424 // Handle move versus extend, and special behaviour for non-emoty left/right
3425 switch (iMessage) {
3426 case SCI_CHARLEFT:
3427 case SCI_CHARRIGHT:
3428 if (sel.Range(r).Empty()) {
3429 sel.Range(r) = SelectionRange(spCaret);
3430 } else {
3431 sel.Range(r) = SelectionRange(
3432 (iMessage == SCI_CHARLEFT) ? sel.Range(r).Start() : sel.Range(r).End());
3434 break;
3436 case SCI_WORDLEFT:
3437 case SCI_WORDRIGHT:
3438 case SCI_WORDLEFTEND:
3439 case SCI_WORDRIGHTEND:
3440 case SCI_WORDPARTLEFT:
3441 case SCI_WORDPARTRIGHT:
3442 case SCI_HOME:
3443 case SCI_HOMEDISPLAY:
3444 case SCI_HOMEWRAP:
3445 case SCI_VCHOME:
3446 case SCI_VCHOMEDISPLAY:
3447 case SCI_VCHOMEWRAP:
3448 case SCI_LINEEND:
3449 case SCI_LINEENDDISPLAY:
3450 case SCI_LINEENDWRAP:
3451 sel.Range(r) = SelectionRange(spCaret);
3452 break;
3454 case SCI_CHARLEFTEXTEND:
3455 case SCI_CHARRIGHTEXTEND:
3456 case SCI_WORDLEFTEXTEND:
3457 case SCI_WORDRIGHTEXTEND:
3458 case SCI_WORDLEFTENDEXTEND:
3459 case SCI_WORDRIGHTENDEXTEND:
3460 case SCI_WORDPARTLEFTEXTEND:
3461 case SCI_WORDPARTRIGHTEXTEND:
3462 case SCI_HOMEEXTEND:
3463 case SCI_HOMEDISPLAYEXTEND:
3464 case SCI_HOMEWRAPEXTEND:
3465 case SCI_VCHOMEEXTEND:
3466 case SCI_VCHOMEDISPLAYEXTEND:
3467 case SCI_VCHOMEWRAPEXTEND:
3468 case SCI_LINEENDEXTEND:
3469 case SCI_LINEENDDISPLAYEXTEND:
3470 case SCI_LINEENDWRAPEXTEND: {
3471 SelectionRange rangeNew = SelectionRange(spCaret, sel.Range(r).anchor);
3472 sel.TrimOtherSelections(r, SelectionRange(rangeNew));
3473 sel.Range(r) = rangeNew;
3475 break;
3477 default:
3478 PLATFORM_ASSERT(false);
3483 sel.RemoveDuplicates();
3485 MovedCaret(sel.RangeMain().caret, SelectionPosition(INVALID_POSITION), true);
3487 // Invalidate the new state of the selection
3488 InvalidateWholeSelection();
3490 SetLastXChosen();
3491 // Need the line moving and so forth from MovePositionTo
3492 return 0;
3495 int Editor::DelWordOrLine(unsigned int iMessage) {
3496 // Virtual space may be realised for SCI_DELWORDRIGHT or SCI_DELWORDRIGHTEND
3497 // which means 2 actions so wrap in an undo group.
3499 // Rightwards and leftwards deletions differ in treatment of virtual space.
3500 // Clear virtual space for leftwards, realise for rightwards.
3501 const bool leftwards = (iMessage == SCI_DELWORDLEFT) || (iMessage == SCI_DELLINELEFT);
3503 if (!additionalSelectionTyping) {
3504 InvalidateWholeSelection();
3505 sel.DropAdditionalRanges();
3508 UndoGroup ug0(pdoc, (sel.Count() > 1) || !leftwards);
3510 for (size_t r = 0; r < sel.Count(); r++) {
3511 if (leftwards) {
3512 // Delete to the left so first clear the virtual space.
3513 sel.Range(r).ClearVirtualSpace();
3514 } else {
3515 // Delete to the right so first realise the virtual space.
3516 sel.Range(r) = SelectionRange(
3517 RealizeVirtualSpace(sel.Range(r).caret));
3520 Range rangeDelete;
3521 switch (iMessage) {
3522 case SCI_DELWORDLEFT:
3523 rangeDelete = Range(
3524 pdoc->NextWordStart(sel.Range(r).caret.Position(), -1),
3525 sel.Range(r).caret.Position());
3526 break;
3527 case SCI_DELWORDRIGHT:
3528 rangeDelete = Range(
3529 sel.Range(r).caret.Position(),
3530 pdoc->NextWordStart(sel.Range(r).caret.Position(), 1));
3531 break;
3532 case SCI_DELWORDRIGHTEND:
3533 rangeDelete = Range(
3534 sel.Range(r).caret.Position(),
3535 pdoc->NextWordEnd(sel.Range(r).caret.Position(), 1));
3536 break;
3537 case SCI_DELLINELEFT:
3538 rangeDelete = Range(
3539 pdoc->LineStart(pdoc->LineFromPosition(sel.Range(r).caret.Position())),
3540 sel.Range(r).caret.Position());
3541 break;
3542 case SCI_DELLINERIGHT:
3543 rangeDelete = Range(
3544 sel.Range(r).caret.Position(),
3545 pdoc->LineEnd(pdoc->LineFromPosition(sel.Range(r).caret.Position())));
3546 break;
3548 if (!RangeContainsProtected(rangeDelete.start, rangeDelete.end)) {
3549 pdoc->DeleteChars(rangeDelete.start, rangeDelete.end - rangeDelete.start);
3553 // May need something stronger here: can selections overlap at this point?
3554 sel.RemoveDuplicates();
3556 MovedCaret(sel.RangeMain().caret, SelectionPosition(INVALID_POSITION), true);
3558 // Invalidate the new state of the selection
3559 InvalidateWholeSelection();
3561 SetLastXChosen();
3562 return 0;
3565 int Editor::KeyCommand(unsigned int iMessage) {
3566 switch (iMessage) {
3567 case SCI_LINEDOWN:
3568 CursorUpOrDown(1, Selection::noSel);
3569 break;
3570 case SCI_LINEDOWNEXTEND:
3571 CursorUpOrDown(1, Selection::selStream);
3572 break;
3573 case SCI_LINEDOWNRECTEXTEND:
3574 CursorUpOrDown(1, Selection::selRectangle);
3575 break;
3576 case SCI_PARADOWN:
3577 ParaUpOrDown(1, Selection::noSel);
3578 break;
3579 case SCI_PARADOWNEXTEND:
3580 ParaUpOrDown(1, Selection::selStream);
3581 break;
3582 case SCI_LINESCROLLDOWN:
3583 ScrollTo(topLine + 1);
3584 MoveCaretInsideView(false);
3585 break;
3586 case SCI_LINEUP:
3587 CursorUpOrDown(-1, Selection::noSel);
3588 break;
3589 case SCI_LINEUPEXTEND:
3590 CursorUpOrDown(-1, Selection::selStream);
3591 break;
3592 case SCI_LINEUPRECTEXTEND:
3593 CursorUpOrDown(-1, Selection::selRectangle);
3594 break;
3595 case SCI_PARAUP:
3596 ParaUpOrDown(-1, Selection::noSel);
3597 break;
3598 case SCI_PARAUPEXTEND:
3599 ParaUpOrDown(-1, Selection::selStream);
3600 break;
3601 case SCI_LINESCROLLUP:
3602 ScrollTo(topLine - 1);
3603 MoveCaretInsideView(false);
3604 break;
3606 case SCI_CHARLEFT:
3607 case SCI_CHARLEFTEXTEND:
3608 case SCI_CHARLEFTRECTEXTEND:
3609 case SCI_CHARRIGHT:
3610 case SCI_CHARRIGHTEXTEND:
3611 case SCI_CHARRIGHTRECTEXTEND:
3612 case SCI_WORDLEFT:
3613 case SCI_WORDLEFTEXTEND:
3614 case SCI_WORDRIGHT:
3615 case SCI_WORDRIGHTEXTEND:
3616 case SCI_WORDLEFTEND:
3617 case SCI_WORDLEFTENDEXTEND:
3618 case SCI_WORDRIGHTEND:
3619 case SCI_WORDRIGHTENDEXTEND:
3620 case SCI_WORDPARTLEFT:
3621 case SCI_WORDPARTLEFTEXTEND:
3622 case SCI_WORDPARTRIGHT:
3623 case SCI_WORDPARTRIGHTEXTEND:
3624 case SCI_HOME:
3625 case SCI_HOMEEXTEND:
3626 case SCI_HOMERECTEXTEND:
3627 case SCI_HOMEDISPLAY:
3628 case SCI_HOMEDISPLAYEXTEND:
3629 case SCI_HOMEWRAP:
3630 case SCI_HOMEWRAPEXTEND:
3631 case SCI_VCHOME:
3632 case SCI_VCHOMEEXTEND:
3633 case SCI_VCHOMERECTEXTEND:
3634 case SCI_VCHOMEDISPLAY:
3635 case SCI_VCHOMEDISPLAYEXTEND:
3636 case SCI_VCHOMEWRAP:
3637 case SCI_VCHOMEWRAPEXTEND:
3638 case SCI_LINEEND:
3639 case SCI_LINEENDEXTEND:
3640 case SCI_LINEENDRECTEXTEND:
3641 case SCI_LINEENDDISPLAY:
3642 case SCI_LINEENDDISPLAYEXTEND:
3643 case SCI_LINEENDWRAP:
3644 case SCI_LINEENDWRAPEXTEND:
3645 return HorizontalMove(iMessage);
3647 case SCI_DOCUMENTSTART:
3648 MovePositionTo(0);
3649 SetLastXChosen();
3650 break;
3651 case SCI_DOCUMENTSTARTEXTEND:
3652 MovePositionTo(0, Selection::selStream);
3653 SetLastXChosen();
3654 break;
3655 case SCI_DOCUMENTEND:
3656 MovePositionTo(pdoc->Length());
3657 SetLastXChosen();
3658 break;
3659 case SCI_DOCUMENTENDEXTEND:
3660 MovePositionTo(pdoc->Length(), Selection::selStream);
3661 SetLastXChosen();
3662 break;
3663 case SCI_STUTTEREDPAGEUP:
3664 PageMove(-1, Selection::noSel, true);
3665 break;
3666 case SCI_STUTTEREDPAGEUPEXTEND:
3667 PageMove(-1, Selection::selStream, true);
3668 break;
3669 case SCI_STUTTEREDPAGEDOWN:
3670 PageMove(1, Selection::noSel, true);
3671 break;
3672 case SCI_STUTTEREDPAGEDOWNEXTEND:
3673 PageMove(1, Selection::selStream, true);
3674 break;
3675 case SCI_PAGEUP:
3676 PageMove(-1);
3677 break;
3678 case SCI_PAGEUPEXTEND:
3679 PageMove(-1, Selection::selStream);
3680 break;
3681 case SCI_PAGEUPRECTEXTEND:
3682 PageMove(-1, Selection::selRectangle);
3683 break;
3684 case SCI_PAGEDOWN:
3685 PageMove(1);
3686 break;
3687 case SCI_PAGEDOWNEXTEND:
3688 PageMove(1, Selection::selStream);
3689 break;
3690 case SCI_PAGEDOWNRECTEXTEND:
3691 PageMove(1, Selection::selRectangle);
3692 break;
3693 case SCI_EDITTOGGLEOVERTYPE:
3694 inOverstrike = !inOverstrike;
3695 ShowCaretAtCurrentPosition();
3696 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
3697 NotifyUpdateUI();
3698 break;
3699 case SCI_CANCEL: // Cancel any modes - handled in subclass
3700 // Also unselect text
3701 CancelModes();
3702 if (sel.Count() > 1) {
3703 // Drop additional selections
3704 InvalidateWholeSelection();
3705 sel.DropAdditionalRanges();
3707 break;
3708 case SCI_DELETEBACK:
3709 DelCharBack(true);
3710 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3711 SetLastXChosen();
3713 EnsureCaretVisible();
3714 break;
3715 case SCI_DELETEBACKNOTLINE:
3716 DelCharBack(false);
3717 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3718 SetLastXChosen();
3720 EnsureCaretVisible();
3721 break;
3722 case SCI_TAB:
3723 Indent(true);
3724 if (caretSticky == SC_CARETSTICKY_OFF) {
3725 SetLastXChosen();
3727 EnsureCaretVisible();
3728 ShowCaretAtCurrentPosition(); // Avoid blinking
3729 break;
3730 case SCI_BACKTAB:
3731 Indent(false);
3732 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3733 SetLastXChosen();
3735 EnsureCaretVisible();
3736 ShowCaretAtCurrentPosition(); // Avoid blinking
3737 break;
3738 case SCI_NEWLINE:
3739 NewLine();
3740 break;
3741 case SCI_FORMFEED:
3742 AddChar('\f');
3743 break;
3744 case SCI_ZOOMIN:
3745 if (vs.zoomLevel < 20) {
3746 vs.zoomLevel++;
3747 InvalidateStyleRedraw();
3748 NotifyZoom();
3750 break;
3751 case SCI_ZOOMOUT:
3752 if (vs.zoomLevel > -10) {
3753 vs.zoomLevel--;
3754 InvalidateStyleRedraw();
3755 NotifyZoom();
3757 break;
3759 case SCI_DELWORDLEFT:
3760 case SCI_DELWORDRIGHT:
3761 case SCI_DELWORDRIGHTEND:
3762 case SCI_DELLINELEFT:
3763 case SCI_DELLINERIGHT:
3764 return DelWordOrLine(iMessage);
3766 case SCI_LINECOPY: {
3767 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
3768 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
3769 CopyRangeToClipboard(pdoc->LineStart(lineStart),
3770 pdoc->LineStart(lineEnd + 1));
3772 break;
3773 case SCI_LINECUT: {
3774 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
3775 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
3776 int start = pdoc->LineStart(lineStart);
3777 int end = pdoc->LineStart(lineEnd + 1);
3778 SetSelection(start, end);
3779 Cut();
3780 SetLastXChosen();
3782 break;
3783 case SCI_LINEDELETE: {
3784 int line = pdoc->LineFromPosition(sel.MainCaret());
3785 int start = pdoc->LineStart(line);
3786 int end = pdoc->LineStart(line + 1);
3787 pdoc->DeleteChars(start, end - start);
3789 break;
3790 case SCI_LINETRANSPOSE:
3791 LineTranspose();
3792 break;
3793 case SCI_LINEDUPLICATE:
3794 Duplicate(true);
3795 break;
3796 case SCI_SELECTIONDUPLICATE:
3797 Duplicate(false);
3798 break;
3799 case SCI_LOWERCASE:
3800 ChangeCaseOfSelection(cmLower);
3801 break;
3802 case SCI_UPPERCASE:
3803 ChangeCaseOfSelection(cmUpper);
3804 break;
3805 case SCI_SCROLLTOSTART:
3806 ScrollTo(0);
3807 break;
3808 case SCI_SCROLLTOEND:
3809 ScrollTo(MaxScrollPos());
3810 break;
3812 return 0;
3815 int Editor::KeyDefault(int, int) {
3816 return 0;
3819 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
3820 DwellEnd(false);
3821 int msg = kmap.Find(key, modifiers);
3822 if (msg) {
3823 if (consumed)
3824 *consumed = true;
3825 return static_cast<int>(WndProc(msg, 0, 0));
3826 } else {
3827 if (consumed)
3828 *consumed = false;
3829 return KeyDefault(key, modifiers);
3833 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
3834 return KeyDownWithModifiers(key, ModifierFlags(shift, ctrl, alt), consumed);
3837 void Editor::Indent(bool forwards) {
3838 UndoGroup ug(pdoc);
3839 for (size_t r=0; r<sel.Count(); r++) {
3840 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
3841 int caretPosition = sel.Range(r).caret.Position();
3842 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
3843 if (lineOfAnchor == lineCurrentPos) {
3844 if (forwards) {
3845 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
3846 caretPosition = sel.Range(r).caret.Position();
3847 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
3848 pdoc->tabIndents) {
3849 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3850 int indentationStep = pdoc->IndentSize();
3851 const int posSelect = pdoc->SetLineIndentation(
3852 lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
3853 sel.Range(r) = SelectionRange(posSelect);
3854 } else {
3855 if (pdoc->useTabs) {
3856 const int lengthInserted = pdoc->InsertString(caretPosition, "\t", 1);
3857 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
3858 } else {
3859 int numSpaces = (pdoc->tabInChars) -
3860 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
3861 if (numSpaces < 1)
3862 numSpaces = pdoc->tabInChars;
3863 const std::string spaceText(numSpaces, ' ');
3864 const int lengthInserted = pdoc->InsertString(caretPosition, spaceText.c_str(),
3865 static_cast<int>(spaceText.length()));
3866 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
3869 } else {
3870 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
3871 pdoc->tabIndents) {
3872 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3873 int indentationStep = pdoc->IndentSize();
3874 const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
3875 sel.Range(r) = SelectionRange(posSelect);
3876 } else {
3877 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
3878 pdoc->tabInChars;
3879 if (newColumn < 0)
3880 newColumn = 0;
3881 int newPos = caretPosition;
3882 while (pdoc->GetColumn(newPos) > newColumn)
3883 newPos--;
3884 sel.Range(r) = SelectionRange(newPos);
3887 } else { // Multiline
3888 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
3889 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
3890 // Multiple lines selected so indent / dedent
3891 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
3892 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
3893 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
3894 lineBottomSel--; // If not selecting any characters on a line, do not indent
3895 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
3896 if (lineOfAnchor < lineCurrentPos) {
3897 if (currentPosPosOnLine == 0)
3898 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
3899 else
3900 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
3901 } else {
3902 if (anchorPosOnLine == 0)
3903 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
3904 else
3905 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
3909 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
3912 class CaseFolderASCII : public CaseFolderTable {
3913 public:
3914 CaseFolderASCII() {
3915 StandardASCII();
3917 ~CaseFolderASCII() {
3922 CaseFolder *Editor::CaseFolderForEncoding() {
3923 // Simple default that only maps ASCII upper case to lower case.
3924 return new CaseFolderASCII();
3928 * Search of a text in the document, in the given range.
3929 * @return The position of the found text, -1 if not found.
3931 long Editor::FindText(
3932 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
3933 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
3934 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
3936 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
3937 int lengthFound = istrlen(ft->lpstrText);
3938 if (!pdoc->HasCaseFolder())
3939 pdoc->SetCaseFolder(CaseFolderForEncoding());
3940 try {
3941 long pos = pdoc->FindText(
3942 static_cast<int>(ft->chrg.cpMin),
3943 static_cast<int>(ft->chrg.cpMax),
3944 ft->lpstrText,
3945 static_cast<int>(wParam),
3946 &lengthFound);
3947 if (pos != -1) {
3948 ft->chrgText.cpMin = pos;
3949 ft->chrgText.cpMax = pos + lengthFound;
3951 return static_cast<int>(pos);
3952 } catch (RegexError &) {
3953 errorStatus = SC_STATUS_WARN_REGEX;
3954 return -1;
3959 * Relocatable search support : Searches relative to current selection
3960 * point and sets the selection to the found text range with
3961 * each search.
3964 * Anchor following searches at current selection start: This allows
3965 * multiple incremental interactive searches to be macro recorded
3966 * while still setting the selection to found text so the find/select
3967 * operation is self-contained.
3969 void Editor::SearchAnchor() {
3970 searchAnchor = SelectionStart().Position();
3974 * Find text from current search anchor: Must call @c SearchAnchor first.
3975 * Used for next text and previous text requests.
3976 * @return The position of the found text, -1 if not found.
3978 long Editor::SearchText(
3979 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
3980 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
3981 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
3982 sptr_t lParam) { ///< The text to search for.
3984 const char *txt = reinterpret_cast<char *>(lParam);
3985 long pos;
3986 int lengthFound = istrlen(txt);
3987 if (!pdoc->HasCaseFolder())
3988 pdoc->SetCaseFolder(CaseFolderForEncoding());
3989 try {
3990 if (iMessage == SCI_SEARCHNEXT) {
3991 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
3992 static_cast<int>(wParam),
3993 &lengthFound);
3994 } else {
3995 pos = pdoc->FindText(searchAnchor, 0, txt,
3996 static_cast<int>(wParam),
3997 &lengthFound);
3999 } catch (RegexError &) {
4000 errorStatus = SC_STATUS_WARN_REGEX;
4001 return -1;
4003 if (pos != -1) {
4004 SetSelection(static_cast<int>(pos), static_cast<int>(pos + lengthFound));
4007 return pos;
4010 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
4011 std::string ret(s);
4012 for (size_t i=0; i<ret.size(); i++) {
4013 switch (caseMapping) {
4014 case cmUpper:
4015 if (ret[i] >= 'a' && ret[i] <= 'z')
4016 ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
4017 break;
4018 case cmLower:
4019 if (ret[i] >= 'A' && ret[i] <= 'Z')
4020 ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
4021 break;
4024 return ret;
4028 * Search for text in the target range of the document.
4029 * @return The position of the found text, -1 if not found.
4031 long Editor::SearchInTarget(const char *text, int length) {
4032 int lengthFound = length;
4034 if (!pdoc->HasCaseFolder())
4035 pdoc->SetCaseFolder(CaseFolderForEncoding());
4036 try {
4037 long pos = pdoc->FindText(targetStart, targetEnd, text,
4038 searchFlags,
4039 &lengthFound);
4040 if (pos != -1) {
4041 targetStart = static_cast<int>(pos);
4042 targetEnd = static_cast<int>(pos + lengthFound);
4044 return pos;
4045 } catch (RegexError &) {
4046 errorStatus = SC_STATUS_WARN_REGEX;
4047 return -1;
4051 void Editor::GoToLine(int lineNo) {
4052 if (lineNo > pdoc->LinesTotal())
4053 lineNo = pdoc->LinesTotal();
4054 if (lineNo < 0)
4055 lineNo = 0;
4056 SetEmptySelection(pdoc->LineStart(lineNo));
4057 ShowCaretAtCurrentPosition();
4058 EnsureCaretVisible();
4061 static bool Close(Point pt1, Point pt2, Point threshold) {
4062 if (std::abs(pt1.x - pt2.x) > threshold.x)
4063 return false;
4064 if (std::abs(pt1.y - pt2.y) > threshold.y)
4065 return false;
4066 return true;
4069 std::string Editor::RangeText(int start, int end) const {
4070 if (start < end) {
4071 int len = end - start;
4072 std::string ret(len, '\0');
4073 for (int i = 0; i < len; i++) {
4074 ret[i] = pdoc->CharAt(start + i);
4076 return ret;
4078 return std::string();
4081 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
4082 if (sel.Empty()) {
4083 if (allowLineCopy) {
4084 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
4085 int start = pdoc->LineStart(currentLine);
4086 int end = pdoc->LineEnd(currentLine);
4088 std::string text = RangeText(start, end);
4089 if (pdoc->eolMode != SC_EOL_LF)
4090 text.push_back('\r');
4091 if (pdoc->eolMode != SC_EOL_CR)
4092 text.push_back('\n');
4093 ss->Copy(text, pdoc->dbcsCodePage,
4094 vs.styles[STYLE_DEFAULT].characterSet, false, true);
4096 } else {
4097 std::string text;
4098 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
4099 if (sel.selType == Selection::selRectangle)
4100 std::sort(rangesInOrder.begin(), rangesInOrder.end());
4101 for (size_t r=0; r<rangesInOrder.size(); r++) {
4102 SelectionRange current = rangesInOrder[r];
4103 text.append(RangeText(current.Start().Position(), current.End().Position()));
4104 if (sel.selType == Selection::selRectangle) {
4105 if (pdoc->eolMode != SC_EOL_LF)
4106 text.push_back('\r');
4107 if (pdoc->eolMode != SC_EOL_CR)
4108 text.push_back('\n');
4111 ss->Copy(text, pdoc->dbcsCodePage,
4112 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
4116 void Editor::CopyRangeToClipboard(int start, int end) {
4117 start = pdoc->ClampPositionIntoDocument(start);
4118 end = pdoc->ClampPositionIntoDocument(end);
4119 SelectionText selectedText;
4120 std::string text = RangeText(start, end);
4121 selectedText.Copy(text,
4122 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
4123 CopyToClipboard(selectedText);
4126 void Editor::CopyText(int length, const char *text) {
4127 SelectionText selectedText;
4128 selectedText.Copy(std::string(text, length),
4129 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
4130 CopyToClipboard(selectedText);
4133 void Editor::SetDragPosition(SelectionPosition newPos) {
4134 if (newPos.Position() >= 0) {
4135 newPos = MovePositionOutsideChar(newPos, 1);
4136 posDrop = newPos;
4138 if (!(posDrag == newPos)) {
4139 caret.on = true;
4140 if (FineTickerAvailable()) {
4141 FineTickerCancel(tickCaret);
4142 if ((caret.active) && (caret.period > 0) && (newPos.Position() < 0))
4143 FineTickerStart(tickCaret, caret.period, caret.period/10);
4144 } else {
4145 SetTicking(true);
4147 InvalidateCaret();
4148 posDrag = newPos;
4149 InvalidateCaret();
4153 void Editor::DisplayCursor(Window::Cursor c) {
4154 if (cursorMode == SC_CURSORNORMAL)
4155 wMain.SetCursor(c);
4156 else
4157 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
4160 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
4161 int xMove = static_cast<int>(ptStart.x - ptNow.x);
4162 int yMove = static_cast<int>(ptStart.y - ptNow.y);
4163 int distanceSquared = xMove * xMove + yMove * yMove;
4164 return distanceSquared > 16;
4167 void Editor::StartDrag() {
4168 // Always handled by subclasses
4169 //SetMouseCapture(true);
4170 //DisplayCursor(Window::cursorArrow);
4173 void Editor::DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular) {
4174 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
4175 if (inDragDrop == ddDragging)
4176 dropWentOutside = false;
4178 bool positionWasInSelection = PositionInSelection(position.Position());
4180 bool positionOnEdgeOfSelection =
4181 (position == SelectionStart()) || (position == SelectionEnd());
4183 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
4184 (positionOnEdgeOfSelection && !moving)) {
4186 SelectionPosition selStart = SelectionStart();
4187 SelectionPosition selEnd = SelectionEnd();
4189 UndoGroup ug(pdoc);
4191 SelectionPosition positionAfterDeletion = position;
4192 if ((inDragDrop == ddDragging) && moving) {
4193 // Remove dragged out text
4194 if (rectangular || sel.selType == Selection::selLines) {
4195 for (size_t r=0; r<sel.Count(); r++) {
4196 if (position >= sel.Range(r).Start()) {
4197 if (position > sel.Range(r).End()) {
4198 positionAfterDeletion.Add(-sel.Range(r).Length());
4199 } else {
4200 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
4204 } else {
4205 if (position > selStart) {
4206 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
4209 ClearSelection();
4211 position = positionAfterDeletion;
4213 std::string convertedText = Document::TransformLineEnds(value, lengthValue, pdoc->eolMode);
4215 if (rectangular) {
4216 PasteRectangular(position, convertedText.c_str(), static_cast<int>(convertedText.length()));
4217 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
4218 SetEmptySelection(position);
4219 } else {
4220 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
4221 position = RealizeVirtualSpace(position);
4222 const int lengthInserted = pdoc->InsertString(
4223 position.Position(), convertedText.c_str(), static_cast<int>(convertedText.length()));
4224 if (lengthInserted > 0) {
4225 SelectionPosition posAfterInsertion = position;
4226 posAfterInsertion.Add(lengthInserted);
4227 SetSelection(posAfterInsertion, position);
4230 } else if (inDragDrop == ddDragging) {
4231 SetEmptySelection(position);
4235 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
4236 DropAt(position, value, strlen(value), moving, rectangular);
4240 * @return true if given position is inside the selection,
4242 bool Editor::PositionInSelection(int pos) {
4243 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
4244 for (size_t r=0; r<sel.Count(); r++) {
4245 if (sel.Range(r).Contains(pos))
4246 return true;
4248 return false;
4251 bool Editor::PointInSelection(Point pt) {
4252 SelectionPosition pos = SPositionFromLocation(pt, false, true);
4253 Point ptPos = LocationFromPosition(pos);
4254 for (size_t r=0; r<sel.Count(); r++) {
4255 SelectionRange range = sel.Range(r);
4256 if (range.Contains(pos)) {
4257 bool hit = true;
4258 if (pos == range.Start()) {
4259 // see if just before selection
4260 if (pt.x < ptPos.x) {
4261 hit = false;
4264 if (pos == range.End()) {
4265 // see if just after selection
4266 if (pt.x > ptPos.x) {
4267 hit = false;
4270 if (hit)
4271 return true;
4274 return false;
4277 bool Editor::PointInSelMargin(Point pt) const {
4278 // Really means: "Point in a margin"
4279 if (vs.fixedColumnWidth > 0) { // There is a margin
4280 PRectangle rcSelMargin = GetClientRectangle();
4281 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart - vs.leftMarginWidth);
4282 rcSelMargin.left = static_cast<XYPOSITION>(vs.textStart - vs.fixedColumnWidth);
4283 return rcSelMargin.ContainsWholePixel(pt);
4284 } else {
4285 return false;
4289 Window::Cursor Editor::GetMarginCursor(Point pt) const {
4290 int x = 0;
4291 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
4292 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
4293 return static_cast<Window::Cursor>(vs.ms[margin].cursor);
4294 x += vs.ms[margin].width;
4296 return Window::cursorReverseArrow;
4299 void Editor::TrimAndSetSelection(int currentPos_, int anchor_) {
4300 sel.TrimSelection(SelectionRange(currentPos_, anchor_));
4301 SetSelection(currentPos_, anchor_);
4304 void Editor::LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine) {
4305 int selCurrentPos, selAnchorPos;
4306 if (wholeLine) {
4307 int lineCurrent_ = pdoc->LineFromPosition(lineCurrentPos_);
4308 int lineAnchor_ = pdoc->LineFromPosition(lineAnchorPos_);
4309 if (lineAnchorPos_ < lineCurrentPos_) {
4310 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
4311 selAnchorPos = pdoc->LineStart(lineAnchor_);
4312 } else if (lineAnchorPos_ > lineCurrentPos_) {
4313 selCurrentPos = pdoc->LineStart(lineCurrent_);
4314 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
4315 } else { // Same line, select it
4316 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
4317 selAnchorPos = pdoc->LineStart(lineAnchor_);
4319 } else {
4320 if (lineAnchorPos_ < lineCurrentPos_) {
4321 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
4322 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4323 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4324 } else if (lineAnchorPos_ > lineCurrentPos_) {
4325 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
4326 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4327 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
4328 } else { // Same line, select it
4329 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4330 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4331 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4334 TrimAndSetSelection(selCurrentPos, selAnchorPos);
4337 void Editor::WordSelection(int pos) {
4338 if (pos < wordSelectAnchorStartPos) {
4339 // Extend backward to the word containing pos.
4340 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
4341 // This ensures that a series of empty lines isn't counted as a single "word".
4342 if (!pdoc->IsLineEndPosition(pos))
4343 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
4344 TrimAndSetSelection(pos, wordSelectAnchorEndPos);
4345 } else if (pos > wordSelectAnchorEndPos) {
4346 // Extend forward to the word containing the character to the left of pos.
4347 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
4348 // This ensures that a series of empty lines isn't counted as a single "word".
4349 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
4350 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
4351 TrimAndSetSelection(pos, wordSelectAnchorStartPos);
4352 } else {
4353 // Select only the anchored word
4354 if (pos >= originalAnchorPos)
4355 TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
4356 else
4357 TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
4361 void Editor::DwellEnd(bool mouseMoved) {
4362 if (mouseMoved)
4363 ticksToDwell = dwellDelay;
4364 else
4365 ticksToDwell = SC_TIME_FOREVER;
4366 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
4367 dwelling = false;
4368 NotifyDwelling(ptMouseLast, dwelling);
4370 if (FineTickerAvailable()) {
4371 FineTickerCancel(tickDwell);
4372 if (mouseMoved && (dwellDelay < SC_TIME_FOREVER)) {
4373 //FineTickerStart(tickDwell, dwellDelay, dwellDelay/10);
4378 void Editor::MouseLeave() {
4379 SetHotSpotRange(NULL);
4380 if (!HaveMouseCapture()) {
4381 ptMouseLast = Point(-1,-1);
4382 DwellEnd(true);
4386 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
4387 return (!rectangular && ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0))
4388 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
4391 void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) {
4392 SetHoverIndicatorPoint(pt);
4393 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
4394 ptMouseLast = pt;
4395 const bool ctrl = (modifiers & SCI_CTRL) != 0;
4396 const bool shift = (modifiers & SCI_SHIFT) != 0;
4397 const bool alt = (modifiers & SCI_ALT) != 0;
4398 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
4399 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4400 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4401 newCharPos = MovePositionOutsideChar(newCharPos, -1);
4402 inDragDrop = ddNone;
4403 sel.SetMoveExtends(false);
4405 if (NotifyMarginClick(pt, modifiers))
4406 return;
4408 NotifyIndicatorClick(true, newPos.Position(), modifiers);
4410 bool inSelMargin = PointInSelMargin(pt);
4411 // In margin ctrl+(double)click should always select everything
4412 if (ctrl && inSelMargin) {
4413 SelectAll();
4414 lastClickTime = curTime;
4415 lastClick = pt;
4416 return;
4418 if (shift && !inSelMargin) {
4419 SetSelection(newPos);
4421 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick, doubleClickCloseThreshold)) {
4422 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
4423 SetMouseCapture(true);
4424 if (FineTickerAvailable()) {
4425 FineTickerStart(tickScroll, 100, 10);
4427 if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))
4428 SetEmptySelection(newPos.Position());
4429 bool doubleClick = false;
4430 // Stop mouse button bounce changing selection type
4431 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
4432 if (inSelMargin) {
4433 // Inside margin selection type should be either selSubLine or selWholeLine.
4434 if (selectionType == selSubLine) {
4435 // If it is selSubLine, we're inside a *double* click and word wrap is enabled,
4436 // so we switch to selWholeLine in order to select whole line.
4437 selectionType = selWholeLine;
4438 } else if (selectionType != selSubLine && selectionType != selWholeLine) {
4439 // If it is neither, reset selection type to line selection.
4440 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4442 } else {
4443 if (selectionType == selChar) {
4444 selectionType = selWord;
4445 doubleClick = true;
4446 } else if (selectionType == selWord) {
4447 // Since we ended up here, we're inside a *triple* click, which should always select
4448 // whole line regardless of word wrap being enabled or not.
4449 selectionType = selWholeLine;
4450 } else {
4451 selectionType = selChar;
4452 originalAnchorPos = sel.MainCaret();
4457 if (selectionType == selWord) {
4458 int charPos = originalAnchorPos;
4459 if (sel.MainCaret() == originalAnchorPos) {
4460 charPos = PositionFromLocation(pt, false, true);
4461 charPos = MovePositionOutsideChar(charPos, -1);
4464 int startWord, endWord;
4465 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
4466 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
4467 endWord = pdoc->ExtendWordSelect(charPos, 1);
4468 } else {
4469 // Selecting backwards, or anchor beyond last character on line. In these cases,
4470 // we select the word containing the character to the *left* of the anchor.
4471 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
4472 startWord = pdoc->ExtendWordSelect(charPos, -1);
4473 endWord = pdoc->ExtendWordSelect(startWord, 1);
4474 } else {
4475 // Anchor at start of line; select nothing to begin with.
4476 startWord = charPos;
4477 endWord = charPos;
4481 wordSelectAnchorStartPos = startWord;
4482 wordSelectAnchorEndPos = endWord;
4483 wordSelectInitialCaretPos = sel.MainCaret();
4484 WordSelection(wordSelectInitialCaretPos);
4485 } else if (selectionType == selSubLine || selectionType == selWholeLine) {
4486 lineAnchorPos = newPos.Position();
4487 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
4488 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
4489 } else {
4490 SetEmptySelection(sel.MainCaret());
4492 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
4493 if (doubleClick) {
4494 NotifyDoubleClick(pt, modifiers);
4495 if (PositionIsHotspot(newCharPos.Position()))
4496 NotifyHotSpotDoubleClicked(newCharPos.Position(), modifiers);
4498 } else { // Single click
4499 if (inSelMargin) {
4500 sel.selType = Selection::selStream;
4501 if (!shift) {
4502 // Single click in margin: select whole line or only subline if word wrap is enabled
4503 lineAnchorPos = newPos.Position();
4504 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4505 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
4506 } else {
4507 // Single shift+click in margin: select from line anchor to clicked line
4508 if (sel.MainAnchor() > sel.MainCaret())
4509 lineAnchorPos = sel.MainAnchor() - 1;
4510 else
4511 lineAnchorPos = sel.MainAnchor();
4512 // Reset selection type if there is an empty selection.
4513 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
4514 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
4515 // This ensures that we continue selecting in the same selection mode.
4516 if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
4517 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4518 LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
4521 SetDragPosition(SelectionPosition(invalidPosition));
4522 SetMouseCapture(true);
4523 if (FineTickerAvailable()) {
4524 FineTickerStart(tickScroll, 100, 10);
4526 } else {
4527 if (PointIsHotspot(pt)) {
4528 NotifyHotSpotClicked(newCharPos.Position(), modifiers);
4529 hotSpotClickPos = newCharPos.Position();
4531 if (!shift) {
4532 if (PointInSelection(pt) && !SelectionEmpty())
4533 inDragDrop = ddInitial;
4534 else
4535 inDragDrop = ddNone;
4537 SetMouseCapture(true);
4538 if (FineTickerAvailable()) {
4539 FineTickerStart(tickScroll, 100, 10);
4541 if (inDragDrop != ddInitial) {
4542 SetDragPosition(SelectionPosition(invalidPosition));
4543 if (!shift) {
4544 if (ctrl && multipleSelection) {
4545 SelectionRange range(newPos);
4546 sel.TentativeSelection(range);
4547 InvalidateSelection(range, true);
4548 } else {
4549 InvalidateSelection(SelectionRange(newPos), true);
4550 if (sel.Count() > 1)
4551 Redraw();
4552 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
4553 sel.Clear();
4554 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
4555 SetSelection(newPos, newPos);
4558 SelectionPosition anchorCurrent = newPos;
4559 if (shift)
4560 anchorCurrent = sel.IsRectangular() ?
4561 sel.Rectangular().anchor : sel.RangeMain().anchor;
4562 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
4563 selectionType = selChar;
4564 originalAnchorPos = sel.MainCaret();
4565 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
4566 SetRectangularRange();
4570 lastClickTime = curTime;
4571 lastClick = pt;
4572 lastXChosen = static_cast<int>(pt.x) + xOffset;
4573 ShowCaretAtCurrentPosition();
4576 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
4577 return ButtonDownWithModifiers(pt, curTime, ModifierFlags(shift, ctrl, alt));
4580 bool Editor::PositionIsHotspot(int position) const {
4581 return vs.styles[pdoc->StyleIndexAt(position)].hotspot;
4584 bool Editor::PointIsHotspot(Point pt) {
4585 int pos = PositionFromLocation(pt, true, true);
4586 if (pos == INVALID_POSITION)
4587 return false;
4588 return PositionIsHotspot(pos);
4591 void Editor::SetHoverIndicatorPosition(int position) {
4592 int hoverIndicatorPosPrev = hoverIndicatorPos;
4593 hoverIndicatorPos = INVALID_POSITION;
4594 if (vs.indicatorsDynamic == 0)
4595 return;
4596 if (position != INVALID_POSITION) {
4597 for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
4598 if (vs.indicators[deco->indicator].IsDynamic()) {
4599 if (pdoc->decorations.ValueAt(deco->indicator, position)) {
4600 hoverIndicatorPos = position;
4605 if (hoverIndicatorPosPrev != hoverIndicatorPos) {
4606 Redraw();
4610 void Editor::SetHoverIndicatorPoint(Point pt) {
4611 if (vs.indicatorsDynamic == 0) {
4612 SetHoverIndicatorPosition(INVALID_POSITION);
4613 } else {
4614 SetHoverIndicatorPosition(PositionFromLocation(pt, true, true));
4618 void Editor::SetHotSpotRange(Point *pt) {
4619 if (pt) {
4620 int pos = PositionFromLocation(*pt, false, true);
4622 // If we don't limit this to word characters then the
4623 // range can encompass more than the run range and then
4624 // the underline will not be drawn properly.
4625 Range hsNew;
4626 hsNew.start = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
4627 hsNew.end = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
4629 // Only invalidate the range if the hotspot range has changed...
4630 if (!(hsNew == hotspot)) {
4631 if (hotspot.Valid()) {
4632 InvalidateRange(hotspot.start, hotspot.end);
4634 hotspot = hsNew;
4635 InvalidateRange(hotspot.start, hotspot.end);
4637 } else {
4638 if (hotspot.Valid()) {
4639 InvalidateRange(hotspot.start, hotspot.end);
4641 hotspot = Range(invalidPosition);
4645 Range Editor::GetHotSpotRange() const {
4646 return hotspot;
4649 void Editor::ButtonMoveWithModifiers(Point pt, int modifiers) {
4650 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
4651 DwellEnd(true);
4654 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
4655 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4656 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
4658 if (inDragDrop == ddInitial) {
4659 if (DragThreshold(ptMouseLast, pt)) {
4660 SetMouseCapture(false);
4661 if (FineTickerAvailable()) {
4662 FineTickerCancel(tickScroll);
4664 SetDragPosition(movePos);
4665 CopySelectionRange(&drag);
4666 StartDrag();
4668 return;
4671 ptMouseLast = pt;
4672 PRectangle rcClient = GetClientRectangle();
4673 Point ptOrigin = GetVisibleOriginInMain();
4674 rcClient.Move(0, -ptOrigin.y);
4675 if (FineTickerAvailable() && (dwellDelay < SC_TIME_FOREVER) && rcClient.Contains(pt)) {
4676 FineTickerStart(tickDwell, dwellDelay, dwellDelay/10);
4678 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
4679 if (HaveMouseCapture()) {
4681 // Slow down autoscrolling/selection
4682 autoScrollTimer.ticksToWait -= timer.tickSize;
4683 if (autoScrollTimer.ticksToWait > 0)
4684 return;
4685 autoScrollTimer.ticksToWait = autoScrollDelay;
4687 // Adjust selection
4688 if (posDrag.IsValid()) {
4689 SetDragPosition(movePos);
4690 } else {
4691 if (selectionType == selChar) {
4692 if (sel.selType == Selection::selStream && (modifiers & SCI_ALT) && mouseSelectionRectangularSwitch) {
4693 sel.selType = Selection::selRectangle;
4695 if (sel.IsRectangular()) {
4696 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
4697 SetSelection(movePos, sel.RangeMain().anchor);
4698 } else if (sel.Count() > 1) {
4699 InvalidateSelection(sel.RangeMain(), false);
4700 SelectionRange range(movePos, sel.RangeMain().anchor);
4701 sel.TentativeSelection(range);
4702 InvalidateSelection(range, true);
4703 } else {
4704 SetSelection(movePos, sel.RangeMain().anchor);
4706 } else if (selectionType == selWord) {
4707 // Continue selecting by word
4708 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
4709 // No need to do anything. Previously this case was lumped
4710 // in with "Moved forward", but that can be harmful in this
4711 // case: a handler for the NotifyDoubleClick re-adjusts
4712 // the selection for a fancier definition of "word" (for
4713 // example, in Perl it is useful to include the leading
4714 // '$', '%' or '@' on variables for word selection). In this
4715 // the ButtonMove() called via Tick() for auto-scrolling
4716 // could result in the fancier word selection adjustment
4717 // being unmade.
4718 } else {
4719 wordSelectInitialCaretPos = -1;
4720 WordSelection(movePos.Position());
4722 } else {
4723 // Continue selecting by line
4724 LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
4728 // Autoscroll
4729 int lineMove = DisplayFromPosition(movePos.Position());
4730 if (pt.y > rcClient.bottom) {
4731 ScrollTo(lineMove - LinesOnScreen() + 1);
4732 Redraw();
4733 } else if (pt.y < rcClient.top) {
4734 ScrollTo(lineMove);
4735 Redraw();
4737 EnsureCaretVisible(false, false, true);
4739 if (hotspot.Valid() && !PointIsHotspot(pt))
4740 SetHotSpotRange(NULL);
4742 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,true) != hotSpotClickPos) {
4743 if (inDragDrop == ddNone) {
4744 DisplayCursor(Window::cursorText);
4746 hotSpotClickPos = INVALID_POSITION;
4749 } else {
4750 if (vs.fixedColumnWidth > 0) { // There is a margin
4751 if (PointInSelMargin(pt)) {
4752 DisplayCursor(GetMarginCursor(pt));
4753 SetHotSpotRange(NULL);
4754 return; // No need to test for selection
4757 // Display regular (drag) cursor over selection
4758 if (PointInSelection(pt) && !SelectionEmpty()) {
4759 DisplayCursor(Window::cursorArrow);
4760 } else {
4761 SetHoverIndicatorPoint(pt);
4762 if (PointIsHotspot(pt)) {
4763 DisplayCursor(Window::cursorHand);
4764 SetHotSpotRange(&pt);
4765 } else {
4766 if (hoverIndicatorPos != invalidPosition)
4767 DisplayCursor(Window::cursorHand);
4768 else
4769 DisplayCursor(Window::cursorText);
4770 SetHotSpotRange(NULL);
4776 void Editor::ButtonMove(Point pt) {
4777 ButtonMoveWithModifiers(pt, 0);
4780 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
4781 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
4782 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
4783 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4784 if (hoverIndicatorPos != INVALID_POSITION)
4785 InvalidateRange(newPos.Position(), newPos.Position() + 1);
4786 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4787 if (inDragDrop == ddInitial) {
4788 inDragDrop = ddNone;
4789 SetEmptySelection(newPos);
4790 selectionType = selChar;
4791 originalAnchorPos = sel.MainCaret();
4793 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
4794 hotSpotClickPos = INVALID_POSITION;
4795 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4796 newCharPos = MovePositionOutsideChar(newCharPos, -1);
4797 NotifyHotSpotReleaseClick(newCharPos.Position(), ctrl ? SCI_CTRL : 0);
4799 if (HaveMouseCapture()) {
4800 if (PointInSelMargin(pt)) {
4801 DisplayCursor(GetMarginCursor(pt));
4802 } else {
4803 DisplayCursor(Window::cursorText);
4804 SetHotSpotRange(NULL);
4806 ptMouseLast = pt;
4807 SetMouseCapture(false);
4808 if (FineTickerAvailable()) {
4809 FineTickerCancel(tickScroll);
4811 NotifyIndicatorClick(false, newPos.Position(), 0);
4812 if (inDragDrop == ddDragging) {
4813 SelectionPosition selStart = SelectionStart();
4814 SelectionPosition selEnd = SelectionEnd();
4815 if (selStart < selEnd) {
4816 if (drag.Length()) {
4817 const int length = static_cast<int>(drag.Length());
4818 if (ctrl) {
4819 const int lengthInserted = pdoc->InsertString(
4820 newPos.Position(), drag.Data(), length);
4821 if (lengthInserted > 0) {
4822 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4824 } else if (newPos < selStart) {
4825 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
4826 const int lengthInserted = pdoc->InsertString(
4827 newPos.Position(), drag.Data(), length);
4828 if (lengthInserted > 0) {
4829 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4831 } else if (newPos > selEnd) {
4832 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
4833 newPos.Add(-static_cast<int>(drag.Length()));
4834 const int lengthInserted = pdoc->InsertString(
4835 newPos.Position(), drag.Data(), length);
4836 if (lengthInserted > 0) {
4837 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4839 } else {
4840 SetEmptySelection(newPos.Position());
4842 drag.Clear();
4844 selectionType = selChar;
4846 } else {
4847 if (selectionType == selChar) {
4848 if (sel.Count() > 1) {
4849 sel.RangeMain() =
4850 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
4851 InvalidateWholeSelection();
4852 } else {
4853 SetSelection(newPos, sel.RangeMain().anchor);
4856 sel.CommitTentative();
4858 SetRectangularRange();
4859 lastClickTime = curTime;
4860 lastClick = pt;
4861 lastXChosen = static_cast<int>(pt.x) + xOffset;
4862 if (sel.selType == Selection::selStream) {
4863 SetLastXChosen();
4865 inDragDrop = ddNone;
4866 EnsureCaretVisible(false);
4870 // Called frequently to perform background UI including
4871 // caret blinking and automatic scrolling.
4872 void Editor::Tick() {
4873 if (HaveMouseCapture()) {
4874 // Auto scroll
4875 ButtonMove(ptMouseLast);
4877 if (caret.period > 0) {
4878 timer.ticksToWait -= timer.tickSize;
4879 if (timer.ticksToWait <= 0) {
4880 caret.on = !caret.on;
4881 timer.ticksToWait = caret.period;
4882 if (caret.active) {
4883 InvalidateCaret();
4887 if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) {
4888 scrollWidth = view.lineWidthMaxSeen;
4889 SetScrollBars();
4891 if ((dwellDelay < SC_TIME_FOREVER) &&
4892 (ticksToDwell > 0) &&
4893 (!HaveMouseCapture()) &&
4894 (ptMouseLast.y >= 0)) {
4895 ticksToDwell -= timer.tickSize;
4896 if (ticksToDwell <= 0) {
4897 dwelling = true;
4898 NotifyDwelling(ptMouseLast, dwelling);
4903 bool Editor::Idle() {
4904 bool needWrap = Wrapping() && wrapPending.NeedsWrap();
4906 if (needWrap) {
4907 // Wrap lines during idle.
4908 WrapLines(wsIdle);
4909 // No more wrapping
4910 needWrap = wrapPending.NeedsWrap();
4911 } else if (needIdleStyling) {
4912 IdleStyling();
4915 // Add more idle things to do here, but make sure idleDone is
4916 // set correctly before the function returns. returning
4917 // false will stop calling this idle function until SetIdle() is
4918 // called again.
4920 const bool idleDone = !needWrap && !needIdleStyling; // && thatDone && theOtherThingDone...
4922 return !idleDone;
4925 void Editor::SetTicking(bool) {
4926 // SetTicking is deprecated. In the past it was pure virtual and was overridden in each
4927 // derived platform class but fine grained timers should now be implemented.
4928 // Either way, execution should not arrive here so assert failure.
4929 assert(false);
4932 void Editor::TickFor(TickReason reason) {
4933 switch (reason) {
4934 case tickCaret:
4935 caret.on = !caret.on;
4936 if (caret.active) {
4937 InvalidateCaret();
4939 break;
4940 case tickScroll:
4941 // Auto scroll
4942 ButtonMove(ptMouseLast);
4943 break;
4944 case tickWiden:
4945 SetScrollBars();
4946 FineTickerCancel(tickWiden);
4947 break;
4948 case tickDwell:
4949 if ((!HaveMouseCapture()) &&
4950 (ptMouseLast.y >= 0)) {
4951 dwelling = true;
4952 NotifyDwelling(ptMouseLast, dwelling);
4954 FineTickerCancel(tickDwell);
4955 break;
4956 default:
4957 // tickPlatform handled by subclass
4958 break;
4962 bool Editor::FineTickerAvailable() {
4963 return false;
4966 // FineTickerStart is be overridden by subclasses that support fine ticking so
4967 // this method should never be called.
4968 bool Editor::FineTickerRunning(TickReason) {
4969 assert(false);
4970 return false;
4973 // FineTickerStart is be overridden by subclasses that support fine ticking so
4974 // this method should never be called.
4975 void Editor::FineTickerStart(TickReason, int, int) {
4976 assert(false);
4979 // FineTickerCancel is be overridden by subclasses that support fine ticking so
4980 // this method should never be called.
4981 void Editor::FineTickerCancel(TickReason) {
4982 assert(false);
4985 void Editor::SetFocusState(bool focusState) {
4986 hasFocus = focusState;
4987 NotifyFocus(hasFocus);
4988 if (!hasFocus) {
4989 CancelModes();
4991 ShowCaretAtCurrentPosition();
4994 int Editor::PositionAfterArea(PRectangle rcArea) const {
4995 // The start of the document line after the display line after the area
4996 // This often means that the line after a modification is restyled which helps
4997 // detect multiline comment additions and heals single line comments
4998 int lineAfter = TopLineOfMain() + static_cast<int>(rcArea.bottom - 1) / vs.lineHeight + 1;
4999 if (lineAfter < cs.LinesDisplayed())
5000 return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
5001 else
5002 return pdoc->Length();
5005 // Style to a position within the view. If this causes a change at end of last line then
5006 // affects later lines so style all the viewed text.
5007 void Editor::StyleToPositionInView(Position pos) {
5008 int endWindow = PositionAfterArea(GetClientDrawingRectangle());
5009 if (pos > endWindow)
5010 pos = endWindow;
5011 const int styleAtEnd = pdoc->StyleIndexAt(pos-1);
5012 pdoc->EnsureStyledTo(pos);
5013 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleIndexAt(pos-1))) {
5014 // Style at end of line changed so is multi-line change like starting a comment
5015 // so require rest of window to be styled.
5016 DiscardOverdraw(); // Prepared bitmaps may be invalid
5017 // DiscardOverdraw may have truncated client drawing area so recalculate endWindow
5018 endWindow = PositionAfterArea(GetClientDrawingRectangle());
5019 pdoc->EnsureStyledTo(endWindow);
5023 int Editor::PositionAfterMaxStyling(int posMax, bool scrolling) const {
5024 if ((idleStyling == SC_IDLESTYLING_NONE) || (idleStyling == SC_IDLESTYLING_AFTERVISIBLE)) {
5025 // Both states do not limit styling
5026 return posMax;
5029 // Try to keep time taken by styling reasonable so interaction remains smooth.
5030 // When scrolling, allow less time to ensure responsive
5031 const double secondsAllowed = scrolling ? 0.005 : 0.02;
5033 const int linesToStyle = Platform::Clamp(static_cast<int>(secondsAllowed / pdoc->durationStyleOneLine),
5034 10, 0x10000);
5035 const int stylingMaxLine = std::min(
5036 static_cast<int>(pdoc->LineFromPosition(pdoc->GetEndStyled()) + linesToStyle),
5037 pdoc->LinesTotal());
5038 return std::min(static_cast<int>(pdoc->LineStart(stylingMaxLine)), posMax);
5041 void Editor::StartIdleStyling(bool truncatedLastStyling) {
5042 if ((idleStyling == SC_IDLESTYLING_ALL) || (idleStyling == SC_IDLESTYLING_AFTERVISIBLE)) {
5043 if (pdoc->GetEndStyled() < pdoc->Length()) {
5044 // Style remainder of document in idle time
5045 needIdleStyling = true;
5047 } else if (truncatedLastStyling) {
5048 needIdleStyling = true;
5051 if (needIdleStyling) {
5052 SetIdle(true);
5056 // Style for an area but bound the amount of styling to remain responsive
5057 void Editor::StyleAreaBounded(PRectangle rcArea, bool scrolling) {
5058 const int posAfterArea = PositionAfterArea(rcArea);
5059 const int posAfterMax = PositionAfterMaxStyling(posAfterArea, scrolling);
5060 if (posAfterMax < posAfterArea) {
5061 // Idle styling may be performed before current visible area
5062 // Style a bit now then style further in idle time
5063 pdoc->StyleToAdjustingLineDuration(posAfterMax);
5064 } else {
5065 // Can style all wanted now.
5066 StyleToPositionInView(posAfterArea);
5068 StartIdleStyling(posAfterMax < posAfterArea);
5071 void Editor::IdleStyling() {
5072 const int posAfterArea = PositionAfterArea(GetClientRectangle());
5073 const int endGoal = (idleStyling >= SC_IDLESTYLING_AFTERVISIBLE) ?
5074 pdoc->Length() : posAfterArea;
5075 const int posAfterMax = PositionAfterMaxStyling(endGoal, false);
5076 pdoc->StyleToAdjustingLineDuration(posAfterMax);
5077 if (pdoc->GetEndStyled() >= endGoal) {
5078 needIdleStyling = false;
5082 void Editor::IdleWork() {
5083 // Style the line after the modification as this allows modifications that change just the
5084 // line of the modification to heal instead of propagating to the rest of the window.
5085 if (workNeeded.items & WorkNeeded::workStyle) {
5086 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2));
5088 NotifyUpdateUI();
5089 workNeeded.Reset();
5092 void Editor::QueueIdleWork(WorkNeeded::workItems items, int upTo) {
5093 workNeeded.Need(items, upTo);
5096 bool Editor::PaintContains(PRectangle rc) {
5097 if (rc.Empty()) {
5098 return true;
5099 } else {
5100 return rcPaint.Contains(rc);
5104 bool Editor::PaintContainsMargin() {
5105 if (wMargin.GetID()) {
5106 // With separate margin view, paint of text view
5107 // never contains margin.
5108 return false;
5110 PRectangle rcSelMargin = GetClientRectangle();
5111 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart);
5112 return PaintContains(rcSelMargin);
5115 void Editor::CheckForChangeOutsidePaint(Range r) {
5116 if (paintState == painting && !paintingAllText) {
5117 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
5118 if (!r.Valid())
5119 return;
5121 PRectangle rcRange = RectangleFromRange(r, 0);
5122 PRectangle rcText = GetTextRectangle();
5123 if (rcRange.top < rcText.top) {
5124 rcRange.top = rcText.top;
5126 if (rcRange.bottom > rcText.bottom) {
5127 rcRange.bottom = rcText.bottom;
5130 if (!PaintContains(rcRange)) {
5131 AbandonPaint();
5132 paintAbandonedByStyling = true;
5137 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
5138 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
5139 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
5140 CheckForChangeOutsidePaint(Range(braces[0]));
5141 CheckForChangeOutsidePaint(Range(pos0));
5142 braces[0] = pos0;
5144 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
5145 CheckForChangeOutsidePaint(Range(braces[1]));
5146 CheckForChangeOutsidePaint(Range(pos1));
5147 braces[1] = pos1;
5149 bracesMatchStyle = matchStyle;
5150 if (paintState == notPainting) {
5151 Redraw();
5156 void Editor::SetAnnotationHeights(int start, int end) {
5157 if (vs.annotationVisible) {
5158 RefreshStyleData();
5159 bool changedHeight = false;
5160 for (int line=start; line<end && line<pdoc->LinesTotal(); line++) {
5161 int linesWrapped = 1;
5162 if (Wrapping()) {
5163 AutoSurface surface(this);
5164 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
5165 if (surface && ll) {
5166 view.LayoutLine(*this, line, surface, vs, ll, wrapWidth);
5167 linesWrapped = ll->lines;
5170 if (cs.SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
5171 changedHeight = true;
5173 if (changedHeight) {
5174 Redraw();
5179 void Editor::SetDocPointer(Document *document) {
5180 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
5181 pdoc->RemoveWatcher(this, 0);
5182 pdoc->Release();
5183 if (document == NULL) {
5184 pdoc = new Document();
5185 } else {
5186 pdoc = document;
5188 pdoc->AddRef();
5190 // Ensure all positions within document
5191 sel.Clear();
5192 targetStart = 0;
5193 targetEnd = 0;
5195 braces[0] = invalidPosition;
5196 braces[1] = invalidPosition;
5198 vs.ReleaseAllExtendedStyles();
5200 SetRepresentations();
5202 // Reset the contraction state to fully shown.
5203 cs.Clear();
5204 cs.InsertLines(0, pdoc->LinesTotal() - 1);
5205 SetAnnotationHeights(0, pdoc->LinesTotal());
5206 view.llc.Deallocate();
5207 NeedWrapping();
5209 hotspot = Range(invalidPosition);
5210 hoverIndicatorPos = invalidPosition;
5212 view.ClearAllTabstops();
5214 pdoc->AddWatcher(this, 0);
5215 SetScrollBars();
5216 Redraw();
5219 void Editor::SetAnnotationVisible(int visible) {
5220 if (vs.annotationVisible != visible) {
5221 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
5222 vs.annotationVisible = visible;
5223 if (changedFromOrToHidden) {
5224 int dir = vs.annotationVisible ? 1 : -1;
5225 for (int line=0; line<pdoc->LinesTotal(); line++) {
5226 int annotationLines = pdoc->AnnotationLines(line);
5227 if (annotationLines > 0) {
5228 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
5232 Redraw();
5237 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
5239 int Editor::ExpandLine(int line) {
5240 int lineMaxSubord = pdoc->GetLastChild(line);
5241 line++;
5242 while (line <= lineMaxSubord) {
5243 cs.SetVisible(line, line, true);
5244 int level = pdoc->GetLevel(line);
5245 if (level & SC_FOLDLEVELHEADERFLAG) {
5246 if (cs.GetExpanded(line)) {
5247 line = ExpandLine(line);
5248 } else {
5249 line = pdoc->GetLastChild(line);
5252 line++;
5254 return lineMaxSubord;
5257 void Editor::SetFoldExpanded(int lineDoc, bool expanded) {
5258 if (cs.SetExpanded(lineDoc, expanded)) {
5259 RedrawSelMargin();
5263 void Editor::FoldLine(int line, int action) {
5264 if (line >= 0) {
5265 if (action == SC_FOLDACTION_TOGGLE) {
5266 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
5267 line = pdoc->GetFoldParent(line);
5268 if (line < 0)
5269 return;
5271 action = (cs.GetExpanded(line)) ? SC_FOLDACTION_CONTRACT : SC_FOLDACTION_EXPAND;
5274 if (action == SC_FOLDACTION_CONTRACT) {
5275 int lineMaxSubord = pdoc->GetLastChild(line);
5276 if (lineMaxSubord > line) {
5277 cs.SetExpanded(line, 0);
5278 cs.SetVisible(line + 1, lineMaxSubord, false);
5280 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
5281 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
5282 // This does not re-expand the fold
5283 EnsureCaretVisible();
5287 } else {
5288 if (!(cs.GetVisible(line))) {
5289 EnsureLineVisible(line, false);
5290 GoToLine(line);
5292 cs.SetExpanded(line, 1);
5293 ExpandLine(line);
5296 SetScrollBars();
5297 Redraw();
5301 void Editor::FoldExpand(int line, int action, int level) {
5302 bool expanding = action == SC_FOLDACTION_EXPAND;
5303 if (action == SC_FOLDACTION_TOGGLE) {
5304 expanding = !cs.GetExpanded(line);
5306 // Ensure child lines lexed and fold information extracted before
5307 // flipping the state.
5308 pdoc->GetLastChild(line, LevelNumber(level));
5309 SetFoldExpanded(line, expanding);
5310 if (expanding && (cs.HiddenLines() == 0))
5311 // Nothing to do
5312 return;
5313 int lineMaxSubord = pdoc->GetLastChild(line, LevelNumber(level));
5314 line++;
5315 cs.SetVisible(line, lineMaxSubord, expanding);
5316 while (line <= lineMaxSubord) {
5317 int levelLine = pdoc->GetLevel(line);
5318 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
5319 SetFoldExpanded(line, expanding);
5321 line++;
5323 SetScrollBars();
5324 Redraw();
5327 int Editor::ContractedFoldNext(int lineStart) const {
5328 for (int line = lineStart; line<pdoc->LinesTotal();) {
5329 if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
5330 return line;
5331 line = cs.ContractedNext(line+1);
5332 if (line < 0)
5333 return -1;
5336 return -1;
5340 * Recurse up from this line to find any folds that prevent this line from being visible
5341 * and unfold them all.
5343 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
5345 // In case in need of wrapping to ensure DisplayFromDoc works.
5346 if (lineDoc >= wrapPending.start)
5347 WrapLines(wsAll);
5349 if (!cs.GetVisible(lineDoc)) {
5350 // Back up to find a non-blank line
5351 int lookLine = lineDoc;
5352 int lookLineLevel = pdoc->GetLevel(lookLine);
5353 while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
5354 lookLineLevel = pdoc->GetLevel(--lookLine);
5356 int lineParent = pdoc->GetFoldParent(lookLine);
5357 if (lineParent < 0) {
5358 // Backed up to a top level line, so try to find parent of initial line
5359 lineParent = pdoc->GetFoldParent(lineDoc);
5361 if (lineParent >= 0) {
5362 if (lineDoc != lineParent)
5363 EnsureLineVisible(lineParent, enforcePolicy);
5364 if (!cs.GetExpanded(lineParent)) {
5365 cs.SetExpanded(lineParent, 1);
5366 ExpandLine(lineParent);
5369 SetScrollBars();
5370 Redraw();
5372 if (enforcePolicy) {
5373 int lineDisplay = cs.DisplayFromDoc(lineDoc);
5374 if (visiblePolicy & VISIBLE_SLOP) {
5375 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
5376 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
5377 SetVerticalScrollPos();
5378 Redraw();
5379 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
5380 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
5381 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
5382 SetVerticalScrollPos();
5383 Redraw();
5385 } else {
5386 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
5387 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
5388 SetVerticalScrollPos();
5389 Redraw();
5395 void Editor::FoldAll(int action) {
5396 pdoc->EnsureStyledTo(pdoc->Length());
5397 int maxLine = pdoc->LinesTotal();
5398 bool expanding = action == SC_FOLDACTION_EXPAND;
5399 if (action == SC_FOLDACTION_TOGGLE) {
5400 // Discover current state
5401 for (int lineSeek = 0; lineSeek < maxLine; lineSeek++) {
5402 if (pdoc->GetLevel(lineSeek) & SC_FOLDLEVELHEADERFLAG) {
5403 expanding = !cs.GetExpanded(lineSeek);
5404 break;
5408 if (expanding) {
5409 cs.SetVisible(0, maxLine-1, true);
5410 for (int line = 0; line < maxLine; line++) {
5411 int levelLine = pdoc->GetLevel(line);
5412 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
5413 SetFoldExpanded(line, true);
5416 } else {
5417 for (int line = 0; line < maxLine; line++) {
5418 int level = pdoc->GetLevel(line);
5419 if ((level & SC_FOLDLEVELHEADERFLAG) &&
5420 (SC_FOLDLEVELBASE == LevelNumber(level))) {
5421 SetFoldExpanded(line, false);
5422 int lineMaxSubord = pdoc->GetLastChild(line, -1);
5423 if (lineMaxSubord > line) {
5424 cs.SetVisible(line + 1, lineMaxSubord, false);
5429 SetScrollBars();
5430 Redraw();
5433 void Editor::FoldChanged(int line, int levelNow, int levelPrev) {
5434 if (levelNow & SC_FOLDLEVELHEADERFLAG) {
5435 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
5436 // Adding a fold point.
5437 if (cs.SetExpanded(line, true)) {
5438 RedrawSelMargin();
5440 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
5442 } else if (levelPrev & SC_FOLDLEVELHEADERFLAG) {
5443 const int prevLine = line - 1;
5444 const int prevLineLevel = pdoc->GetLevel(prevLine);
5446 // Combining two blocks where the first block is collapsed (e.g. by deleting the line(s) which separate(s) the two blocks)
5447 if ((LevelNumber(prevLineLevel) == LevelNumber(levelNow)) && !cs.GetVisible(prevLine))
5448 FoldLine(pdoc->GetFoldParent(prevLine), SC_FOLDACTION_EXPAND);
5450 if (!cs.GetExpanded(line)) {
5451 // Removing the fold from one that has been contracted so should expand
5452 // otherwise lines are left invisible with no way to make them visible
5453 if (cs.SetExpanded(line, true)) {
5454 RedrawSelMargin();
5456 // Combining two blocks where the second one is collapsed (e.g. by adding characters in the line which separates the two blocks)
5457 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
5460 if (!(levelNow & SC_FOLDLEVELWHITEFLAG) &&
5461 (LevelNumber(levelPrev) > LevelNumber(levelNow))) {
5462 if (cs.HiddenLines()) {
5463 // See if should still be hidden
5464 int parentLine = pdoc->GetFoldParent(line);
5465 if ((parentLine < 0) || (cs.GetExpanded(parentLine) && cs.GetVisible(parentLine))) {
5466 cs.SetVisible(line, line, true);
5467 SetScrollBars();
5468 Redraw();
5473 // Combining two blocks where the first one is collapsed (e.g. by adding characters in the line which separates the two blocks)
5474 if (!(levelNow & SC_FOLDLEVELWHITEFLAG) && (LevelNumber(levelPrev) < LevelNumber(levelNow))) {
5475 if (cs.HiddenLines()) {
5476 const int parentLine = pdoc->GetFoldParent(line);
5477 if (!cs.GetExpanded(parentLine) && cs.GetExpanded(line))
5478 FoldLine(parentLine, SC_FOLDACTION_EXPAND);
5483 void Editor::NeedShown(int pos, int len) {
5484 if (foldAutomatic & SC_AUTOMATICFOLD_SHOW) {
5485 int lineStart = pdoc->LineFromPosition(pos);
5486 int lineEnd = pdoc->LineFromPosition(pos+len);
5487 for (int line = lineStart; line <= lineEnd; line++) {
5488 EnsureLineVisible(line, false);
5490 } else {
5491 NotifyNeedShown(pos, len);
5495 int Editor::GetTag(char *tagValue, int tagNumber) {
5496 const char *text = 0;
5497 int length = 0;
5498 if ((tagNumber >= 1) && (tagNumber <= 9)) {
5499 char name[3] = "\\?";
5500 name[1] = static_cast<char>(tagNumber + '0');
5501 length = 2;
5502 text = pdoc->SubstituteByPosition(name, &length);
5504 if (tagValue) {
5505 if (text)
5506 memcpy(tagValue, text, length + 1);
5507 else
5508 *tagValue = '\0';
5510 return length;
5513 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
5514 UndoGroup ug(pdoc);
5515 if (length == -1)
5516 length = istrlen(text);
5517 if (replacePatterns) {
5518 text = pdoc->SubstituteByPosition(text, &length);
5519 if (!text) {
5520 return 0;
5523 if (targetStart != targetEnd)
5524 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
5525 targetEnd = targetStart;
5526 const int lengthInserted = pdoc->InsertString(targetStart, text, length);
5527 targetEnd = targetStart + lengthInserted;
5528 return length;
5531 bool Editor::IsUnicodeMode() const {
5532 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
5535 int Editor::CodePage() const {
5536 if (pdoc)
5537 return pdoc->dbcsCodePage;
5538 else
5539 return 0;
5542 int Editor::WrapCount(int line) {
5543 AutoSurface surface(this);
5544 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
5546 if (surface && ll) {
5547 view.LayoutLine(*this, line, surface, vs, ll, wrapWidth);
5548 return ll->lines;
5549 } else {
5550 return 1;
5554 void Editor::AddStyledText(char *buffer, int appendLength) {
5555 // The buffer consists of alternating character bytes and style bytes
5556 int textLength = appendLength / 2;
5557 std::string text(textLength, '\0');
5558 int i;
5559 for (i = 0; i < textLength; i++) {
5560 text[i] = buffer[i*2];
5562 const int lengthInserted = pdoc->InsertString(CurrentPosition(), text.c_str(), textLength);
5563 for (i = 0; i < textLength; i++) {
5564 text[i] = buffer[i*2+1];
5566 pdoc->StartStyling(CurrentPosition(), static_cast<unsigned char>(0xff));
5567 pdoc->SetStyles(textLength, text.c_str());
5568 SetEmptySelection(sel.MainCaret() + lengthInserted);
5571 static bool ValidMargin(uptr_t wParam) {
5572 return wParam <= SC_MAX_MARGIN;
5575 static char *CharPtrFromSPtr(sptr_t lParam) {
5576 return reinterpret_cast<char *>(lParam);
5579 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5580 vs.EnsureStyle(wParam);
5581 switch (iMessage) {
5582 case SCI_STYLESETFORE:
5583 vs.styles[wParam].fore = ColourDesired(static_cast<long>(lParam));
5584 break;
5585 case SCI_STYLESETBACK:
5586 vs.styles[wParam].back = ColourDesired(static_cast<long>(lParam));
5587 break;
5588 case SCI_STYLESETBOLD:
5589 vs.styles[wParam].weight = lParam != 0 ? SC_WEIGHT_BOLD : SC_WEIGHT_NORMAL;
5590 break;
5591 case SCI_STYLESETWEIGHT:
5592 vs.styles[wParam].weight = static_cast<int>(lParam);
5593 break;
5594 case SCI_STYLESETITALIC:
5595 vs.styles[wParam].italic = lParam != 0;
5596 break;
5597 case SCI_STYLESETEOLFILLED:
5598 vs.styles[wParam].eolFilled = lParam != 0;
5599 break;
5600 case SCI_STYLESETSIZE:
5601 vs.styles[wParam].size = static_cast<int>(lParam * SC_FONT_SIZE_MULTIPLIER);
5602 break;
5603 case SCI_STYLESETSIZEFRACTIONAL:
5604 vs.styles[wParam].size = static_cast<int>(lParam);
5605 break;
5606 case SCI_STYLESETFONT:
5607 if (lParam != 0) {
5608 vs.SetStyleFontName(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
5610 break;
5611 case SCI_STYLESETUNDERLINE:
5612 vs.styles[wParam].underline = lParam != 0;
5613 break;
5614 case SCI_STYLESETCASE:
5615 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
5616 break;
5617 case SCI_STYLESETCHARACTERSET:
5618 vs.styles[wParam].characterSet = static_cast<int>(lParam);
5619 pdoc->SetCaseFolder(NULL);
5620 break;
5621 case SCI_STYLESETVISIBLE:
5622 vs.styles[wParam].visible = lParam != 0;
5623 break;
5624 case SCI_STYLESETCHANGEABLE:
5625 vs.styles[wParam].changeable = lParam != 0;
5626 break;
5627 case SCI_STYLESETHOTSPOT:
5628 vs.styles[wParam].hotspot = lParam != 0;
5629 break;
5631 InvalidateStyleRedraw();
5634 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5635 vs.EnsureStyle(wParam);
5636 switch (iMessage) {
5637 case SCI_STYLEGETFORE:
5638 return vs.styles[wParam].fore.AsLong();
5639 case SCI_STYLEGETBACK:
5640 return vs.styles[wParam].back.AsLong();
5641 case SCI_STYLEGETBOLD:
5642 return vs.styles[wParam].weight > SC_WEIGHT_NORMAL;
5643 case SCI_STYLEGETWEIGHT:
5644 return vs.styles[wParam].weight;
5645 case SCI_STYLEGETITALIC:
5646 return vs.styles[wParam].italic ? 1 : 0;
5647 case SCI_STYLEGETEOLFILLED:
5648 return vs.styles[wParam].eolFilled ? 1 : 0;
5649 case SCI_STYLEGETSIZE:
5650 return vs.styles[wParam].size / SC_FONT_SIZE_MULTIPLIER;
5651 case SCI_STYLEGETSIZEFRACTIONAL:
5652 return vs.styles[wParam].size;
5653 case SCI_STYLEGETFONT:
5654 return StringResult(lParam, vs.styles[wParam].fontName);
5655 case SCI_STYLEGETUNDERLINE:
5656 return vs.styles[wParam].underline ? 1 : 0;
5657 case SCI_STYLEGETCASE:
5658 return static_cast<int>(vs.styles[wParam].caseForce);
5659 case SCI_STYLEGETCHARACTERSET:
5660 return vs.styles[wParam].characterSet;
5661 case SCI_STYLEGETVISIBLE:
5662 return vs.styles[wParam].visible ? 1 : 0;
5663 case SCI_STYLEGETCHANGEABLE:
5664 return vs.styles[wParam].changeable ? 1 : 0;
5665 case SCI_STYLEGETHOTSPOT:
5666 return vs.styles[wParam].hotspot ? 1 : 0;
5668 return 0;
5671 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
5672 const size_t len = val ? strlen(val) : 0;
5673 if (lParam) {
5674 char *ptr = CharPtrFromSPtr(lParam);
5675 if (val)
5676 memcpy(ptr, val, len+1);
5677 else
5678 *ptr = 0;
5680 return len; // Not including NUL
5683 sptr_t Editor::BytesResult(sptr_t lParam, const unsigned char *val, size_t len) {
5684 // No NUL termination: len is number of valid/displayed bytes
5685 if ((lParam) && (len > 0)) {
5686 char *ptr = CharPtrFromSPtr(lParam);
5687 if (val)
5688 memcpy(ptr, val, len);
5689 else
5690 *ptr = 0;
5692 return val ? len : 0;
5695 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5696 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
5698 // Optional macro recording hook
5699 if (recordingMacro)
5700 NotifyMacroRecord(iMessage, wParam, lParam);
5702 switch (iMessage) {
5704 case SCI_GETTEXT: {
5705 if (lParam == 0)
5706 return pdoc->Length() + 1;
5707 if (wParam == 0)
5708 return 0;
5709 char *ptr = CharPtrFromSPtr(lParam);
5710 unsigned int iChar = 0;
5711 for (; iChar < wParam - 1; iChar++)
5712 ptr[iChar] = pdoc->CharAt(iChar);
5713 ptr[iChar] = '\0';
5714 return iChar;
5717 case SCI_SETTEXT: {
5718 if (lParam == 0)
5719 return 0;
5720 UndoGroup ug(pdoc);
5721 pdoc->DeleteChars(0, pdoc->Length());
5722 SetEmptySelection(0);
5723 const char *text = CharPtrFromSPtr(lParam);
5724 pdoc->InsertString(0, text, istrlen(text));
5725 return 1;
5728 case SCI_GETTEXTLENGTH:
5729 return pdoc->Length();
5731 case SCI_CUT:
5732 Cut();
5733 SetLastXChosen();
5734 break;
5736 case SCI_COPY:
5737 Copy();
5738 break;
5740 case SCI_COPYALLOWLINE:
5741 CopyAllowLine();
5742 break;
5744 case SCI_VERTICALCENTRECARET:
5745 VerticalCentreCaret();
5746 break;
5748 case SCI_MOVESELECTEDLINESUP:
5749 MoveSelectedLinesUp();
5750 break;
5752 case SCI_MOVESELECTEDLINESDOWN:
5753 MoveSelectedLinesDown();
5754 break;
5756 case SCI_COPYRANGE:
5757 CopyRangeToClipboard(static_cast<int>(wParam), static_cast<int>(lParam));
5758 break;
5760 case SCI_COPYTEXT:
5761 CopyText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
5762 break;
5764 case SCI_PASTE:
5765 Paste();
5766 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5767 SetLastXChosen();
5769 EnsureCaretVisible();
5770 break;
5772 case SCI_CLEAR:
5773 Clear();
5774 SetLastXChosen();
5775 EnsureCaretVisible();
5776 break;
5778 case SCI_UNDO:
5779 Undo();
5780 SetLastXChosen();
5781 break;
5783 case SCI_CANUNDO:
5784 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
5786 case SCI_EMPTYUNDOBUFFER:
5787 pdoc->DeleteUndoHistory();
5788 return 0;
5790 case SCI_GETFIRSTVISIBLELINE:
5791 return topLine;
5793 case SCI_SETFIRSTVISIBLELINE:
5794 ScrollTo(static_cast<int>(wParam));
5795 break;
5797 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
5798 int lineStart = pdoc->LineStart(static_cast<int>(wParam));
5799 int lineEnd = pdoc->LineStart(static_cast<int>(wParam + 1));
5800 if (lParam == 0) {
5801 return lineEnd - lineStart;
5803 char *ptr = CharPtrFromSPtr(lParam);
5804 int iPlace = 0;
5805 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
5806 ptr[iPlace++] = pdoc->CharAt(iChar);
5808 return iPlace;
5811 case SCI_GETLINECOUNT:
5812 if (pdoc->LinesTotal() == 0)
5813 return 1;
5814 else
5815 return pdoc->LinesTotal();
5817 case SCI_GETMODIFY:
5818 return !pdoc->IsSavePoint();
5820 case SCI_SETSEL: {
5821 int nStart = static_cast<int>(wParam);
5822 int nEnd = static_cast<int>(lParam);
5823 if (nEnd < 0)
5824 nEnd = pdoc->Length();
5825 if (nStart < 0)
5826 nStart = nEnd; // Remove selection
5827 InvalidateSelection(SelectionRange(nStart, nEnd));
5828 sel.Clear();
5829 sel.selType = Selection::selStream;
5830 SetSelection(nEnd, nStart);
5831 EnsureCaretVisible();
5833 break;
5835 case SCI_GETSELTEXT: {
5836 SelectionText selectedText;
5837 CopySelectionRange(&selectedText);
5838 if (lParam == 0) {
5839 return selectedText.LengthWithTerminator();
5840 } else {
5841 char *ptr = CharPtrFromSPtr(lParam);
5842 unsigned int iChar = 0;
5843 if (selectedText.Length()) {
5844 for (; iChar < selectedText.LengthWithTerminator(); iChar++)
5845 ptr[iChar] = selectedText.Data()[iChar];
5846 } else {
5847 ptr[0] = '\0';
5849 return iChar;
5853 case SCI_LINEFROMPOSITION:
5854 if (static_cast<int>(wParam) < 0)
5855 return 0;
5856 return pdoc->LineFromPosition(static_cast<int>(wParam));
5858 case SCI_POSITIONFROMLINE:
5859 if (static_cast<int>(wParam) < 0)
5860 wParam = pdoc->LineFromPosition(SelectionStart().Position());
5861 if (wParam == 0)
5862 return 0; // Even if there is no text, there is a first line that starts at 0
5863 if (static_cast<int>(wParam) > pdoc->LinesTotal())
5864 return -1;
5865 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
5866 // return -1;
5867 return pdoc->LineStart(static_cast<int>(wParam));
5869 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
5870 case SCI_LINELENGTH:
5871 if ((static_cast<int>(wParam) < 0) ||
5872 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
5873 return 0;
5874 return pdoc->LineStart(static_cast<int>(wParam) + 1) - pdoc->LineStart(static_cast<int>(wParam));
5876 case SCI_REPLACESEL: {
5877 if (lParam == 0)
5878 return 0;
5879 UndoGroup ug(pdoc);
5880 ClearSelection();
5881 char *replacement = CharPtrFromSPtr(lParam);
5882 const int lengthInserted = pdoc->InsertString(
5883 sel.MainCaret(), replacement, istrlen(replacement));
5884 SetEmptySelection(sel.MainCaret() + lengthInserted);
5885 EnsureCaretVisible();
5887 break;
5889 case SCI_SETTARGETSTART:
5890 targetStart = static_cast<int>(wParam);
5891 break;
5893 case SCI_GETTARGETSTART:
5894 return targetStart;
5896 case SCI_SETTARGETEND:
5897 targetEnd = static_cast<int>(wParam);
5898 break;
5900 case SCI_GETTARGETEND:
5901 return targetEnd;
5903 case SCI_SETTARGETRANGE:
5904 targetStart = static_cast<int>(wParam);
5905 targetEnd = static_cast<int>(lParam);
5906 break;
5908 case SCI_TARGETWHOLEDOCUMENT:
5909 targetStart = 0;
5910 targetEnd = pdoc->Length();
5911 break;
5913 case SCI_TARGETFROMSELECTION:
5914 if (sel.MainCaret() < sel.MainAnchor()) {
5915 targetStart = sel.MainCaret();
5916 targetEnd = sel.MainAnchor();
5917 } else {
5918 targetStart = sel.MainAnchor();
5919 targetEnd = sel.MainCaret();
5921 break;
5923 case SCI_GETTARGETTEXT: {
5924 std::string text = RangeText(targetStart, targetEnd);
5925 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(text.c_str()), text.length());
5928 case SCI_REPLACETARGET:
5929 PLATFORM_ASSERT(lParam);
5930 return ReplaceTarget(false, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5932 case SCI_REPLACETARGETRE:
5933 PLATFORM_ASSERT(lParam);
5934 return ReplaceTarget(true, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5936 case SCI_SEARCHINTARGET:
5937 PLATFORM_ASSERT(lParam);
5938 return SearchInTarget(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5940 case SCI_SETSEARCHFLAGS:
5941 searchFlags = static_cast<int>(wParam);
5942 break;
5944 case SCI_GETSEARCHFLAGS:
5945 return searchFlags;
5947 case SCI_GETTAG:
5948 return GetTag(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5950 case SCI_POSITIONBEFORE:
5951 return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) - 1, -1, true);
5953 case SCI_POSITIONAFTER:
5954 return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) + 1, 1, true);
5956 case SCI_POSITIONRELATIVE:
5957 return Platform::Clamp(pdoc->GetRelativePosition(static_cast<int>(wParam), static_cast<int>(lParam)), 0, pdoc->Length());
5959 case SCI_LINESCROLL:
5960 ScrollTo(topLine + static_cast<int>(lParam));
5961 HorizontalScrollTo(xOffset + static_cast<int>(wParam)* static_cast<int>(vs.spaceWidth));
5962 return 1;
5964 case SCI_SETXOFFSET:
5965 xOffset = static_cast<int>(wParam);
5966 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
5967 SetHorizontalScrollPos();
5968 Redraw();
5969 break;
5971 case SCI_GETXOFFSET:
5972 return xOffset;
5974 case SCI_CHOOSECARETX:
5975 SetLastXChosen();
5976 break;
5978 case SCI_SCROLLCARET:
5979 EnsureCaretVisible();
5980 break;
5982 case SCI_SETREADONLY:
5983 pdoc->SetReadOnly(wParam != 0);
5984 return 1;
5986 case SCI_GETREADONLY:
5987 return pdoc->IsReadOnly();
5989 case SCI_CANPASTE:
5990 return CanPaste();
5992 case SCI_POINTXFROMPOSITION:
5993 if (lParam < 0) {
5994 return 0;
5995 } else {
5996 Point pt = LocationFromPosition(static_cast<int>(lParam));
5997 // Convert to view-relative
5998 return static_cast<int>(pt.x) - vs.textStart + vs.fixedColumnWidth;
6001 case SCI_POINTYFROMPOSITION:
6002 if (lParam < 0) {
6003 return 0;
6004 } else {
6005 Point pt = LocationFromPosition(static_cast<int>(lParam));
6006 return static_cast<int>(pt.y);
6009 case SCI_FINDTEXT:
6010 return FindText(wParam, lParam);
6012 case SCI_GETTEXTRANGE: {
6013 if (lParam == 0)
6014 return 0;
6015 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
6016 int cpMax = static_cast<int>(tr->chrg.cpMax);
6017 if (cpMax == -1)
6018 cpMax = pdoc->Length();
6019 PLATFORM_ASSERT(cpMax <= pdoc->Length());
6020 int len = static_cast<int>(cpMax - tr->chrg.cpMin); // No -1 as cpMin and cpMax are referring to inter character positions
6021 pdoc->GetCharRange(tr->lpstrText, static_cast<int>(tr->chrg.cpMin), len);
6022 // Spec says copied text is terminated with a NUL
6023 tr->lpstrText[len] = '\0';
6024 return len; // Not including NUL
6027 case SCI_HIDESELECTION:
6028 view.hideSelection = wParam != 0;
6029 Redraw();
6030 break;
6032 case SCI_FORMATRANGE:
6033 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
6035 case SCI_GETMARGINLEFT:
6036 return vs.leftMarginWidth;
6038 case SCI_GETMARGINRIGHT:
6039 return vs.rightMarginWidth;
6041 case SCI_SETMARGINLEFT:
6042 lastXChosen += static_cast<int>(lParam) - vs.leftMarginWidth;
6043 vs.leftMarginWidth = static_cast<int>(lParam);
6044 InvalidateStyleRedraw();
6045 break;
6047 case SCI_SETMARGINRIGHT:
6048 vs.rightMarginWidth = static_cast<int>(lParam);
6049 InvalidateStyleRedraw();
6050 break;
6052 // Control specific mesages
6054 case SCI_ADDTEXT: {
6055 if (lParam == 0)
6056 return 0;
6057 const int lengthInserted = pdoc->InsertString(
6058 CurrentPosition(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
6059 SetEmptySelection(sel.MainCaret() + lengthInserted);
6060 return 0;
6063 case SCI_ADDSTYLEDTEXT:
6064 if (lParam)
6065 AddStyledText(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
6066 return 0;
6068 case SCI_INSERTTEXT: {
6069 if (lParam == 0)
6070 return 0;
6071 int insertPos = static_cast<int>(wParam);
6072 if (static_cast<int>(wParam) == -1)
6073 insertPos = CurrentPosition();
6074 int newCurrent = CurrentPosition();
6075 char *sz = CharPtrFromSPtr(lParam);
6076 const int lengthInserted = pdoc->InsertString(insertPos, sz, istrlen(sz));
6077 if (newCurrent > insertPos)
6078 newCurrent += lengthInserted;
6079 SetEmptySelection(newCurrent);
6080 return 0;
6083 case SCI_CHANGEINSERTION:
6084 PLATFORM_ASSERT(lParam);
6085 pdoc->ChangeInsertion(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
6086 return 0;
6088 case SCI_APPENDTEXT:
6089 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
6090 return 0;
6092 case SCI_CLEARALL:
6093 ClearAll();
6094 return 0;
6096 case SCI_DELETERANGE:
6097 pdoc->DeleteChars(static_cast<int>(wParam), static_cast<int>(lParam));
6098 return 0;
6100 case SCI_CLEARDOCUMENTSTYLE:
6101 ClearDocumentStyle();
6102 return 0;
6104 case SCI_SETUNDOCOLLECTION:
6105 pdoc->SetUndoCollection(wParam != 0);
6106 return 0;
6108 case SCI_GETUNDOCOLLECTION:
6109 return pdoc->IsCollectingUndo();
6111 case SCI_BEGINUNDOACTION:
6112 pdoc->BeginUndoAction();
6113 return 0;
6115 case SCI_ENDUNDOACTION:
6116 pdoc->EndUndoAction();
6117 return 0;
6119 case SCI_GETCARETPERIOD:
6120 return caret.period;
6122 case SCI_SETCARETPERIOD:
6123 CaretSetPeriod(static_cast<int>(wParam));
6124 break;
6126 case SCI_GETWORDCHARS:
6127 return pdoc->GetCharsOfClass(CharClassify::ccWord, reinterpret_cast<unsigned char *>(lParam));
6129 case SCI_SETWORDCHARS: {
6130 pdoc->SetDefaultCharClasses(false);
6131 if (lParam == 0)
6132 return 0;
6133 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
6135 break;
6137 case SCI_GETWHITESPACECHARS:
6138 return pdoc->GetCharsOfClass(CharClassify::ccSpace, reinterpret_cast<unsigned char *>(lParam));
6140 case SCI_SETWHITESPACECHARS: {
6141 if (lParam == 0)
6142 return 0;
6143 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
6145 break;
6147 case SCI_GETPUNCTUATIONCHARS:
6148 return pdoc->GetCharsOfClass(CharClassify::ccPunctuation, reinterpret_cast<unsigned char *>(lParam));
6150 case SCI_SETPUNCTUATIONCHARS: {
6151 if (lParam == 0)
6152 return 0;
6153 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccPunctuation);
6155 break;
6157 case SCI_SETCHARSDEFAULT:
6158 pdoc->SetDefaultCharClasses(true);
6159 break;
6161 case SCI_GETLENGTH:
6162 return pdoc->Length();
6164 case SCI_ALLOCATE:
6165 pdoc->Allocate(static_cast<int>(wParam));
6166 break;
6168 case SCI_GETCHARAT:
6169 return pdoc->CharAt(static_cast<int>(wParam));
6171 case SCI_SETCURRENTPOS:
6172 if (sel.IsRectangular()) {
6173 sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
6174 SetRectangularRange();
6175 Redraw();
6176 } else {
6177 SetSelection(static_cast<int>(wParam), sel.MainAnchor());
6179 break;
6181 case SCI_GETCURRENTPOS:
6182 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
6184 case SCI_SETANCHOR:
6185 if (sel.IsRectangular()) {
6186 sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
6187 SetRectangularRange();
6188 Redraw();
6189 } else {
6190 SetSelection(sel.MainCaret(), static_cast<int>(wParam));
6192 break;
6194 case SCI_GETANCHOR:
6195 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
6197 case SCI_SETSELECTIONSTART:
6198 SetSelection(Platform::Maximum(sel.MainCaret(), static_cast<int>(wParam)), static_cast<int>(wParam));
6199 break;
6201 case SCI_GETSELECTIONSTART:
6202 return sel.LimitsForRectangularElseMain().start.Position();
6204 case SCI_SETSELECTIONEND:
6205 SetSelection(static_cast<int>(wParam), Platform::Minimum(sel.MainAnchor(), static_cast<int>(wParam)));
6206 break;
6208 case SCI_GETSELECTIONEND:
6209 return sel.LimitsForRectangularElseMain().end.Position();
6211 case SCI_SETEMPTYSELECTION:
6212 SetEmptySelection(static_cast<int>(wParam));
6213 break;
6215 case SCI_SETPRINTMAGNIFICATION:
6216 view.printParameters.magnification = static_cast<int>(wParam);
6217 break;
6219 case SCI_GETPRINTMAGNIFICATION:
6220 return view.printParameters.magnification;
6222 case SCI_SETPRINTCOLOURMODE:
6223 view.printParameters.colourMode = static_cast<int>(wParam);
6224 break;
6226 case SCI_GETPRINTCOLOURMODE:
6227 return view.printParameters.colourMode;
6229 case SCI_SETPRINTWRAPMODE:
6230 view.printParameters.wrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
6231 break;
6233 case SCI_GETPRINTWRAPMODE:
6234 return view.printParameters.wrapState;
6236 case SCI_GETSTYLEAT:
6237 if (static_cast<int>(wParam) >= pdoc->Length())
6238 return 0;
6239 else
6240 return pdoc->StyleAt(static_cast<int>(wParam));
6242 case SCI_REDO:
6243 Redo();
6244 break;
6246 case SCI_SELECTALL:
6247 SelectAll();
6248 break;
6250 case SCI_SETSAVEPOINT:
6251 pdoc->SetSavePoint();
6252 break;
6254 case SCI_GETSTYLEDTEXT: {
6255 if (lParam == 0)
6256 return 0;
6257 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
6258 int iPlace = 0;
6259 for (long iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
6260 tr->lpstrText[iPlace++] = pdoc->CharAt(static_cast<int>(iChar));
6261 tr->lpstrText[iPlace++] = pdoc->StyleAt(static_cast<int>(iChar));
6263 tr->lpstrText[iPlace] = '\0';
6264 tr->lpstrText[iPlace + 1] = '\0';
6265 return iPlace;
6268 case SCI_CANREDO:
6269 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
6271 case SCI_MARKERLINEFROMHANDLE:
6272 return pdoc->LineFromHandle(static_cast<int>(wParam));
6274 case SCI_MARKERDELETEHANDLE:
6275 pdoc->DeleteMarkFromHandle(static_cast<int>(wParam));
6276 break;
6278 case SCI_GETVIEWWS:
6279 return vs.viewWhitespace;
6281 case SCI_SETVIEWWS:
6282 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
6283 Redraw();
6284 break;
6286 case SCI_GETWHITESPACESIZE:
6287 return vs.whitespaceSize;
6289 case SCI_SETWHITESPACESIZE:
6290 vs.whitespaceSize = static_cast<int>(wParam);
6291 Redraw();
6292 break;
6294 case SCI_POSITIONFROMPOINT:
6295 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6296 false, false);
6298 case SCI_POSITIONFROMPOINTCLOSE:
6299 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6300 true, false);
6302 case SCI_CHARPOSITIONFROMPOINT:
6303 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6304 false, true);
6306 case SCI_CHARPOSITIONFROMPOINTCLOSE:
6307 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6308 true, true);
6310 case SCI_GOTOLINE:
6311 GoToLine(static_cast<int>(wParam));
6312 break;
6314 case SCI_GOTOPOS:
6315 SetEmptySelection(static_cast<int>(wParam));
6316 EnsureCaretVisible();
6317 break;
6319 case SCI_GETCURLINE: {
6320 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
6321 int lineStart = pdoc->LineStart(lineCurrentPos);
6322 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
6323 if (lParam == 0) {
6324 return 1 + lineEnd - lineStart;
6326 PLATFORM_ASSERT(wParam > 0);
6327 char *ptr = CharPtrFromSPtr(lParam);
6328 unsigned int iPlace = 0;
6329 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
6330 ptr[iPlace++] = pdoc->CharAt(iChar);
6332 ptr[iPlace] = '\0';
6333 return sel.MainCaret() - lineStart;
6336 case SCI_GETENDSTYLED:
6337 return pdoc->GetEndStyled();
6339 case SCI_GETEOLMODE:
6340 return pdoc->eolMode;
6342 case SCI_SETEOLMODE:
6343 pdoc->eolMode = static_cast<int>(wParam);
6344 break;
6346 case SCI_SETLINEENDTYPESALLOWED:
6347 if (pdoc->SetLineEndTypesAllowed(static_cast<int>(wParam))) {
6348 cs.Clear();
6349 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6350 SetAnnotationHeights(0, pdoc->LinesTotal());
6351 InvalidateStyleRedraw();
6353 break;
6355 case SCI_GETLINEENDTYPESALLOWED:
6356 return pdoc->GetLineEndTypesAllowed();
6358 case SCI_GETLINEENDTYPESACTIVE:
6359 return pdoc->GetLineEndTypesActive();
6361 case SCI_STARTSTYLING:
6362 pdoc->StartStyling(static_cast<int>(wParam), static_cast<char>(lParam));
6363 break;
6365 case SCI_SETSTYLING:
6366 if (static_cast<int>(wParam) < 0)
6367 errorStatus = SC_STATUS_FAILURE;
6368 else
6369 pdoc->SetStyleFor(static_cast<int>(wParam), static_cast<char>(lParam));
6370 break;
6372 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
6373 if (lParam == 0)
6374 return 0;
6375 pdoc->SetStyles(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
6376 break;
6378 case SCI_SETBUFFEREDDRAW:
6379 view.bufferedDraw = wParam != 0;
6380 break;
6382 case SCI_GETBUFFEREDDRAW:
6383 return view.bufferedDraw;
6385 case SCI_GETTWOPHASEDRAW:
6386 return view.phasesDraw == EditView::phasesTwo;
6388 case SCI_SETTWOPHASEDRAW:
6389 if (view.SetTwoPhaseDraw(wParam != 0))
6390 InvalidateStyleRedraw();
6391 break;
6393 case SCI_GETPHASESDRAW:
6394 return view.phasesDraw;
6396 case SCI_SETPHASESDRAW:
6397 if (view.SetPhasesDraw(static_cast<int>(wParam)))
6398 InvalidateStyleRedraw();
6399 break;
6401 case SCI_SETFONTQUALITY:
6402 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
6403 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
6404 InvalidateStyleRedraw();
6405 break;
6407 case SCI_GETFONTQUALITY:
6408 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
6410 case SCI_SETTABWIDTH:
6411 if (wParam > 0) {
6412 pdoc->tabInChars = static_cast<int>(wParam);
6413 if (pdoc->indentInChars == 0)
6414 pdoc->actualIndentInChars = pdoc->tabInChars;
6416 InvalidateStyleRedraw();
6417 break;
6419 case SCI_GETTABWIDTH:
6420 return pdoc->tabInChars;
6422 case SCI_CLEARTABSTOPS:
6423 if (view.ClearTabstops(static_cast<int>(wParam))) {
6424 DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, 0, static_cast<int>(wParam));
6425 NotifyModified(pdoc, mh, NULL);
6427 break;
6429 case SCI_ADDTABSTOP:
6430 if (view.AddTabstop(static_cast<int>(wParam), static_cast<int>(lParam))) {
6431 DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, 0, static_cast<int>(wParam));
6432 NotifyModified(pdoc, mh, NULL);
6434 break;
6436 case SCI_GETNEXTTABSTOP:
6437 return view.GetNextTabstop(static_cast<int>(wParam), static_cast<int>(lParam));
6439 case SCI_SETINDENT:
6440 pdoc->indentInChars = static_cast<int>(wParam);
6441 if (pdoc->indentInChars != 0)
6442 pdoc->actualIndentInChars = pdoc->indentInChars;
6443 else
6444 pdoc->actualIndentInChars = pdoc->tabInChars;
6445 InvalidateStyleRedraw();
6446 break;
6448 case SCI_GETINDENT:
6449 return pdoc->indentInChars;
6451 case SCI_SETUSETABS:
6452 pdoc->useTabs = wParam != 0;
6453 InvalidateStyleRedraw();
6454 break;
6456 case SCI_GETUSETABS:
6457 return pdoc->useTabs;
6459 case SCI_SETLINEINDENTATION:
6460 pdoc->SetLineIndentation(static_cast<int>(wParam), static_cast<int>(lParam));
6461 break;
6463 case SCI_GETLINEINDENTATION:
6464 return pdoc->GetLineIndentation(static_cast<int>(wParam));
6466 case SCI_GETLINEINDENTPOSITION:
6467 return pdoc->GetLineIndentPosition(static_cast<int>(wParam));
6469 case SCI_SETTABINDENTS:
6470 pdoc->tabIndents = wParam != 0;
6471 break;
6473 case SCI_GETTABINDENTS:
6474 return pdoc->tabIndents;
6476 case SCI_SETBACKSPACEUNINDENTS:
6477 pdoc->backspaceUnindents = wParam != 0;
6478 break;
6480 case SCI_GETBACKSPACEUNINDENTS:
6481 return pdoc->backspaceUnindents;
6483 case SCI_SETMOUSEDWELLTIME:
6484 dwellDelay = static_cast<int>(wParam);
6485 ticksToDwell = dwellDelay;
6486 break;
6488 case SCI_GETMOUSEDWELLTIME:
6489 return dwellDelay;
6491 case SCI_WORDSTARTPOSITION:
6492 return pdoc->ExtendWordSelect(static_cast<int>(wParam), -1, lParam != 0);
6494 case SCI_WORDENDPOSITION:
6495 return pdoc->ExtendWordSelect(static_cast<int>(wParam), 1, lParam != 0);
6497 case SCI_ISRANGEWORD:
6498 return pdoc->IsWordAt(static_cast<int>(wParam), static_cast<int>(lParam));
6500 case SCI_SETIDLESTYLING:
6501 idleStyling = static_cast<int>(wParam);
6502 break;
6504 case SCI_GETIDLESTYLING:
6505 return idleStyling;
6507 case SCI_SETWRAPMODE:
6508 if (vs.SetWrapState(static_cast<int>(wParam))) {
6509 xOffset = 0;
6510 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
6511 InvalidateStyleRedraw();
6512 ReconfigureScrollBars();
6514 break;
6516 case SCI_GETWRAPMODE:
6517 return vs.wrapState;
6519 case SCI_SETWRAPVISUALFLAGS:
6520 if (vs.SetWrapVisualFlags(static_cast<int>(wParam))) {
6521 InvalidateStyleRedraw();
6522 ReconfigureScrollBars();
6524 break;
6526 case SCI_GETWRAPVISUALFLAGS:
6527 return vs.wrapVisualFlags;
6529 case SCI_SETWRAPVISUALFLAGSLOCATION:
6530 if (vs.SetWrapVisualFlagsLocation(static_cast<int>(wParam))) {
6531 InvalidateStyleRedraw();
6533 break;
6535 case SCI_GETWRAPVISUALFLAGSLOCATION:
6536 return vs.wrapVisualFlagsLocation;
6538 case SCI_SETWRAPSTARTINDENT:
6539 if (vs.SetWrapVisualStartIndent(static_cast<int>(wParam))) {
6540 InvalidateStyleRedraw();
6541 ReconfigureScrollBars();
6543 break;
6545 case SCI_GETWRAPSTARTINDENT:
6546 return vs.wrapVisualStartIndent;
6548 case SCI_SETWRAPINDENTMODE:
6549 if (vs.SetWrapIndentMode(static_cast<int>(wParam))) {
6550 InvalidateStyleRedraw();
6551 ReconfigureScrollBars();
6553 break;
6555 case SCI_GETWRAPINDENTMODE:
6556 return vs.wrapIndentMode;
6558 case SCI_SETLAYOUTCACHE:
6559 view.llc.SetLevel(static_cast<int>(wParam));
6560 break;
6562 case SCI_GETLAYOUTCACHE:
6563 return view.llc.GetLevel();
6565 case SCI_SETPOSITIONCACHE:
6566 view.posCache.SetSize(wParam);
6567 break;
6569 case SCI_GETPOSITIONCACHE:
6570 return view.posCache.GetSize();
6572 case SCI_SETSCROLLWIDTH:
6573 PLATFORM_ASSERT(wParam > 0);
6574 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
6575 view.lineWidthMaxSeen = 0;
6576 scrollWidth = static_cast<int>(wParam);
6577 SetScrollBars();
6579 break;
6581 case SCI_GETSCROLLWIDTH:
6582 return scrollWidth;
6584 case SCI_SETSCROLLWIDTHTRACKING:
6585 trackLineWidth = wParam != 0;
6586 break;
6588 case SCI_GETSCROLLWIDTHTRACKING:
6589 return trackLineWidth;
6591 case SCI_LINESJOIN:
6592 LinesJoin();
6593 break;
6595 case SCI_LINESSPLIT:
6596 LinesSplit(static_cast<int>(wParam));
6597 break;
6599 case SCI_TEXTWIDTH:
6600 PLATFORM_ASSERT(wParam < vs.styles.size());
6601 PLATFORM_ASSERT(lParam);
6602 return TextWidth(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
6604 case SCI_TEXTHEIGHT:
6605 RefreshStyleData();
6606 return vs.lineHeight;
6608 case SCI_SETENDATLASTLINE:
6609 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
6610 if (endAtLastLine != (wParam != 0)) {
6611 endAtLastLine = wParam != 0;
6612 SetScrollBars();
6614 break;
6616 case SCI_GETENDATLASTLINE:
6617 return endAtLastLine;
6619 case SCI_SETCARETSTICKY:
6620 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
6621 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
6622 caretSticky = static_cast<int>(wParam);
6624 break;
6626 case SCI_GETCARETSTICKY:
6627 return caretSticky;
6629 case SCI_TOGGLECARETSTICKY:
6630 caretSticky = !caretSticky;
6631 break;
6633 case SCI_GETCOLUMN:
6634 return pdoc->GetColumn(static_cast<int>(wParam));
6636 case SCI_FINDCOLUMN:
6637 return pdoc->FindColumn(static_cast<int>(wParam), static_cast<int>(lParam));
6639 case SCI_SETHSCROLLBAR :
6640 if (horizontalScrollBarVisible != (wParam != 0)) {
6641 horizontalScrollBarVisible = wParam != 0;
6642 SetScrollBars();
6643 ReconfigureScrollBars();
6645 break;
6647 case SCI_GETHSCROLLBAR:
6648 return horizontalScrollBarVisible;
6650 case SCI_SETVSCROLLBAR:
6651 if (verticalScrollBarVisible != (wParam != 0)) {
6652 verticalScrollBarVisible = wParam != 0;
6653 SetScrollBars();
6654 ReconfigureScrollBars();
6655 if (verticalScrollBarVisible)
6656 SetVerticalScrollPos();
6658 break;
6660 case SCI_GETVSCROLLBAR:
6661 return verticalScrollBarVisible;
6663 case SCI_SETINDENTATIONGUIDES:
6664 vs.viewIndentationGuides = IndentView(wParam);
6665 Redraw();
6666 break;
6668 case SCI_GETINDENTATIONGUIDES:
6669 return vs.viewIndentationGuides;
6671 case SCI_SETHIGHLIGHTGUIDE:
6672 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
6673 highlightGuideColumn = static_cast<int>(wParam);
6674 Redraw();
6676 break;
6678 case SCI_GETHIGHLIGHTGUIDE:
6679 return highlightGuideColumn;
6681 case SCI_GETLINEENDPOSITION:
6682 return pdoc->LineEnd(static_cast<int>(wParam));
6684 case SCI_SETCODEPAGE:
6685 if (ValidCodePage(static_cast<int>(wParam))) {
6686 if (pdoc->SetDBCSCodePage(static_cast<int>(wParam))) {
6687 cs.Clear();
6688 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6689 SetAnnotationHeights(0, pdoc->LinesTotal());
6690 InvalidateStyleRedraw();
6691 SetRepresentations();
6694 break;
6696 case SCI_GETCODEPAGE:
6697 return pdoc->dbcsCodePage;
6699 case SCI_SETIMEINTERACTION:
6700 imeInteraction = static_cast<EditModel::IMEInteraction>(wParam);
6701 break;
6703 case SCI_GETIMEINTERACTION:
6704 return imeInteraction;
6706 #ifdef INCLUDE_DEPRECATED_FEATURES
6707 case SCI_SETUSEPALETTE:
6708 InvalidateStyleRedraw();
6709 break;
6711 case SCI_GETUSEPALETTE:
6712 return 0;
6713 #endif
6715 // Marker definition and setting
6716 case SCI_MARKERDEFINE:
6717 if (wParam <= MARKER_MAX) {
6718 vs.markers[wParam].markType = static_cast<int>(lParam);
6719 vs.CalcLargestMarkerHeight();
6721 InvalidateStyleData();
6722 RedrawSelMargin();
6723 break;
6725 case SCI_MARKERSYMBOLDEFINED:
6726 if (wParam <= MARKER_MAX)
6727 return vs.markers[wParam].markType;
6728 else
6729 return 0;
6731 case SCI_MARKERSETFORE:
6732 if (wParam <= MARKER_MAX)
6733 vs.markers[wParam].fore = ColourDesired(static_cast<long>(lParam));
6734 InvalidateStyleData();
6735 RedrawSelMargin();
6736 break;
6737 case SCI_MARKERSETBACKSELECTED:
6738 if (wParam <= MARKER_MAX)
6739 vs.markers[wParam].backSelected = ColourDesired(static_cast<long>(lParam));
6740 InvalidateStyleData();
6741 RedrawSelMargin();
6742 break;
6743 case SCI_MARKERENABLEHIGHLIGHT:
6744 marginView.highlightDelimiter.isEnabled = wParam == 1;
6745 RedrawSelMargin();
6746 break;
6747 case SCI_MARKERSETBACK:
6748 if (wParam <= MARKER_MAX)
6749 vs.markers[wParam].back = ColourDesired(static_cast<long>(lParam));
6750 InvalidateStyleData();
6751 RedrawSelMargin();
6752 break;
6753 case SCI_MARKERSETALPHA:
6754 if (wParam <= MARKER_MAX)
6755 vs.markers[wParam].alpha = static_cast<int>(lParam);
6756 InvalidateStyleRedraw();
6757 break;
6758 case SCI_MARKERADD: {
6759 int markerID = pdoc->AddMark(static_cast<int>(wParam), static_cast<int>(lParam));
6760 return markerID;
6762 case SCI_MARKERADDSET:
6763 if (lParam != 0)
6764 pdoc->AddMarkSet(static_cast<int>(wParam), static_cast<int>(lParam));
6765 break;
6767 case SCI_MARKERDELETE:
6768 pdoc->DeleteMark(static_cast<int>(wParam), static_cast<int>(lParam));
6769 break;
6771 case SCI_MARKERDELETEALL:
6772 pdoc->DeleteAllMarks(static_cast<int>(wParam));
6773 break;
6775 case SCI_MARKERGET:
6776 return pdoc->GetMark(static_cast<int>(wParam));
6778 case SCI_MARKERNEXT:
6779 return pdoc->MarkerNext(static_cast<int>(wParam), static_cast<int>(lParam));
6781 case SCI_MARKERPREVIOUS: {
6782 for (int iLine = static_cast<int>(wParam); iLine >= 0; iLine--) {
6783 if ((pdoc->GetMark(iLine) & lParam) != 0)
6784 return iLine;
6787 return -1;
6789 case SCI_MARKERDEFINEPIXMAP:
6790 if (wParam <= MARKER_MAX) {
6791 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
6792 vs.CalcLargestMarkerHeight();
6794 InvalidateStyleData();
6795 RedrawSelMargin();
6796 break;
6798 case SCI_RGBAIMAGESETWIDTH:
6799 sizeRGBAImage.x = static_cast<XYPOSITION>(wParam);
6800 break;
6802 case SCI_RGBAIMAGESETHEIGHT:
6803 sizeRGBAImage.y = static_cast<XYPOSITION>(wParam);
6804 break;
6806 case SCI_RGBAIMAGESETSCALE:
6807 scaleRGBAImage = static_cast<float>(wParam);
6808 break;
6810 case SCI_MARKERDEFINERGBAIMAGE:
6811 if (wParam <= MARKER_MAX) {
6812 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0f, reinterpret_cast<unsigned char *>(lParam));
6813 vs.CalcLargestMarkerHeight();
6815 InvalidateStyleData();
6816 RedrawSelMargin();
6817 break;
6819 case SCI_SETMARGINTYPEN:
6820 if (ValidMargin(wParam)) {
6821 vs.ms[wParam].style = static_cast<int>(lParam);
6822 InvalidateStyleRedraw();
6824 break;
6826 case SCI_GETMARGINTYPEN:
6827 if (ValidMargin(wParam))
6828 return vs.ms[wParam].style;
6829 else
6830 return 0;
6832 case SCI_SETMARGINWIDTHN:
6833 if (ValidMargin(wParam)) {
6834 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
6835 if (vs.ms[wParam].width != lParam) {
6836 lastXChosen += static_cast<int>(lParam) - vs.ms[wParam].width;
6837 vs.ms[wParam].width = static_cast<int>(lParam);
6838 InvalidateStyleRedraw();
6841 break;
6843 case SCI_GETMARGINWIDTHN:
6844 if (ValidMargin(wParam))
6845 return vs.ms[wParam].width;
6846 else
6847 return 0;
6849 case SCI_SETMARGINMASKN:
6850 if (ValidMargin(wParam)) {
6851 vs.ms[wParam].mask = static_cast<int>(lParam);
6852 InvalidateStyleRedraw();
6854 break;
6856 case SCI_GETMARGINMASKN:
6857 if (ValidMargin(wParam))
6858 return vs.ms[wParam].mask;
6859 else
6860 return 0;
6862 case SCI_SETMARGINSENSITIVEN:
6863 if (ValidMargin(wParam)) {
6864 vs.ms[wParam].sensitive = lParam != 0;
6865 InvalidateStyleRedraw();
6867 break;
6869 case SCI_GETMARGINSENSITIVEN:
6870 if (ValidMargin(wParam))
6871 return vs.ms[wParam].sensitive ? 1 : 0;
6872 else
6873 return 0;
6875 case SCI_SETMARGINCURSORN:
6876 if (ValidMargin(wParam))
6877 vs.ms[wParam].cursor = static_cast<int>(lParam);
6878 break;
6880 case SCI_GETMARGINCURSORN:
6881 if (ValidMargin(wParam))
6882 return vs.ms[wParam].cursor;
6883 else
6884 return 0;
6886 case SCI_STYLECLEARALL:
6887 vs.ClearStyles();
6888 InvalidateStyleRedraw();
6889 break;
6891 case SCI_STYLESETFORE:
6892 case SCI_STYLESETBACK:
6893 case SCI_STYLESETBOLD:
6894 case SCI_STYLESETWEIGHT:
6895 case SCI_STYLESETITALIC:
6896 case SCI_STYLESETEOLFILLED:
6897 case SCI_STYLESETSIZE:
6898 case SCI_STYLESETSIZEFRACTIONAL:
6899 case SCI_STYLESETFONT:
6900 case SCI_STYLESETUNDERLINE:
6901 case SCI_STYLESETCASE:
6902 case SCI_STYLESETCHARACTERSET:
6903 case SCI_STYLESETVISIBLE:
6904 case SCI_STYLESETCHANGEABLE:
6905 case SCI_STYLESETHOTSPOT:
6906 StyleSetMessage(iMessage, wParam, lParam);
6907 break;
6909 case SCI_STYLEGETFORE:
6910 case SCI_STYLEGETBACK:
6911 case SCI_STYLEGETBOLD:
6912 case SCI_STYLEGETWEIGHT:
6913 case SCI_STYLEGETITALIC:
6914 case SCI_STYLEGETEOLFILLED:
6915 case SCI_STYLEGETSIZE:
6916 case SCI_STYLEGETSIZEFRACTIONAL:
6917 case SCI_STYLEGETFONT:
6918 case SCI_STYLEGETUNDERLINE:
6919 case SCI_STYLEGETCASE:
6920 case SCI_STYLEGETCHARACTERSET:
6921 case SCI_STYLEGETVISIBLE:
6922 case SCI_STYLEGETCHANGEABLE:
6923 case SCI_STYLEGETHOTSPOT:
6924 return StyleGetMessage(iMessage, wParam, lParam);
6926 case SCI_STYLERESETDEFAULT:
6927 vs.ResetDefaultStyle();
6928 InvalidateStyleRedraw();
6929 break;
6930 case SCI_SETSTYLEBITS:
6931 vs.EnsureStyle(0xff);
6932 break;
6934 case SCI_GETSTYLEBITS:
6935 return 8;
6937 case SCI_SETLINESTATE:
6938 return pdoc->SetLineState(static_cast<int>(wParam), static_cast<int>(lParam));
6940 case SCI_GETLINESTATE:
6941 return pdoc->GetLineState(static_cast<int>(wParam));
6943 case SCI_GETMAXLINESTATE:
6944 return pdoc->GetMaxLineState();
6946 case SCI_GETCARETLINEVISIBLE:
6947 return vs.showCaretLineBackground;
6948 case SCI_SETCARETLINEVISIBLE:
6949 vs.showCaretLineBackground = wParam != 0;
6950 InvalidateStyleRedraw();
6951 break;
6952 case SCI_GETCARETLINEVISIBLEALWAYS:
6953 return vs.alwaysShowCaretLineBackground;
6954 case SCI_SETCARETLINEVISIBLEALWAYS:
6955 vs.alwaysShowCaretLineBackground = wParam != 0;
6956 InvalidateStyleRedraw();
6957 break;
6959 case SCI_GETCARETLINEBACK:
6960 return vs.caretLineBackground.AsLong();
6961 case SCI_SETCARETLINEBACK:
6962 vs.caretLineBackground = static_cast<int>(wParam);
6963 InvalidateStyleRedraw();
6964 break;
6965 case SCI_GETCARETLINEBACKALPHA:
6966 return vs.caretLineAlpha;
6967 case SCI_SETCARETLINEBACKALPHA:
6968 vs.caretLineAlpha = static_cast<int>(wParam);
6969 InvalidateStyleRedraw();
6970 break;
6972 // Folding messages
6974 case SCI_VISIBLEFROMDOCLINE:
6975 return cs.DisplayFromDoc(static_cast<int>(wParam));
6977 case SCI_DOCLINEFROMVISIBLE:
6978 return cs.DocFromDisplay(static_cast<int>(wParam));
6980 case SCI_WRAPCOUNT:
6981 return WrapCount(static_cast<int>(wParam));
6983 case SCI_SETFOLDLEVEL: {
6984 int prev = pdoc->SetLevel(static_cast<int>(wParam), static_cast<int>(lParam));
6985 if (prev != static_cast<int>(lParam))
6986 RedrawSelMargin();
6987 return prev;
6990 case SCI_GETFOLDLEVEL:
6991 return pdoc->GetLevel(static_cast<int>(wParam));
6993 case SCI_GETLASTCHILD:
6994 return pdoc->GetLastChild(static_cast<int>(wParam), static_cast<int>(lParam));
6996 case SCI_GETFOLDPARENT:
6997 return pdoc->GetFoldParent(static_cast<int>(wParam));
6999 case SCI_SHOWLINES:
7000 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), true);
7001 SetScrollBars();
7002 Redraw();
7003 break;
7005 case SCI_HIDELINES:
7006 if (wParam > 0)
7007 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), false);
7008 SetScrollBars();
7009 Redraw();
7010 break;
7012 case SCI_GETLINEVISIBLE:
7013 return cs.GetVisible(static_cast<int>(wParam));
7015 case SCI_GETALLLINESVISIBLE:
7016 return cs.HiddenLines() ? 0 : 1;
7018 case SCI_SETFOLDEXPANDED:
7019 SetFoldExpanded(static_cast<int>(wParam), lParam != 0);
7020 break;
7022 case SCI_GETFOLDEXPANDED:
7023 return cs.GetExpanded(static_cast<int>(wParam));
7025 case SCI_SETAUTOMATICFOLD:
7026 foldAutomatic = static_cast<int>(wParam);
7027 break;
7029 case SCI_GETAUTOMATICFOLD:
7030 return foldAutomatic;
7032 case SCI_SETFOLDFLAGS:
7033 foldFlags = static_cast<int>(wParam);
7034 Redraw();
7035 break;
7037 case SCI_TOGGLEFOLD:
7038 FoldLine(static_cast<int>(wParam), SC_FOLDACTION_TOGGLE);
7039 break;
7041 case SCI_FOLDLINE:
7042 FoldLine(static_cast<int>(wParam), static_cast<int>(lParam));
7043 break;
7045 case SCI_FOLDCHILDREN:
7046 FoldExpand(static_cast<int>(wParam), static_cast<int>(lParam), pdoc->GetLevel(static_cast<int>(wParam)));
7047 break;
7049 case SCI_FOLDALL:
7050 FoldAll(static_cast<int>(wParam));
7051 break;
7053 case SCI_EXPANDCHILDREN:
7054 FoldExpand(static_cast<int>(wParam), SC_FOLDACTION_EXPAND, static_cast<int>(lParam));
7055 break;
7057 case SCI_CONTRACTEDFOLDNEXT:
7058 return ContractedFoldNext(static_cast<int>(wParam));
7060 case SCI_ENSUREVISIBLE:
7061 EnsureLineVisible(static_cast<int>(wParam), false);
7062 break;
7064 case SCI_ENSUREVISIBLEENFORCEPOLICY:
7065 EnsureLineVisible(static_cast<int>(wParam), true);
7066 break;
7068 case SCI_SCROLLRANGE:
7069 ScrollRange(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
7070 break;
7072 case SCI_SEARCHANCHOR:
7073 SearchAnchor();
7074 break;
7076 case SCI_SEARCHNEXT:
7077 case SCI_SEARCHPREV:
7078 return SearchText(iMessage, wParam, lParam);
7080 case SCI_SETXCARETPOLICY:
7081 caretXPolicy = static_cast<int>(wParam);
7082 caretXSlop = static_cast<int>(lParam);
7083 break;
7085 case SCI_SETYCARETPOLICY:
7086 caretYPolicy = static_cast<int>(wParam);
7087 caretYSlop = static_cast<int>(lParam);
7088 break;
7090 case SCI_SETVISIBLEPOLICY:
7091 visiblePolicy = static_cast<int>(wParam);
7092 visibleSlop = static_cast<int>(lParam);
7093 break;
7095 case SCI_LINESONSCREEN:
7096 return LinesOnScreen();
7098 case SCI_SETSELFORE:
7099 vs.selColours.fore = ColourOptional(wParam, lParam);
7100 vs.selAdditionalForeground = ColourDesired(static_cast<long>(lParam));
7101 InvalidateStyleRedraw();
7102 break;
7104 case SCI_SETSELBACK:
7105 vs.selColours.back = ColourOptional(wParam, lParam);
7106 vs.selAdditionalBackground = ColourDesired(static_cast<long>(lParam));
7107 InvalidateStyleRedraw();
7108 break;
7110 case SCI_SETSELALPHA:
7111 vs.selAlpha = static_cast<int>(wParam);
7112 vs.selAdditionalAlpha = static_cast<int>(wParam);
7113 InvalidateStyleRedraw();
7114 break;
7116 case SCI_GETSELALPHA:
7117 return vs.selAlpha;
7119 case SCI_GETSELEOLFILLED:
7120 return vs.selEOLFilled;
7122 case SCI_SETSELEOLFILLED:
7123 vs.selEOLFilled = wParam != 0;
7124 InvalidateStyleRedraw();
7125 break;
7127 case SCI_SETWHITESPACEFORE:
7128 vs.whitespaceColours.fore = ColourOptional(wParam, lParam);
7129 InvalidateStyleRedraw();
7130 break;
7132 case SCI_SETWHITESPACEBACK:
7133 vs.whitespaceColours.back = ColourOptional(wParam, lParam);
7134 InvalidateStyleRedraw();
7135 break;
7137 case SCI_SETCARETFORE:
7138 vs.caretcolour = ColourDesired(static_cast<long>(wParam));
7139 InvalidateStyleRedraw();
7140 break;
7142 case SCI_GETCARETFORE:
7143 return vs.caretcolour.AsLong();
7145 case SCI_SETCARETSTYLE:
7146 if (wParam <= CARETSTYLE_BLOCK)
7147 vs.caretStyle = static_cast<int>(wParam);
7148 else
7149 /* Default to the line caret */
7150 vs.caretStyle = CARETSTYLE_LINE;
7151 InvalidateStyleRedraw();
7152 break;
7154 case SCI_GETCARETSTYLE:
7155 return vs.caretStyle;
7157 case SCI_SETCARETWIDTH:
7158 if (static_cast<int>(wParam) <= 0)
7159 vs.caretWidth = 0;
7160 else if (wParam >= 3)
7161 vs.caretWidth = 3;
7162 else
7163 vs.caretWidth = static_cast<int>(wParam);
7164 InvalidateStyleRedraw();
7165 break;
7167 case SCI_GETCARETWIDTH:
7168 return vs.caretWidth;
7170 case SCI_ASSIGNCMDKEY:
7171 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
7172 Platform::HighShortFromLong(static_cast<long>(wParam)), static_cast<unsigned int>(lParam));
7173 break;
7175 case SCI_CLEARCMDKEY:
7176 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
7177 Platform::HighShortFromLong(static_cast<long>(wParam)), SCI_NULL);
7178 break;
7180 case SCI_CLEARALLCMDKEYS:
7181 kmap.Clear();
7182 break;
7184 case SCI_INDICSETSTYLE:
7185 if (wParam <= INDIC_MAX) {
7186 vs.indicators[wParam].sacNormal.style = static_cast<int>(lParam);
7187 vs.indicators[wParam].sacHover.style = static_cast<int>(lParam);
7188 InvalidateStyleRedraw();
7190 break;
7192 case SCI_INDICGETSTYLE:
7193 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacNormal.style : 0;
7195 case SCI_INDICSETFORE:
7196 if (wParam <= INDIC_MAX) {
7197 vs.indicators[wParam].sacNormal.fore = ColourDesired(static_cast<long>(lParam));
7198 vs.indicators[wParam].sacHover.fore = ColourDesired(static_cast<long>(lParam));
7199 InvalidateStyleRedraw();
7201 break;
7203 case SCI_INDICGETFORE:
7204 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacNormal.fore.AsLong() : 0;
7206 case SCI_INDICSETHOVERSTYLE:
7207 if (wParam <= INDIC_MAX) {
7208 vs.indicators[wParam].sacHover.style = static_cast<int>(lParam);
7209 InvalidateStyleRedraw();
7211 break;
7213 case SCI_INDICGETHOVERSTYLE:
7214 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacHover.style : 0;
7216 case SCI_INDICSETHOVERFORE:
7217 if (wParam <= INDIC_MAX) {
7218 vs.indicators[wParam].sacHover.fore = ColourDesired(static_cast<long>(lParam));
7219 InvalidateStyleRedraw();
7221 break;
7223 case SCI_INDICGETHOVERFORE:
7224 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacHover.fore.AsLong() : 0;
7226 case SCI_INDICSETFLAGS:
7227 if (wParam <= INDIC_MAX) {
7228 vs.indicators[wParam].SetFlags(static_cast<int>(lParam));
7229 InvalidateStyleRedraw();
7231 break;
7233 case SCI_INDICGETFLAGS:
7234 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].Flags() : 0;
7236 case SCI_INDICSETUNDER:
7237 if (wParam <= INDIC_MAX) {
7238 vs.indicators[wParam].under = lParam != 0;
7239 InvalidateStyleRedraw();
7241 break;
7243 case SCI_INDICGETUNDER:
7244 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
7246 case SCI_INDICSETALPHA:
7247 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
7248 vs.indicators[wParam].fillAlpha = static_cast<int>(lParam);
7249 InvalidateStyleRedraw();
7251 break;
7253 case SCI_INDICGETALPHA:
7254 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
7256 case SCI_INDICSETOUTLINEALPHA:
7257 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
7258 vs.indicators[wParam].outlineAlpha = static_cast<int>(lParam);
7259 InvalidateStyleRedraw();
7261 break;
7263 case SCI_INDICGETOUTLINEALPHA:
7264 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
7266 case SCI_SETINDICATORCURRENT:
7267 pdoc->decorations.SetCurrentIndicator(static_cast<int>(wParam));
7268 break;
7269 case SCI_GETINDICATORCURRENT:
7270 return pdoc->decorations.GetCurrentIndicator();
7271 case SCI_SETINDICATORVALUE:
7272 pdoc->decorations.SetCurrentValue(static_cast<int>(wParam));
7273 break;
7274 case SCI_GETINDICATORVALUE:
7275 return pdoc->decorations.GetCurrentValue();
7277 case SCI_INDICATORFILLRANGE:
7278 pdoc->DecorationFillRange(static_cast<int>(wParam), pdoc->decorations.GetCurrentValue(), static_cast<int>(lParam));
7279 break;
7281 case SCI_INDICATORCLEARRANGE:
7282 pdoc->DecorationFillRange(static_cast<int>(wParam), 0, static_cast<int>(lParam));
7283 break;
7285 case SCI_INDICATORALLONFOR:
7286 return pdoc->decorations.AllOnFor(static_cast<int>(wParam));
7288 case SCI_INDICATORVALUEAT:
7289 return pdoc->decorations.ValueAt(static_cast<int>(wParam), static_cast<int>(lParam));
7291 case SCI_INDICATORSTART:
7292 return pdoc->decorations.Start(static_cast<int>(wParam), static_cast<int>(lParam));
7294 case SCI_INDICATOREND:
7295 return pdoc->decorations.End(static_cast<int>(wParam), static_cast<int>(lParam));
7297 case SCI_LINEDOWN:
7298 case SCI_LINEDOWNEXTEND:
7299 case SCI_PARADOWN:
7300 case SCI_PARADOWNEXTEND:
7301 case SCI_LINEUP:
7302 case SCI_LINEUPEXTEND:
7303 case SCI_PARAUP:
7304 case SCI_PARAUPEXTEND:
7305 case SCI_CHARLEFT:
7306 case SCI_CHARLEFTEXTEND:
7307 case SCI_CHARRIGHT:
7308 case SCI_CHARRIGHTEXTEND:
7309 case SCI_WORDLEFT:
7310 case SCI_WORDLEFTEXTEND:
7311 case SCI_WORDRIGHT:
7312 case SCI_WORDRIGHTEXTEND:
7313 case SCI_WORDLEFTEND:
7314 case SCI_WORDLEFTENDEXTEND:
7315 case SCI_WORDRIGHTEND:
7316 case SCI_WORDRIGHTENDEXTEND:
7317 case SCI_HOME:
7318 case SCI_HOMEEXTEND:
7319 case SCI_LINEEND:
7320 case SCI_LINEENDEXTEND:
7321 case SCI_HOMEWRAP:
7322 case SCI_HOMEWRAPEXTEND:
7323 case SCI_LINEENDWRAP:
7324 case SCI_LINEENDWRAPEXTEND:
7325 case SCI_DOCUMENTSTART:
7326 case SCI_DOCUMENTSTARTEXTEND:
7327 case SCI_DOCUMENTEND:
7328 case SCI_DOCUMENTENDEXTEND:
7329 case SCI_SCROLLTOSTART:
7330 case SCI_SCROLLTOEND:
7332 case SCI_STUTTEREDPAGEUP:
7333 case SCI_STUTTEREDPAGEUPEXTEND:
7334 case SCI_STUTTEREDPAGEDOWN:
7335 case SCI_STUTTEREDPAGEDOWNEXTEND:
7337 case SCI_PAGEUP:
7338 case SCI_PAGEUPEXTEND:
7339 case SCI_PAGEDOWN:
7340 case SCI_PAGEDOWNEXTEND:
7341 case SCI_EDITTOGGLEOVERTYPE:
7342 case SCI_CANCEL:
7343 case SCI_DELETEBACK:
7344 case SCI_TAB:
7345 case SCI_BACKTAB:
7346 case SCI_NEWLINE:
7347 case SCI_FORMFEED:
7348 case SCI_VCHOME:
7349 case SCI_VCHOMEEXTEND:
7350 case SCI_VCHOMEWRAP:
7351 case SCI_VCHOMEWRAPEXTEND:
7352 case SCI_VCHOMEDISPLAY:
7353 case SCI_VCHOMEDISPLAYEXTEND:
7354 case SCI_ZOOMIN:
7355 case SCI_ZOOMOUT:
7356 case SCI_DELWORDLEFT:
7357 case SCI_DELWORDRIGHT:
7358 case SCI_DELWORDRIGHTEND:
7359 case SCI_DELLINELEFT:
7360 case SCI_DELLINERIGHT:
7361 case SCI_LINECOPY:
7362 case SCI_LINECUT:
7363 case SCI_LINEDELETE:
7364 case SCI_LINETRANSPOSE:
7365 case SCI_LINEDUPLICATE:
7366 case SCI_LOWERCASE:
7367 case SCI_UPPERCASE:
7368 case SCI_LINESCROLLDOWN:
7369 case SCI_LINESCROLLUP:
7370 case SCI_WORDPARTLEFT:
7371 case SCI_WORDPARTLEFTEXTEND:
7372 case SCI_WORDPARTRIGHT:
7373 case SCI_WORDPARTRIGHTEXTEND:
7374 case SCI_DELETEBACKNOTLINE:
7375 case SCI_HOMEDISPLAY:
7376 case SCI_HOMEDISPLAYEXTEND:
7377 case SCI_LINEENDDISPLAY:
7378 case SCI_LINEENDDISPLAYEXTEND:
7379 case SCI_LINEDOWNRECTEXTEND:
7380 case SCI_LINEUPRECTEXTEND:
7381 case SCI_CHARLEFTRECTEXTEND:
7382 case SCI_CHARRIGHTRECTEXTEND:
7383 case SCI_HOMERECTEXTEND:
7384 case SCI_VCHOMERECTEXTEND:
7385 case SCI_LINEENDRECTEXTEND:
7386 case SCI_PAGEUPRECTEXTEND:
7387 case SCI_PAGEDOWNRECTEXTEND:
7388 case SCI_SELECTIONDUPLICATE:
7389 return KeyCommand(iMessage);
7391 case SCI_BRACEHIGHLIGHT:
7392 SetBraceHighlight(static_cast<int>(wParam), static_cast<int>(lParam), STYLE_BRACELIGHT);
7393 break;
7395 case SCI_BRACEHIGHLIGHTINDICATOR:
7396 if (lParam >= 0 && lParam <= INDIC_MAX) {
7397 vs.braceHighlightIndicatorSet = wParam != 0;
7398 vs.braceHighlightIndicator = static_cast<int>(lParam);
7400 break;
7402 case SCI_BRACEBADLIGHT:
7403 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
7404 break;
7406 case SCI_BRACEBADLIGHTINDICATOR:
7407 if (lParam >= 0 && lParam <= INDIC_MAX) {
7408 vs.braceBadLightIndicatorSet = wParam != 0;
7409 vs.braceBadLightIndicator = static_cast<int>(lParam);
7411 break;
7413 case SCI_BRACEMATCH:
7414 // wParam is position of char to find brace for,
7415 // lParam is maximum amount of text to restyle to find it
7416 return pdoc->BraceMatch(static_cast<int>(wParam), static_cast<int>(lParam));
7418 case SCI_GETVIEWEOL:
7419 return vs.viewEOL;
7421 case SCI_SETVIEWEOL:
7422 vs.viewEOL = wParam != 0;
7423 InvalidateStyleRedraw();
7424 break;
7426 case SCI_SETZOOM:
7427 vs.zoomLevel = static_cast<int>(wParam);
7428 InvalidateStyleRedraw();
7429 NotifyZoom();
7430 break;
7432 case SCI_GETZOOM:
7433 return vs.zoomLevel;
7435 case SCI_GETEDGECOLUMN:
7436 return vs.theEdge;
7438 case SCI_SETEDGECOLUMN:
7439 vs.theEdge = static_cast<int>(wParam);
7440 InvalidateStyleRedraw();
7441 break;
7443 case SCI_GETEDGEMODE:
7444 return vs.edgeState;
7446 case SCI_SETEDGEMODE:
7447 vs.edgeState = static_cast<int>(wParam);
7448 InvalidateStyleRedraw();
7449 break;
7451 case SCI_GETEDGECOLOUR:
7452 return vs.edgecolour.AsLong();
7454 case SCI_SETEDGECOLOUR:
7455 vs.edgecolour = ColourDesired(static_cast<long>(wParam));
7456 InvalidateStyleRedraw();
7457 break;
7459 case SCI_GETDOCPOINTER:
7460 return reinterpret_cast<sptr_t>(pdoc);
7462 case SCI_SETDOCPOINTER:
7463 CancelModes();
7464 SetDocPointer(reinterpret_cast<Document *>(lParam));
7465 return 0;
7467 case SCI_CREATEDOCUMENT: {
7468 Document *doc = new Document();
7469 doc->AddRef();
7470 return reinterpret_cast<sptr_t>(doc);
7473 case SCI_ADDREFDOCUMENT:
7474 (reinterpret_cast<Document *>(lParam))->AddRef();
7475 break;
7477 case SCI_RELEASEDOCUMENT:
7478 (reinterpret_cast<Document *>(lParam))->Release();
7479 break;
7481 case SCI_CREATELOADER: {
7482 Document *doc = new Document();
7483 doc->AddRef();
7484 doc->Allocate(static_cast<int>(wParam));
7485 doc->SetUndoCollection(false);
7486 return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
7489 case SCI_SETMODEVENTMASK:
7490 modEventMask = static_cast<int>(wParam);
7491 return 0;
7493 case SCI_GETMODEVENTMASK:
7494 return modEventMask;
7496 case SCI_CONVERTEOLS:
7497 pdoc->ConvertLineEnds(static_cast<int>(wParam));
7498 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
7499 return 0;
7501 case SCI_SETLENGTHFORENCODE:
7502 lengthForEncode = static_cast<int>(wParam);
7503 return 0;
7505 case SCI_SELECTIONISRECTANGLE:
7506 return sel.selType == Selection::selRectangle ? 1 : 0;
7508 case SCI_SETSELECTIONMODE: {
7509 switch (wParam) {
7510 case SC_SEL_STREAM:
7511 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7512 sel.selType = Selection::selStream;
7513 break;
7514 case SC_SEL_RECTANGLE:
7515 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
7516 sel.selType = Selection::selRectangle;
7517 break;
7518 case SC_SEL_LINES:
7519 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
7520 sel.selType = Selection::selLines;
7521 break;
7522 case SC_SEL_THIN:
7523 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
7524 sel.selType = Selection::selThin;
7525 break;
7526 default:
7527 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7528 sel.selType = Selection::selStream;
7530 InvalidateWholeSelection();
7531 break;
7533 case SCI_GETSELECTIONMODE:
7534 switch (sel.selType) {
7535 case Selection::selStream:
7536 return SC_SEL_STREAM;
7537 case Selection::selRectangle:
7538 return SC_SEL_RECTANGLE;
7539 case Selection::selLines:
7540 return SC_SEL_LINES;
7541 case Selection::selThin:
7542 return SC_SEL_THIN;
7543 default: // ?!
7544 return SC_SEL_STREAM;
7546 case SCI_GETLINESELSTARTPOSITION:
7547 case SCI_GETLINESELENDPOSITION: {
7548 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(static_cast<int>(wParam))),
7549 SelectionPosition(pdoc->LineEnd(static_cast<int>(wParam))));
7550 for (size_t r=0; r<sel.Count(); r++) {
7551 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
7552 if (portion.start.IsValid()) {
7553 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
7556 return INVALID_POSITION;
7559 case SCI_SETOVERTYPE:
7560 inOverstrike = wParam != 0;
7561 break;
7563 case SCI_GETOVERTYPE:
7564 return inOverstrike ? 1 : 0;
7566 case SCI_SETFOCUS:
7567 SetFocusState(wParam != 0);
7568 break;
7570 case SCI_GETFOCUS:
7571 return hasFocus;
7573 case SCI_SETSTATUS:
7574 errorStatus = static_cast<int>(wParam);
7575 break;
7577 case SCI_GETSTATUS:
7578 return errorStatus;
7580 case SCI_SETMOUSEDOWNCAPTURES:
7581 mouseDownCaptures = wParam != 0;
7582 break;
7584 case SCI_GETMOUSEDOWNCAPTURES:
7585 return mouseDownCaptures;
7587 case SCI_SETCURSOR:
7588 cursorMode = static_cast<int>(wParam);
7589 DisplayCursor(Window::cursorText);
7590 break;
7592 case SCI_GETCURSOR:
7593 return cursorMode;
7595 case SCI_SETCONTROLCHARSYMBOL:
7596 vs.controlCharSymbol = static_cast<int>(wParam);
7597 InvalidateStyleRedraw();
7598 break;
7600 case SCI_GETCONTROLCHARSYMBOL:
7601 return vs.controlCharSymbol;
7603 case SCI_SETREPRESENTATION:
7604 reprs.SetRepresentation(reinterpret_cast<const char *>(wParam), CharPtrFromSPtr(lParam));
7605 break;
7607 case SCI_GETREPRESENTATION: {
7608 const Representation *repr = reprs.RepresentationFromCharacter(
7609 reinterpret_cast<const char *>(wParam), UTF8MaxBytes);
7610 if (repr) {
7611 return StringResult(lParam, repr->stringRep.c_str());
7613 return 0;
7616 case SCI_CLEARREPRESENTATION:
7617 reprs.ClearRepresentation(reinterpret_cast<const char *>(wParam));
7618 break;
7620 case SCI_STARTRECORD:
7621 recordingMacro = true;
7622 return 0;
7624 case SCI_STOPRECORD:
7625 recordingMacro = false;
7626 return 0;
7628 case SCI_MOVECARETINSIDEVIEW:
7629 MoveCaretInsideView();
7630 break;
7632 case SCI_SETFOLDMARGINCOLOUR:
7633 vs.foldmarginColour = ColourOptional(wParam, lParam);
7634 InvalidateStyleRedraw();
7635 break;
7637 case SCI_SETFOLDMARGINHICOLOUR:
7638 vs.foldmarginHighlightColour = ColourOptional(wParam, lParam);
7639 InvalidateStyleRedraw();
7640 break;
7642 case SCI_SETHOTSPOTACTIVEFORE:
7643 vs.hotspotColours.fore = ColourOptional(wParam, lParam);
7644 InvalidateStyleRedraw();
7645 break;
7647 case SCI_GETHOTSPOTACTIVEFORE:
7648 return vs.hotspotColours.fore.AsLong();
7650 case SCI_SETHOTSPOTACTIVEBACK:
7651 vs.hotspotColours.back = ColourOptional(wParam, lParam);
7652 InvalidateStyleRedraw();
7653 break;
7655 case SCI_GETHOTSPOTACTIVEBACK:
7656 return vs.hotspotColours.back.AsLong();
7658 case SCI_SETHOTSPOTACTIVEUNDERLINE:
7659 vs.hotspotUnderline = wParam != 0;
7660 InvalidateStyleRedraw();
7661 break;
7663 case SCI_GETHOTSPOTACTIVEUNDERLINE:
7664 return vs.hotspotUnderline ? 1 : 0;
7666 case SCI_SETHOTSPOTSINGLELINE:
7667 vs.hotspotSingleLine = wParam != 0;
7668 InvalidateStyleRedraw();
7669 break;
7671 case SCI_GETHOTSPOTSINGLELINE:
7672 return vs.hotspotSingleLine ? 1 : 0;
7674 case SCI_SETPASTECONVERTENDINGS:
7675 convertPastes = wParam != 0;
7676 break;
7678 case SCI_GETPASTECONVERTENDINGS:
7679 return convertPastes ? 1 : 0;
7681 case SCI_GETCHARACTERPOINTER:
7682 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
7684 case SCI_GETRANGEPOINTER:
7685 return reinterpret_cast<sptr_t>(pdoc->RangePointer(static_cast<int>(wParam), static_cast<int>(lParam)));
7687 case SCI_GETGAPPOSITION:
7688 return pdoc->GapPosition();
7690 case SCI_SETEXTRAASCENT:
7691 vs.extraAscent = static_cast<int>(wParam);
7692 InvalidateStyleRedraw();
7693 break;
7695 case SCI_GETEXTRAASCENT:
7696 return vs.extraAscent;
7698 case SCI_SETEXTRADESCENT:
7699 vs.extraDescent = static_cast<int>(wParam);
7700 InvalidateStyleRedraw();
7701 break;
7703 case SCI_GETEXTRADESCENT:
7704 return vs.extraDescent;
7706 case SCI_MARGINSETSTYLEOFFSET:
7707 vs.marginStyleOffset = static_cast<int>(wParam);
7708 InvalidateStyleRedraw();
7709 break;
7711 case SCI_MARGINGETSTYLEOFFSET:
7712 return vs.marginStyleOffset;
7714 case SCI_SETMARGINOPTIONS:
7715 marginOptions = static_cast<int>(wParam);
7716 break;
7718 case SCI_GETMARGINOPTIONS:
7719 return marginOptions;
7721 case SCI_MARGINSETTEXT:
7722 pdoc->MarginSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7723 break;
7725 case SCI_MARGINGETTEXT: {
7726 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7727 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
7730 case SCI_MARGINSETSTYLE:
7731 pdoc->MarginSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
7732 break;
7734 case SCI_MARGINGETSTYLE: {
7735 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7736 return st.style;
7739 case SCI_MARGINSETSTYLES:
7740 pdoc->MarginSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
7741 break;
7743 case SCI_MARGINGETSTYLES: {
7744 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7745 return BytesResult(lParam, st.styles, st.length);
7748 case SCI_MARGINTEXTCLEARALL:
7749 pdoc->MarginClearAll();
7750 break;
7752 case SCI_ANNOTATIONSETTEXT:
7753 pdoc->AnnotationSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7754 break;
7756 case SCI_ANNOTATIONGETTEXT: {
7757 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7758 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
7761 case SCI_ANNOTATIONGETSTYLE: {
7762 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7763 return st.style;
7766 case SCI_ANNOTATIONSETSTYLE:
7767 pdoc->AnnotationSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
7768 break;
7770 case SCI_ANNOTATIONSETSTYLES:
7771 pdoc->AnnotationSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
7772 break;
7774 case SCI_ANNOTATIONGETSTYLES: {
7775 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7776 return BytesResult(lParam, st.styles, st.length);
7779 case SCI_ANNOTATIONGETLINES:
7780 return pdoc->AnnotationLines(static_cast<int>(wParam));
7782 case SCI_ANNOTATIONCLEARALL:
7783 pdoc->AnnotationClearAll();
7784 break;
7786 case SCI_ANNOTATIONSETVISIBLE:
7787 SetAnnotationVisible(static_cast<int>(wParam));
7788 break;
7790 case SCI_ANNOTATIONGETVISIBLE:
7791 return vs.annotationVisible;
7793 case SCI_ANNOTATIONSETSTYLEOFFSET:
7794 vs.annotationStyleOffset = static_cast<int>(wParam);
7795 InvalidateStyleRedraw();
7796 break;
7798 case SCI_ANNOTATIONGETSTYLEOFFSET:
7799 return vs.annotationStyleOffset;
7801 case SCI_RELEASEALLEXTENDEDSTYLES:
7802 vs.ReleaseAllExtendedStyles();
7803 break;
7805 case SCI_ALLOCATEEXTENDEDSTYLES:
7806 return vs.AllocateExtendedStyles(static_cast<int>(wParam));
7808 case SCI_ADDUNDOACTION:
7809 pdoc->AddUndoAction(static_cast<int>(wParam), lParam & UNDO_MAY_COALESCE);
7810 break;
7812 case SCI_SETMOUSESELECTIONRECTANGULARSWITCH:
7813 mouseSelectionRectangularSwitch = wParam != 0;
7814 break;
7816 case SCI_GETMOUSESELECTIONRECTANGULARSWITCH:
7817 return mouseSelectionRectangularSwitch;
7819 case SCI_SETMULTIPLESELECTION:
7820 multipleSelection = wParam != 0;
7821 InvalidateCaret();
7822 break;
7824 case SCI_GETMULTIPLESELECTION:
7825 return multipleSelection;
7827 case SCI_SETADDITIONALSELECTIONTYPING:
7828 additionalSelectionTyping = wParam != 0;
7829 InvalidateCaret();
7830 break;
7832 case SCI_GETADDITIONALSELECTIONTYPING:
7833 return additionalSelectionTyping;
7835 case SCI_SETMULTIPASTE:
7836 multiPasteMode = static_cast<int>(wParam);
7837 break;
7839 case SCI_GETMULTIPASTE:
7840 return multiPasteMode;
7842 case SCI_SETADDITIONALCARETSBLINK:
7843 view.additionalCaretsBlink = wParam != 0;
7844 InvalidateCaret();
7845 break;
7847 case SCI_GETADDITIONALCARETSBLINK:
7848 return view.additionalCaretsBlink;
7850 case SCI_SETADDITIONALCARETSVISIBLE:
7851 view.additionalCaretsVisible = wParam != 0;
7852 InvalidateCaret();
7853 break;
7855 case SCI_GETADDITIONALCARETSVISIBLE:
7856 return view.additionalCaretsVisible;
7858 case SCI_GETSELECTIONS:
7859 return sel.Count();
7861 case SCI_GETSELECTIONEMPTY:
7862 return sel.Empty();
7864 case SCI_CLEARSELECTIONS:
7865 sel.Clear();
7866 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7867 Redraw();
7868 break;
7870 case SCI_SETSELECTION:
7871 sel.SetSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
7872 Redraw();
7873 break;
7875 case SCI_ADDSELECTION:
7876 sel.AddSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
7877 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7878 Redraw();
7879 break;
7881 case SCI_DROPSELECTIONN:
7882 sel.DropSelection(static_cast<int>(wParam));
7883 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7884 Redraw();
7885 break;
7887 case SCI_SETMAINSELECTION:
7888 sel.SetMain(static_cast<int>(wParam));
7889 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7890 Redraw();
7891 break;
7893 case SCI_GETMAINSELECTION:
7894 return sel.Main();
7896 case SCI_SETSELECTIONNCARET:
7897 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
7898 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7899 Redraw();
7900 break;
7902 case SCI_GETSELECTIONNCARET:
7903 return sel.Range(wParam).caret.Position();
7905 case SCI_SETSELECTIONNANCHOR:
7906 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
7907 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7908 Redraw();
7909 break;
7910 case SCI_GETSELECTIONNANCHOR:
7911 return sel.Range(wParam).anchor.Position();
7913 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
7914 sel.Range(wParam).caret.SetVirtualSpace(static_cast<int>(lParam));
7915 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7916 Redraw();
7917 break;
7919 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
7920 return sel.Range(wParam).caret.VirtualSpace();
7922 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
7923 sel.Range(wParam).anchor.SetVirtualSpace(static_cast<int>(lParam));
7924 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7925 Redraw();
7926 break;
7928 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
7929 return sel.Range(wParam).anchor.VirtualSpace();
7931 case SCI_SETSELECTIONNSTART:
7932 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
7933 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7934 Redraw();
7935 break;
7937 case SCI_GETSELECTIONNSTART:
7938 return sel.Range(wParam).Start().Position();
7940 case SCI_SETSELECTIONNEND:
7941 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
7942 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7943 Redraw();
7944 break;
7946 case SCI_GETSELECTIONNEND:
7947 return sel.Range(wParam).End().Position();
7949 case SCI_SETRECTANGULARSELECTIONCARET:
7950 if (!sel.IsRectangular())
7951 sel.Clear();
7952 sel.selType = Selection::selRectangle;
7953 sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
7954 SetRectangularRange();
7955 Redraw();
7956 break;
7958 case SCI_GETRECTANGULARSELECTIONCARET:
7959 return sel.Rectangular().caret.Position();
7961 case SCI_SETRECTANGULARSELECTIONANCHOR:
7962 if (!sel.IsRectangular())
7963 sel.Clear();
7964 sel.selType = Selection::selRectangle;
7965 sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
7966 SetRectangularRange();
7967 Redraw();
7968 break;
7970 case SCI_GETRECTANGULARSELECTIONANCHOR:
7971 return sel.Rectangular().anchor.Position();
7973 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
7974 if (!sel.IsRectangular())
7975 sel.Clear();
7976 sel.selType = Selection::selRectangle;
7977 sel.Rectangular().caret.SetVirtualSpace(static_cast<int>(wParam));
7978 SetRectangularRange();
7979 Redraw();
7980 break;
7982 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
7983 return sel.Rectangular().caret.VirtualSpace();
7985 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
7986 if (!sel.IsRectangular())
7987 sel.Clear();
7988 sel.selType = Selection::selRectangle;
7989 sel.Rectangular().anchor.SetVirtualSpace(static_cast<int>(wParam));
7990 SetRectangularRange();
7991 Redraw();
7992 break;
7994 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
7995 return sel.Rectangular().anchor.VirtualSpace();
7997 case SCI_SETVIRTUALSPACEOPTIONS:
7998 virtualSpaceOptions = static_cast<int>(wParam);
7999 break;
8001 case SCI_GETVIRTUALSPACEOPTIONS:
8002 return virtualSpaceOptions;
8004 case SCI_SETADDITIONALSELFORE:
8005 vs.selAdditionalForeground = ColourDesired(static_cast<long>(wParam));
8006 InvalidateStyleRedraw();
8007 break;
8009 case SCI_SETADDITIONALSELBACK:
8010 vs.selAdditionalBackground = ColourDesired(static_cast<long>(wParam));
8011 InvalidateStyleRedraw();
8012 break;
8014 case SCI_SETADDITIONALSELALPHA:
8015 vs.selAdditionalAlpha = static_cast<int>(wParam);
8016 InvalidateStyleRedraw();
8017 break;
8019 case SCI_GETADDITIONALSELALPHA:
8020 return vs.selAdditionalAlpha;
8022 case SCI_SETADDITIONALCARETFORE:
8023 vs.additionalCaretColour = ColourDesired(static_cast<long>(wParam));
8024 InvalidateStyleRedraw();
8025 break;
8027 case SCI_GETADDITIONALCARETFORE:
8028 return vs.additionalCaretColour.AsLong();
8030 case SCI_ROTATESELECTION:
8031 sel.RotateMain();
8032 InvalidateWholeSelection();
8033 break;
8035 case SCI_SWAPMAINANCHORCARET:
8036 InvalidateSelection(sel.RangeMain());
8037 sel.RangeMain().Swap();
8038 break;
8040 case SCI_MULTIPLESELECTADDNEXT:
8041 MultipleSelectAdd(addOne);
8042 break;
8044 case SCI_MULTIPLESELECTADDEACH:
8045 MultipleSelectAdd(addEach);
8046 break;
8048 case SCI_CHANGELEXERSTATE:
8049 pdoc->ChangeLexerState(static_cast<int>(wParam), static_cast<int>(lParam));
8050 break;
8052 case SCI_SETIDENTIFIER:
8053 SetCtrlID(static_cast<int>(wParam));
8054 break;
8056 case SCI_GETIDENTIFIER:
8057 return GetCtrlID();
8059 case SCI_SETTECHNOLOGY:
8060 // No action by default
8061 break;
8063 case SCI_GETTECHNOLOGY:
8064 return technology;
8066 case SCI_COUNTCHARACTERS:
8067 return pdoc->CountCharacters(static_cast<int>(wParam), static_cast<int>(lParam));
8069 default:
8070 return DefWndProc(iMessage, wParam, lParam);
8072 //Platform::DebugPrintf("end wnd proc\n");
8073 return 0l;