Disable the "Blend Alpha" menu item if not in overlap mode
[TortoiseGit.git] / ext / scintilla / src / Editor.cxx
blobee78eacce0f8ce4e772e76f84500e49867f7aec5
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 "SplitVector.h"
29 #include "Partitioning.h"
30 #include "RunStyles.h"
31 #include "ContractionState.h"
32 #include "CellBuffer.h"
33 #include "PerLine.h"
34 #include "KeyMap.h"
35 #include "Indicator.h"
36 #include "XPM.h"
37 #include "LineMarker.h"
38 #include "Style.h"
39 #include "ViewStyle.h"
40 #include "CharClassify.h"
41 #include "Decoration.h"
42 #include "CaseFolder.h"
43 #include "Document.h"
44 #include "UniConversion.h"
45 #include "Selection.h"
46 #include "PositionCache.h"
47 #include "EditModel.h"
48 #include "MarginView.h"
49 #include "EditView.h"
50 #include "Editor.h"
52 #ifdef SCI_NAMESPACE
53 using namespace Scintilla;
54 #endif
57 return whether this modification represents an operation that
58 may reasonably be deferred (not done now OR [possibly] at all)
60 static bool CanDeferToLastStep(const DocModification &mh) {
61 if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
62 return true; // CAN skip
63 if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)))
64 return false; // MUST do
65 if (mh.modificationType & SC_MULTISTEPUNDOREDO)
66 return true; // CAN skip
67 return false; // PRESUMABLY must do
70 static bool CanEliminate(const DocModification &mh) {
71 return
72 (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
76 return whether this modification represents the FINAL step
77 in a [possibly lengthy] multi-step Undo/Redo sequence
79 static bool IsLastStep(const DocModification &mh) {
80 return
81 (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0
82 && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
83 && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
84 && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
87 Timer::Timer() :
88 ticking(false), ticksToWait(0), tickerID(0) {}
90 Idler::Idler() :
91 state(false), idlerID(0) {}
93 static inline bool IsAllSpacesOrTabs(const char *s, unsigned int len) {
94 for (unsigned int i = 0; i < len; i++) {
95 // This is safe because IsSpaceOrTab() will return false for null terminators
96 if (!IsSpaceOrTab(s[i]))
97 return false;
99 return true;
102 Editor::Editor() {
103 view.editor = this;
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;
177 modEventMask = SC_MODEVENTMASKALL;
179 pdoc->AddWatcher(this, 0);
181 recordingMacro = false;
182 foldAutomatic = 0;
184 convertPastes = true;
186 SetRepresentations();
189 Editor::~Editor() {
190 pdoc->RemoveWatcher(this, 0);
191 DropGraphics(true);
194 void Editor::Finalise() {
195 SetIdle(false);
196 CancelModes();
199 void Editor::SetRepresentations() {
200 reprs.Clear();
202 // C0 control set
203 const char *reps[] = {
204 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
205 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
206 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
207 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
209 for (size_t j=0; j < ELEMENTS(reps); j++) {
210 char c[2] = { static_cast<char>(j), 0 };
211 reprs.SetRepresentation(c, reps[j]);
214 // C1 control set
215 // As well as Unicode mode, ISO-8859-1 should use these
216 if (IsUnicodeMode()) {
217 const char *repsC1[] = {
218 "PAD", "HOP", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
219 "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
220 "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
221 "SOS", "SGCI", "SCI", "CSI", "ST", "OSC", "PM", "APC"
223 for (size_t j=0; j < ELEMENTS(repsC1); j++) {
224 char c1[3] = { '\xc2', static_cast<char>(0x80+j), 0 };
225 reprs.SetRepresentation(c1, repsC1[j]);
227 reprs.SetRepresentation("\xe2\x80\xa8", "LS");
228 reprs.SetRepresentation("\xe2\x80\xa9", "PS");
231 // UTF-8 invalid bytes
232 if (IsUnicodeMode()) {
233 for (int k=0x80; k < 0x100; k++) {
234 char hiByte[2] = { static_cast<char>(k), 0 };
235 char hexits[4];
236 sprintf(hexits, "x%2X", k);
237 reprs.SetRepresentation(hiByte, hexits);
242 void Editor::DropGraphics(bool freeObjects) {
243 marginView.DropGraphics(freeObjects);
244 view.DropGraphics(freeObjects);
247 void Editor::AllocateGraphics() {
248 marginView.AllocateGraphics(vs);
249 view.AllocateGraphics(vs);
252 void Editor::InvalidateStyleData() {
253 stylesValid = false;
254 vs.technology = technology;
255 DropGraphics(false);
256 AllocateGraphics();
257 view.llc.Invalidate(LineLayout::llInvalid);
258 view.posCache.Clear();
261 void Editor::InvalidateStyleRedraw() {
262 NeedWrapping();
263 InvalidateStyleData();
264 Redraw();
267 void Editor::RefreshStyleData() {
268 if (!stylesValid) {
269 stylesValid = true;
270 AutoSurface surface(this);
271 if (surface) {
272 vs.Refresh(*surface, pdoc->tabInChars);
274 SetScrollBars();
275 SetRectangularRange();
279 Point Editor::GetVisibleOriginInMain() const {
280 return Point(0,0);
283 Point Editor::DocumentPointFromView(Point ptView) const {
284 Point ptDocument = ptView;
285 if (wMargin.GetID()) {
286 Point ptOrigin = GetVisibleOriginInMain();
287 ptDocument.x += ptOrigin.x;
288 ptDocument.y += ptOrigin.y;
289 } else {
290 ptDocument.x += xOffset;
291 ptDocument.y += topLine * vs.lineHeight;
293 return ptDocument;
296 int Editor::TopLineOfMain() const {
297 if (wMargin.GetID())
298 return 0;
299 else
300 return topLine;
303 PRectangle Editor::GetClientRectangle() const {
304 Window &win = const_cast<Window &>(wMain);
305 return win.GetClientPosition();
308 PRectangle Editor::GetClientDrawingRectangle() {
309 return GetClientRectangle();
312 PRectangle Editor::GetTextRectangle() const {
313 PRectangle rc = GetClientRectangle();
314 rc.left += vs.textStart;
315 rc.right -= vs.rightMarginWidth;
316 return rc;
319 int Editor::LinesOnScreen() const {
320 PRectangle rcClient = GetClientRectangle();
321 int htClient = static_cast<int>(rcClient.bottom - rcClient.top);
322 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
323 return htClient / vs.lineHeight;
326 int Editor::LinesToScroll() const {
327 int retVal = LinesOnScreen() - 1;
328 if (retVal < 1)
329 return 1;
330 else
331 return retVal;
334 int Editor::MaxScrollPos() const {
335 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
336 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
337 int retVal = cs.LinesDisplayed();
338 if (endAtLastLine) {
339 retVal -= LinesOnScreen();
340 } else {
341 retVal--;
343 if (retVal < 0) {
344 return 0;
345 } else {
346 return retVal;
350 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
351 if (sp.Position() < 0) {
352 return SelectionPosition(0);
353 } else if (sp.Position() > pdoc->Length()) {
354 return SelectionPosition(pdoc->Length());
355 } else {
356 // If not at end of line then set offset to 0
357 if (!pdoc->IsLineEndPosition(sp.Position()))
358 sp.SetVirtualSpace(0);
359 return sp;
363 Point Editor::LocationFromPosition(SelectionPosition pos) {
364 RefreshStyleData();
365 AutoSurface surface(this);
366 return view.LocationFromPosition(surface, *this, pos, topLine, vs);
369 Point Editor::LocationFromPosition(int pos) {
370 return LocationFromPosition(SelectionPosition(pos));
373 int Editor::XFromPosition(int pos) {
374 Point pt = LocationFromPosition(pos);
375 return static_cast<int>(pt.x) - vs.textStart + xOffset;
378 int Editor::XFromPosition(SelectionPosition sp) {
379 Point pt = LocationFromPosition(sp);
380 return static_cast<int>(pt.x) - vs.textStart + xOffset;
383 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
384 RefreshStyleData();
385 AutoSurface surface(this);
387 if (canReturnInvalid) {
388 PRectangle rcClient = GetTextRectangle();
389 // May be in scroll view coordinates so translate back to main view
390 Point ptOrigin = GetVisibleOriginInMain();
391 rcClient.Move(-ptOrigin.x, -ptOrigin.y);
392 if (!rcClient.Contains(pt))
393 return SelectionPosition(INVALID_POSITION);
394 if (pt.x < vs.textStart)
395 return SelectionPosition(INVALID_POSITION);
396 if (pt.y < 0)
397 return SelectionPosition(INVALID_POSITION);
399 pt = DocumentPointFromView(pt);
400 return view.SPositionFromLocation(surface, *this, pt, canReturnInvalid, charPosition, virtualSpace, vs);
403 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
404 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
408 * Find the document position corresponding to an x coordinate on a particular document line.
409 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
410 * This method is used for rectangular selections and does not work on wrapped lines.
412 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
413 RefreshStyleData();
414 if (lineDoc >= pdoc->LinesTotal())
415 return SelectionPosition(pdoc->Length());
416 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
417 AutoSurface surface(this);
418 return view.SPositionFromLineX(surface, *this, lineDoc, x, vs);
421 int Editor::PositionFromLineX(int lineDoc, int x) {
422 return SPositionFromLineX(lineDoc, x).Position();
425 int Editor::LineFromLocation(Point pt) const {
426 return cs.DocFromDisplay(static_cast<int>(pt.y) / vs.lineHeight + topLine);
429 void Editor::SetTopLine(int topLineNew) {
430 if ((topLine != topLineNew) && (topLineNew >= 0)) {
431 topLine = topLineNew;
432 ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
434 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
438 * If painting then abandon the painting because a wider redraw is needed.
439 * @return true if calling code should stop drawing.
441 bool Editor::AbandonPaint() {
442 if ((paintState == painting) && !paintingAllText) {
443 paintState = paintAbandoned;
445 return paintState == paintAbandoned;
448 void Editor::RedrawRect(PRectangle rc) {
449 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
451 // Clip the redraw rectangle into the client area
452 PRectangle rcClient = GetClientRectangle();
453 if (rc.top < rcClient.top)
454 rc.top = rcClient.top;
455 if (rc.bottom > rcClient.bottom)
456 rc.bottom = rcClient.bottom;
457 if (rc.left < rcClient.left)
458 rc.left = rcClient.left;
459 if (rc.right > rcClient.right)
460 rc.right = rcClient.right;
462 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
463 wMain.InvalidateRectangle(rc);
467 void Editor::DiscardOverdraw() {
468 // Overridden on platforms that may draw outside visible area.
471 void Editor::Redraw() {
472 //Platform::DebugPrintf("Redraw all\n");
473 PRectangle rcClient = GetClientRectangle();
474 wMain.InvalidateRectangle(rcClient);
475 if (wMargin.GetID())
476 wMargin.InvalidateAll();
477 //wMain.InvalidateAll();
480 void Editor::RedrawSelMargin(int line, bool allAfter) {
481 bool abandonDraw = false;
482 if (!wMargin.GetID()) // Margin in main window so may need to abandon and retry
483 abandonDraw = AbandonPaint();
484 if (!abandonDraw) {
485 if (vs.maskInLine) {
486 Redraw();
487 } else {
488 PRectangle rcSelMargin = GetClientRectangle();
489 rcSelMargin.right = rcSelMargin.left + vs.fixedColumnWidth;
490 if (line != -1) {
491 PRectangle rcLine = RectangleFromRange(Range(pdoc->LineStart(line)), 0);
493 // Inflate line rectangle if there are image markers with height larger than line height
494 if (vs.largestMarkerHeight > vs.lineHeight) {
495 int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
496 rcLine.top -= delta;
497 rcLine.bottom += delta;
498 if (rcLine.top < rcSelMargin.top)
499 rcLine.top = rcSelMargin.top;
500 if (rcLine.bottom > rcSelMargin.bottom)
501 rcLine.bottom = rcSelMargin.bottom;
504 rcSelMargin.top = rcLine.top;
505 if (!allAfter)
506 rcSelMargin.bottom = rcLine.bottom;
507 if (rcSelMargin.Empty())
508 return;
510 if (wMargin.GetID()) {
511 Point ptOrigin = GetVisibleOriginInMain();
512 rcSelMargin.Move(-ptOrigin.x, -ptOrigin.y);
513 wMargin.InvalidateRectangle(rcSelMargin);
514 } else {
515 wMain.InvalidateRectangle(rcSelMargin);
521 PRectangle Editor::RectangleFromRange(Range r, int overlap) {
522 const int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(r.First()));
523 const int maxLine = cs.DisplayLastFromDoc(pdoc->LineFromPosition(r.Last()));
524 const PRectangle rcClientDrawing = GetClientDrawingRectangle();
525 PRectangle rc;
526 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
527 rc.left = static_cast<XYPOSITION>(vs.textStart - leftTextOverlap);
528 rc.top = static_cast<XYPOSITION>((minLine - TopLineOfMain()) * vs.lineHeight - overlap);
529 if (rc.top < rcClientDrawing.top)
530 rc.top = rcClientDrawing.top;
531 // Extend to right of prepared area if any to prevent artifacts from caret line highlight
532 rc.right = rcClientDrawing.right;
533 rc.bottom = static_cast<XYPOSITION>((maxLine - TopLineOfMain() + 1) * vs.lineHeight + overlap);
535 return rc;
538 void Editor::InvalidateRange(int start, int end) {
539 RedrawRect(RectangleFromRange(Range(start, end), view.LinesOverlap() ? vs.lineOverlap : 0));
542 int Editor::CurrentPosition() const {
543 return sel.MainCaret();
546 bool Editor::SelectionEmpty() const {
547 return sel.Empty();
550 SelectionPosition Editor::SelectionStart() {
551 return sel.RangeMain().Start();
554 SelectionPosition Editor::SelectionEnd() {
555 return sel.RangeMain().End();
558 void Editor::SetRectangularRange() {
559 if (sel.IsRectangular()) {
560 int xAnchor = XFromPosition(sel.Rectangular().anchor);
561 int xCaret = XFromPosition(sel.Rectangular().caret);
562 if (sel.selType == Selection::selThin) {
563 xCaret = xAnchor;
565 int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
566 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
567 int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
568 for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
569 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
570 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
571 range.ClearVirtualSpace();
572 if (line == lineAnchorRect)
573 sel.SetSelection(range);
574 else
575 sel.AddSelectionWithoutTrim(range);
580 void Editor::ThinRectangularRange() {
581 if (sel.IsRectangular()) {
582 sel.selType = Selection::selThin;
583 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
584 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
585 } else {
586 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
588 SetRectangularRange();
592 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
593 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
594 invalidateWholeSelection = true;
596 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
597 // +1 for lastAffected ensures caret repainted
598 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
599 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
600 if (invalidateWholeSelection) {
601 for (size_t r=0; r<sel.Count(); r++) {
602 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
603 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
604 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
605 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
608 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
609 InvalidateRange(firstAffected, lastAffected);
612 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
613 currentPos_ = ClampPositionIntoDocument(currentPos_);
614 anchor_ = ClampPositionIntoDocument(anchor_);
615 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
616 /* For Line selection - ensure the anchor and caret are always
617 at the beginning and end of the region lines. */
618 if (sel.selType == Selection::selLines) {
619 if (currentPos_ > anchor_) {
620 anchor_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
621 currentPos_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
622 } else {
623 currentPos_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
624 anchor_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
627 SelectionRange rangeNew(currentPos_, anchor_);
628 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
629 InvalidateSelection(rangeNew);
631 sel.RangeMain() = rangeNew;
632 SetRectangularRange();
633 ClaimSelection();
634 SetHoverIndicatorPosition(sel.MainCaret());
636 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
637 RedrawSelMargin();
639 QueueIdleWork(WorkNeeded::workUpdateUI);
642 void Editor::SetSelection(int currentPos_, int anchor_) {
643 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
646 // Just move the caret on the main selection
647 void Editor::SetSelection(SelectionPosition currentPos_) {
648 currentPos_ = ClampPositionIntoDocument(currentPos_);
649 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
650 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
651 InvalidateSelection(SelectionRange(currentPos_));
653 if (sel.IsRectangular()) {
654 sel.Rectangular() =
655 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
656 SetRectangularRange();
657 } else {
658 sel.RangeMain() =
659 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
661 ClaimSelection();
662 SetHoverIndicatorPosition(sel.MainCaret());
664 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
665 RedrawSelMargin();
667 QueueIdleWork(WorkNeeded::workUpdateUI);
670 void Editor::SetSelection(int currentPos_) {
671 SetSelection(SelectionPosition(currentPos_));
674 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
675 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
676 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
677 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
678 InvalidateSelection(rangeNew);
680 sel.Clear();
681 sel.RangeMain() = rangeNew;
682 SetRectangularRange();
683 ClaimSelection();
684 SetHoverIndicatorPosition(sel.MainCaret());
686 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
687 RedrawSelMargin();
689 QueueIdleWork(WorkNeeded::workUpdateUI);
692 void Editor::SetEmptySelection(int currentPos_) {
693 SetEmptySelection(SelectionPosition(currentPos_));
696 bool Editor::RangeContainsProtected(int start, int end) const {
697 if (vs.ProtectionActive()) {
698 if (start > end) {
699 int t = start;
700 start = end;
701 end = t;
703 for (int pos = start; pos < end; pos++) {
704 if (vs.styles[pdoc->StyleAt(pos)].IsProtected())
705 return true;
708 return false;
711 bool Editor::SelectionContainsProtected() {
712 for (size_t r=0; r<sel.Count(); r++) {
713 if (RangeContainsProtected(sel.Range(r).Start().Position(),
714 sel.Range(r).End().Position())) {
715 return true;
718 return false;
722 * Asks document to find a good position and then moves out of any invisible positions.
724 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
725 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
728 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
729 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
730 if (posMoved != pos.Position())
731 pos.SetPosition(posMoved);
732 if (vs.ProtectionActive()) {
733 if (moveDir > 0) {
734 if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1)].IsProtected()) {
735 while ((pos.Position() < pdoc->Length()) &&
736 (vs.styles[pdoc->StyleAt(pos.Position())].IsProtected()))
737 pos.Add(1);
739 } else if (moveDir < 0) {
740 if (vs.styles[pdoc->StyleAt(pos.Position())].IsProtected()) {
741 while ((pos.Position() > 0) &&
742 (vs.styles[pdoc->StyleAt(pos.Position() - 1)].IsProtected()))
743 pos.Add(-1);
747 return pos;
750 int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
751 bool simpleCaret = (sel.Count() == 1) && sel.Empty();
752 SelectionPosition spCaret = sel.Last();
754 int delta = newPos.Position() - sel.MainCaret();
755 newPos = ClampPositionIntoDocument(newPos);
756 newPos = MovePositionOutsideChar(newPos, delta);
757 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
758 // Can't turn into multiple selection so clear additional selections
759 InvalidateSelection(SelectionRange(newPos), true);
760 SelectionRange rangeMain = sel.RangeMain();
761 sel.SetSelection(rangeMain);
763 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
764 // Switching to rectangular
765 InvalidateSelection(sel.RangeMain(), false);
766 SelectionRange rangeMain = sel.RangeMain();
767 sel.Clear();
768 sel.Rectangular() = rangeMain;
770 if (selt != Selection::noSel) {
771 sel.selType = selt;
773 if (selt != Selection::noSel || sel.MoveExtends()) {
774 SetSelection(newPos);
775 } else {
776 SetEmptySelection(newPos);
778 ShowCaretAtCurrentPosition();
780 int currentLine = pdoc->LineFromPosition(newPos.Position());
781 if (ensureVisible) {
782 // In case in need of wrapping to ensure DisplayFromDoc works.
783 if (currentLine >= wrapPending.start)
784 WrapLines(wsAll);
785 XYScrollPosition newXY = XYScrollToMakeVisible(
786 SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret), xysDefault);
787 if (simpleCaret && (newXY.xOffset == xOffset)) {
788 // simple vertical scroll then invalidate
789 ScrollTo(newXY.topLine);
790 InvalidateSelection(SelectionRange(spCaret), true);
791 } else {
792 SetXYScroll(newXY);
796 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
797 RedrawSelMargin();
799 return 0;
802 int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
803 return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
806 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
807 pos = ClampPositionIntoDocument(pos);
808 pos = MovePositionOutsideChar(pos, moveDir);
809 int lineDoc = pdoc->LineFromPosition(pos.Position());
810 if (cs.GetVisible(lineDoc)) {
811 return pos;
812 } else {
813 int lineDisplay = cs.DisplayFromDoc(lineDoc);
814 if (moveDir > 0) {
815 // lineDisplay is already line before fold as lines in fold use display line of line after fold
816 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
817 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
818 } else {
819 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
820 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
825 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
826 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
829 Point Editor::PointMainCaret() {
830 return LocationFromPosition(sel.Range(sel.Main()).caret);
834 * Choose the x position that the caret will try to stick to
835 * as it moves up and down.
837 void Editor::SetLastXChosen() {
838 Point pt = PointMainCaret();
839 lastXChosen = static_cast<int>(pt.x) + xOffset;
842 void Editor::ScrollTo(int line, bool moveThumb) {
843 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
844 if (topLineNew != topLine) {
845 // Try to optimise small scrolls
846 #ifndef UNDER_CE
847 int linesToMove = topLine - topLineNew;
848 bool performBlit = (abs(linesToMove) <= 10) && (paintState == notPainting);
849 willRedrawAll = !performBlit;
850 #endif
851 SetTopLine(topLineNew);
852 // Optimize by styling the view as this will invalidate any needed area
853 // which could abort the initial paint if discovered later.
854 StyleToPositionInView(PositionAfterArea(GetClientRectangle()));
855 #ifndef UNDER_CE
856 // Perform redraw rather than scroll if many lines would be redrawn anyway.
857 if (performBlit) {
858 ScrollText(linesToMove);
859 } else {
860 Redraw();
862 willRedrawAll = false;
863 #else
864 Redraw();
865 #endif
866 if (moveThumb) {
867 SetVerticalScrollPos();
872 void Editor::ScrollText(int /* linesToMove */) {
873 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
874 Redraw();
877 void Editor::HorizontalScrollTo(int xPos) {
878 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
879 if (xPos < 0)
880 xPos = 0;
881 if (!Wrapping() && (xOffset != xPos)) {
882 xOffset = xPos;
883 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
884 SetHorizontalScrollPos();
885 RedrawRect(GetClientRectangle());
889 void Editor::VerticalCentreCaret() {
890 int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
891 int lineDisplay = cs.DisplayFromDoc(lineDoc);
892 int newTop = lineDisplay - (LinesOnScreen() / 2);
893 if (topLine != newTop) {
894 SetTopLine(newTop > 0 ? newTop : 0);
895 RedrawRect(GetClientRectangle());
899 // Avoid 64 bit compiler warnings.
900 // Scintilla does not support text buffers larger than 2**31
901 static int istrlen(const char *s) {
902 return static_cast<int>(s ? strlen(s) : 0);
905 void Editor::MoveSelectedLines(int lineDelta) {
907 // if selection doesn't start at the beginning of the line, set the new start
908 int selectionStart = SelectionStart().Position();
909 int startLine = pdoc->LineFromPosition(selectionStart);
910 int beginningOfStartLine = pdoc->LineStart(startLine);
911 selectionStart = beginningOfStartLine;
913 // if selection doesn't end at the beginning of a line greater than that of the start,
914 // then set it at the beginning of the next one
915 int selectionEnd = SelectionEnd().Position();
916 int endLine = pdoc->LineFromPosition(selectionEnd);
917 int beginningOfEndLine = pdoc->LineStart(endLine);
918 bool appendEol = false;
919 if (selectionEnd > beginningOfEndLine
920 || selectionStart == selectionEnd) {
921 selectionEnd = pdoc->LineStart(endLine + 1);
922 appendEol = (selectionEnd == pdoc->Length() && pdoc->LineFromPosition(selectionEnd) == endLine);
925 // if there's nowhere for the selection to move
926 // (i.e. at the beginning going up or at the end going down),
927 // stop it right there!
928 if ((selectionStart == 0 && lineDelta < 0)
929 || (selectionEnd == pdoc->Length() && lineDelta > 0)
930 || selectionStart == selectionEnd) {
931 return;
934 UndoGroup ug(pdoc);
936 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
937 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
938 ClearSelection();
939 selectionEnd = CurrentPosition();
941 SetSelection(selectionStart, selectionEnd);
943 SelectionText selectedText;
944 CopySelectionRange(&selectedText);
946 int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
947 Point currentLocation = LocationFromPosition(CurrentPosition());
948 int currentLine = LineFromLocation(currentLocation);
950 if (appendEol)
951 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
952 ClearSelection();
954 const char *eol = StringFromEOLMode(pdoc->eolMode);
955 if (currentLine + lineDelta >= pdoc->LinesTotal())
956 pdoc->InsertString(pdoc->Length(), eol, istrlen(eol));
957 GoToLine(currentLine + lineDelta);
959 selectionLength = pdoc->InsertString(CurrentPosition(), selectedText.Data(), selectionLength);
960 if (appendEol) {
961 const int lengthInserted = pdoc->InsertString(CurrentPosition() + selectionLength, eol, istrlen(eol));
962 selectionLength += lengthInserted;
964 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
967 void Editor::MoveSelectedLinesUp() {
968 MoveSelectedLines(-1);
971 void Editor::MoveSelectedLinesDown() {
972 MoveSelectedLines(1);
975 void Editor::MoveCaretInsideView(bool ensureVisible) {
976 PRectangle rcClient = GetTextRectangle();
977 Point pt = PointMainCaret();
978 if (pt.y < rcClient.top) {
979 MovePositionTo(SPositionFromLocation(
980 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top)),
981 false, false, UserVirtualSpace()),
982 Selection::noSel, ensureVisible);
983 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
984 int yOfLastLineFullyDisplayed = static_cast<int>(rcClient.top) + (LinesOnScreen() - 1) * vs.lineHeight;
985 MovePositionTo(SPositionFromLocation(
986 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top) + yOfLastLineFullyDisplayed),
987 false, false, UserVirtualSpace()),
988 Selection::noSel, ensureVisible);
992 int Editor::DisplayFromPosition(int pos) {
993 AutoSurface surface(this);
994 return view.DisplayFromPosition(surface, *this, pos, vs);
998 * Ensure the caret is reasonably visible in context.
1000 Caret policy in SciTE
1002 If slop is set, we can define a slop value.
1003 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1004 This zone is defined as a number of pixels near the vertical margins,
1005 and as a number of lines near the horizontal margins.
1006 By keeping the caret away from the edges, it is seen within its context,
1007 so it is likely that the identifier that the caret is on can be completely seen,
1008 and that the current line is seen with some of the lines following it which are
1009 often dependent on that line.
1011 If strict is set, the policy is enforced... strictly.
1012 The caret is centred on the display if slop is not set,
1013 and cannot go in the UZ if slop is set.
1015 If jumps is set, the display is moved more energetically
1016 so the caret can move in the same direction longer before the policy is applied again.
1017 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1019 If even is not set, instead of having symmetrical UZs,
1020 the left and bottom UZs are extended up to right and top UZs respectively.
1021 This way, we favour the displaying of useful information: the beginning of lines,
1022 where most code reside, and the lines after the caret, eg. the body of a function.
1024 | | | | |
1025 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1026 | | | | | visibility or going into the UZ) display is...
1027 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1028 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1029 0 | 0 | 0 | 1 | Yes | moved by one position
1030 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1031 0 | 0 | 1 | 1 | Yes | centred on the caret
1032 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1033 0 | 1 | - | 1 | No, caret is always centred | -
1034 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1035 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1036 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1037 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1038 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1039 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1040 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1043 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange &range, const XYScrollOptions options) {
1044 PRectangle rcClient = GetTextRectangle();
1045 Point pt = LocationFromPosition(range.caret);
1046 Point ptAnchor = LocationFromPosition(range.anchor);
1047 const Point ptOrigin = GetVisibleOriginInMain();
1048 pt.x += ptOrigin.x;
1049 pt.y += ptOrigin.y;
1050 ptAnchor.x += ptOrigin.x;
1051 ptAnchor.y += ptOrigin.y;
1052 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1054 XYScrollPosition newXY(xOffset, topLine);
1055 if (rcClient.Empty()) {
1056 return newXY;
1059 // Vertical positioning
1060 if ((options & xysVertical) && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1061 const int lineCaret = DisplayFromPosition(range.caret.Position());
1062 const int linesOnScreen = LinesOnScreen();
1063 const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1064 const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
1065 const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
1066 const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
1067 const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
1069 // It should be possible to scroll the window to show the caret,
1070 // but this fails to remove the caret on GTK+
1071 if (bSlop) { // A margin is defined
1072 int yMoveT, yMoveB;
1073 if (bStrict) {
1074 int yMarginT, yMarginB;
1075 if (!(options & xysUseMargin)) {
1076 // In drag mode, avoid moves
1077 // otherwise, a double click will select several lines.
1078 yMarginT = yMarginB = 0;
1079 } else {
1080 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1081 // a maximum of slightly less than half the heigth of the text area.
1082 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1083 if (bEven) {
1084 yMarginB = yMarginT;
1085 } else {
1086 yMarginB = linesOnScreen - yMarginT - 1;
1089 yMoveT = yMarginT;
1090 if (bEven) {
1091 if (bJump) {
1092 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1094 yMoveB = yMoveT;
1095 } else {
1096 yMoveB = linesOnScreen - yMoveT - 1;
1098 if (lineCaret < topLine + yMarginT) {
1099 // Caret goes too high
1100 newXY.topLine = lineCaret - yMoveT;
1101 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1102 // Caret goes too low
1103 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1105 } else { // Not strict
1106 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1107 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1108 if (bEven) {
1109 yMoveB = yMoveT;
1110 } else {
1111 yMoveB = linesOnScreen - yMoveT - 1;
1113 if (lineCaret < topLine) {
1114 // Caret goes too high
1115 newXY.topLine = lineCaret - yMoveT;
1116 } else if (lineCaret > topLine + linesOnScreen - 1) {
1117 // Caret goes too low
1118 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1121 } else { // No slop
1122 if (!bStrict && !bJump) {
1123 // Minimal move
1124 if (lineCaret < topLine) {
1125 // Caret goes too high
1126 newXY.topLine = lineCaret;
1127 } else if (lineCaret > topLine + linesOnScreen - 1) {
1128 // Caret goes too low
1129 if (bEven) {
1130 newXY.topLine = lineCaret - linesOnScreen + 1;
1131 } else {
1132 newXY.topLine = lineCaret;
1135 } else { // Strict or going out of display
1136 if (bEven) {
1137 // Always center caret
1138 newXY.topLine = lineCaret - halfScreen;
1139 } else {
1140 // Always put caret on top of display
1141 newXY.topLine = lineCaret;
1145 if (!(range.caret == range.anchor)) {
1146 const int lineAnchor = DisplayFromPosition(range.anchor.Position());
1147 if (lineAnchor < lineCaret) {
1148 // Shift up to show anchor or as much of range as possible
1149 newXY.topLine = std::min(newXY.topLine, lineAnchor);
1150 newXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());
1151 } else {
1152 // Shift down to show anchor or as much of range as possible
1153 newXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());
1154 newXY.topLine = std::min(newXY.topLine, lineCaret);
1157 newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
1160 // Horizontal positioning
1161 if ((options & xysHorizontal) && !Wrapping()) {
1162 const int halfScreen = Platform::Maximum(static_cast<int>(rcClient.Width()) - 4, 4) / 2;
1163 const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
1164 const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
1165 const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
1166 const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
1168 if (bSlop) { // A margin is defined
1169 int xMoveL, xMoveR;
1170 if (bStrict) {
1171 int xMarginL, xMarginR;
1172 if (!(options & xysUseMargin)) {
1173 // In drag mode, avoid moves unless very near of the margin
1174 // otherwise, a simple click will select text.
1175 xMarginL = xMarginR = 2;
1176 } else {
1177 // xMargin must equal to caretXSlop, with a minimum of 2 and
1178 // a maximum of slightly less than half the width of the text area.
1179 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1180 if (bEven) {
1181 xMarginL = xMarginR;
1182 } else {
1183 xMarginL = static_cast<int>(rcClient.Width()) - xMarginR - 4;
1186 if (bJump && bEven) {
1187 // Jump is used only in even mode
1188 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1189 } else {
1190 xMoveL = xMoveR = 0; // Not used, avoid a warning
1192 if (pt.x < rcClient.left + xMarginL) {
1193 // Caret is on the left of the display
1194 if (bJump && bEven) {
1195 newXY.xOffset -= xMoveL;
1196 } else {
1197 // Move just enough to allow to display the caret
1198 newXY.xOffset -= static_cast<int>((rcClient.left + xMarginL) - pt.x);
1200 } else if (pt.x >= rcClient.right - xMarginR) {
1201 // Caret is on the right of the display
1202 if (bJump && bEven) {
1203 newXY.xOffset += xMoveR;
1204 } else {
1205 // Move just enough to allow to display the caret
1206 newXY.xOffset += static_cast<int>(pt.x - (rcClient.right - xMarginR) + 1);
1209 } else { // Not strict
1210 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1211 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1212 if (bEven) {
1213 xMoveL = xMoveR;
1214 } else {
1215 xMoveL = static_cast<int>(rcClient.Width()) - xMoveR - 4;
1217 if (pt.x < rcClient.left) {
1218 // Caret is on the left of the display
1219 newXY.xOffset -= xMoveL;
1220 } else if (pt.x >= rcClient.right) {
1221 // Caret is on the right of the display
1222 newXY.xOffset += xMoveR;
1225 } else { // No slop
1226 if (bStrict ||
1227 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1228 // Strict or going out of display
1229 if (bEven) {
1230 // Center caret
1231 newXY.xOffset += static_cast<int>(pt.x - rcClient.left - halfScreen);
1232 } else {
1233 // Put caret on right
1234 newXY.xOffset += static_cast<int>(pt.x - rcClient.right + 1);
1236 } else {
1237 // Move just enough to allow to display the caret
1238 if (pt.x < rcClient.left) {
1239 // Caret is on the left of the display
1240 if (bEven) {
1241 newXY.xOffset -= static_cast<int>(rcClient.left - pt.x);
1242 } else {
1243 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1245 } else if (pt.x >= rcClient.right) {
1246 // Caret is on the right of the display
1247 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1251 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1252 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1253 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 2;
1254 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1255 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 2;
1256 if ((vs.caretStyle == CARETSTYLE_BLOCK) || view.imeCaretBlockOverride) {
1257 // Ensure we can see a good portion of the block caret
1258 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1261 if (!(range.caret == range.anchor)) {
1262 if (ptAnchor.x < pt.x) {
1263 // Shift to left to show anchor or as much of range as possible
1264 int maxOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.left) - 1;
1265 int minOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 1;
1266 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1267 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1268 } else {
1269 // Shift to right to show anchor or as much of range as possible
1270 int minOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.right) + 1;
1271 int maxOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 1;
1272 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1273 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1276 if (newXY.xOffset < 0) {
1277 newXY.xOffset = 0;
1281 return newXY;
1284 void Editor::SetXYScroll(XYScrollPosition newXY) {
1285 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1286 if (newXY.topLine != topLine) {
1287 SetTopLine(newXY.topLine);
1288 SetVerticalScrollPos();
1290 if (newXY.xOffset != xOffset) {
1291 xOffset = newXY.xOffset;
1292 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1293 if (newXY.xOffset > 0) {
1294 PRectangle rcText = GetTextRectangle();
1295 if (horizontalScrollBarVisible &&
1296 rcText.Width() + xOffset > scrollWidth) {
1297 scrollWidth = xOffset + static_cast<int>(rcText.Width());
1298 SetScrollBars();
1301 SetHorizontalScrollPos();
1303 Redraw();
1304 UpdateSystemCaret();
1308 void Editor::ScrollRange(SelectionRange range) {
1309 SetXYScroll(XYScrollToMakeVisible(range, xysDefault));
1312 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1313 SetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret),
1314 static_cast<XYScrollOptions>((useMargin?xysUseMargin:0)|(vert?xysVertical:0)|(horiz?xysHorizontal:0))));
1317 void Editor::ShowCaretAtCurrentPosition() {
1318 if (hasFocus) {
1319 caret.active = true;
1320 caret.on = true;
1321 if (FineTickerAvailable()) {
1322 FineTickerCancel(tickCaret);
1323 if (caret.period > 0)
1324 FineTickerStart(tickCaret, caret.period, caret.period/10);
1325 } else {
1326 SetTicking(true);
1328 } else {
1329 caret.active = false;
1330 caret.on = false;
1331 if (FineTickerAvailable()) {
1332 FineTickerCancel(tickCaret);
1335 InvalidateCaret();
1338 void Editor::DropCaret() {
1339 caret.active = false;
1340 if (FineTickerAvailable()) {
1341 FineTickerCancel(tickCaret);
1343 InvalidateCaret();
1346 void Editor::CaretSetPeriod(int period) {
1347 if (caret.period != period) {
1348 caret.period = period;
1349 caret.on = true;
1350 if (FineTickerAvailable()) {
1351 FineTickerCancel(tickCaret);
1352 if ((caret.active) && (caret.period > 0))
1353 FineTickerStart(tickCaret, caret.period, caret.period/10);
1355 InvalidateCaret();
1359 void Editor::InvalidateCaret() {
1360 if (posDrag.IsValid()) {
1361 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1362 } else {
1363 for (size_t r=0; r<sel.Count(); r++) {
1364 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1367 UpdateSystemCaret();
1370 void Editor::UpdateSystemCaret() {
1373 bool Editor::Wrapping() const {
1374 return vs.wrapState != eWrapNone;
1377 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1378 //Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd);
1379 if (wrapPending.AddRange(docLineStart, docLineEnd)) {
1380 view.llc.Invalidate(LineLayout::llPositions);
1382 // Wrap lines during idle.
1383 if (Wrapping() && wrapPending.NeedsWrap()) {
1384 SetIdle(true);
1388 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1389 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(lineToWrap, *this));
1390 int linesWrapped = 1;
1391 if (ll) {
1392 view.LayoutLine(*this, lineToWrap, surface, vs, ll, wrapWidth);
1393 linesWrapped = ll->lines;
1395 return cs.SetHeight(lineToWrap, linesWrapped +
1396 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1399 // Perform wrapping for a subset of the lines needing wrapping.
1400 // wsAll: wrap all lines which need wrapping in this single call
1401 // wsVisible: wrap currently visible lines
1402 // wsIdle: wrap one page + 100 lines
1403 // Return true if wrapping occurred.
1404 bool Editor::WrapLines(enum wrapScope ws) {
1405 int goodTopLine = topLine;
1406 bool wrapOccurred = false;
1407 if (!Wrapping()) {
1408 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1409 wrapWidth = LineLayout::wrapWidthInfinite;
1410 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1411 cs.SetHeight(lineDoc, 1 +
1412 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1414 wrapOccurred = true;
1416 wrapPending.Reset();
1418 } else if (wrapPending.NeedsWrap()) {
1419 wrapPending.start = std::min(wrapPending.start, pdoc->LinesTotal());
1420 if (!SetIdle(true)) {
1421 // Idle processing not supported so full wrap required.
1422 ws = wsAll;
1424 // Decide where to start wrapping
1425 int lineToWrap = wrapPending.start;
1426 int lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal());
1427 const int lineDocTop = cs.DocFromDisplay(topLine);
1428 const int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1429 if (ws == wsVisible) {
1430 lineToWrap = Platform::Clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal());
1431 // Priority wrap to just after visible area.
1432 // Since wrapping could reduce display lines, treat each
1433 // as taking only one display line.
1434 lineToWrapEnd = lineDocTop;
1435 int lines = LinesOnScreen() + 1;
1436 while ((lineToWrapEnd < cs.LinesInDoc()) && (lines>0)) {
1437 if (cs.GetVisible(lineToWrapEnd))
1438 lines--;
1439 lineToWrapEnd++;
1441 // .. and if the paint window is outside pending wraps
1442 if ((lineToWrap > wrapPending.end) || (lineToWrapEnd < wrapPending.start)) {
1443 // Currently visible text does not need wrapping
1444 return false;
1446 } else if (ws == wsIdle) {
1447 lineToWrapEnd = lineToWrap + LinesOnScreen() + 100;
1449 const int lineEndNeedWrap = std::min(wrapPending.end, pdoc->LinesTotal());
1450 lineToWrapEnd = std::min(lineToWrapEnd, lineEndNeedWrap);
1452 // Ensure all lines being wrapped are styled.
1453 pdoc->EnsureStyledTo(pdoc->LineStart(lineToWrapEnd));
1455 if (lineToWrap < lineToWrapEnd) {
1457 PRectangle rcTextArea = GetClientRectangle();
1458 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1459 rcTextArea.right -= vs.rightMarginWidth;
1460 wrapWidth = static_cast<int>(rcTextArea.Width());
1461 RefreshStyleData();
1462 AutoSurface surface(this);
1463 if (surface) {
1464 //Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
1466 while (lineToWrap < lineToWrapEnd) {
1467 if (WrapOneLine(surface, lineToWrap)) {
1468 wrapOccurred = true;
1470 wrapPending.Wrapped(lineToWrap);
1471 lineToWrap++;
1474 goodTopLine = cs.DisplayFromDoc(lineDocTop) + std::min(subLineTop, cs.GetHeight(lineDocTop)-1);
1478 // If wrapping is done, bring it to resting position
1479 if (wrapPending.start >= lineEndNeedWrap) {
1480 wrapPending.Reset();
1484 if (wrapOccurred) {
1485 SetScrollBars();
1486 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1487 SetVerticalScrollPos();
1490 return wrapOccurred;
1493 void Editor::LinesJoin() {
1494 if (!RangeContainsProtected(targetStart, targetEnd)) {
1495 UndoGroup ug(pdoc);
1496 bool prevNonWS = true;
1497 for (int pos = targetStart; pos < targetEnd; pos++) {
1498 if (pdoc->IsPositionInLineEnd(pos)) {
1499 targetEnd -= pdoc->LenChar(pos);
1500 pdoc->DelChar(pos);
1501 if (prevNonWS) {
1502 // Ensure at least one space separating previous lines
1503 const int lengthInserted = pdoc->InsertString(pos, " ", 1);
1504 targetEnd += lengthInserted;
1506 } else {
1507 prevNonWS = pdoc->CharAt(pos) != ' ';
1513 const char *Editor::StringFromEOLMode(int eolMode) {
1514 if (eolMode == SC_EOL_CRLF) {
1515 return "\r\n";
1516 } else if (eolMode == SC_EOL_CR) {
1517 return "\r";
1518 } else {
1519 return "\n";
1523 void Editor::LinesSplit(int pixelWidth) {
1524 if (!RangeContainsProtected(targetStart, targetEnd)) {
1525 if (pixelWidth == 0) {
1526 PRectangle rcText = GetTextRectangle();
1527 pixelWidth = static_cast<int>(rcText.Width());
1529 int lineStart = pdoc->LineFromPosition(targetStart);
1530 int lineEnd = pdoc->LineFromPosition(targetEnd);
1531 const char *eol = StringFromEOLMode(pdoc->eolMode);
1532 UndoGroup ug(pdoc);
1533 for (int line = lineStart; line <= lineEnd; line++) {
1534 AutoSurface surface(this);
1535 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
1536 if (surface && ll) {
1537 unsigned int posLineStart = pdoc->LineStart(line);
1538 view.LayoutLine(*this, line, surface, vs, ll, pixelWidth);
1539 int lengthInsertedTotal = 0;
1540 for (int subLine = 1; subLine < ll->lines; subLine++) {
1541 const int lengthInserted = pdoc->InsertString(
1542 static_cast<int>(posLineStart + lengthInsertedTotal +
1543 ll->LineStart(subLine)),
1544 eol, istrlen(eol));
1545 targetEnd += lengthInserted;
1546 lengthInsertedTotal += lengthInserted;
1549 lineEnd = pdoc->LineFromPosition(targetEnd);
1554 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1555 if (vs.fixedColumnWidth == 0)
1556 return;
1558 AllocateGraphics();
1559 RefreshStyleData();
1560 RefreshPixMaps(surfWindow);
1562 // On GTK+ with Ubuntu overlay scroll bars, the surface may have been finished
1563 // at this point. The Initialised call checks for this case and sets the status
1564 // to be bad which avoids crashes in following calls.
1565 if (!surfWindow->Initialised()) {
1566 return;
1569 PRectangle rcMargin = GetClientRectangle();
1570 Point ptOrigin = GetVisibleOriginInMain();
1571 rcMargin.Move(0, -ptOrigin.y);
1572 rcMargin.left = 0;
1573 rcMargin.right = static_cast<XYPOSITION>(vs.fixedColumnWidth);
1575 if (!rc.Intersects(rcMargin))
1576 return;
1578 Surface *surface;
1579 if (view.bufferedDraw) {
1580 surface = marginView.pixmapSelMargin;
1581 } else {
1582 surface = surfWindow;
1585 // Clip vertically to paint area to avoid drawing line numbers
1586 if (rcMargin.bottom > rc.bottom)
1587 rcMargin.bottom = rc.bottom;
1588 if (rcMargin.top < rc.top)
1589 rcMargin.top = rc.top;
1591 marginView.PaintMargin(surface, topLine, rc, rcMargin, *this, vs);
1593 if (view.bufferedDraw) {
1594 surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *marginView.pixmapSelMargin);
1598 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
1599 view.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1600 marginView.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1601 if (view.bufferedDraw) {
1602 PRectangle rcClient = GetClientRectangle();
1603 if (!view.pixmapLine->Initialised()) {
1605 view.pixmapLine->InitPixMap(static_cast<int>(rcClient.Width()), vs.lineHeight,
1606 surfaceWindow, wMain.GetID());
1608 if (!marginView.pixmapSelMargin->Initialised()) {
1609 marginView.pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
1610 static_cast<int>(rcClient.Height()), surfaceWindow, wMain.GetID());
1615 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
1616 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
1617 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
1618 AllocateGraphics();
1620 RefreshStyleData();
1621 if (paintState == paintAbandoned)
1622 return; // Scroll bars may have changed so need redraw
1623 RefreshPixMaps(surfaceWindow);
1625 paintAbandonedByStyling = false;
1627 StyleToPositionInView(PositionAfterArea(rcArea));
1629 PRectangle rcClient = GetClientRectangle();
1630 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
1631 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1633 if (NotifyUpdateUI()) {
1634 RefreshStyleData();
1635 RefreshPixMaps(surfaceWindow);
1638 // Wrap the visible lines if needed.
1639 if (WrapLines(wsVisible)) {
1640 // The wrapping process has changed the height of some lines so
1641 // abandon this paint for a complete repaint.
1642 if (AbandonPaint()) {
1643 return;
1645 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
1647 PLATFORM_ASSERT(marginView.pixmapSelPattern->Initialised());
1649 if (!view.bufferedDraw)
1650 surfaceWindow->SetClip(rcArea);
1652 if (paintState != paintAbandoned) {
1653 if (vs.marginInside) {
1654 PaintSelMargin(surfaceWindow, rcArea);
1655 PRectangle rcRightMargin = rcClient;
1656 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
1657 if (rcArea.Intersects(rcRightMargin)) {
1658 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
1660 } else { // Else separate view so separate paint event but leftMargin included to allow overlap
1661 PRectangle rcLeftMargin = rcArea;
1662 rcLeftMargin.left = 0;
1663 rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
1664 if (rcArea.Intersects(rcLeftMargin)) {
1665 surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[STYLE_DEFAULT].back);
1670 if (paintState == paintAbandoned) {
1671 // Either styling or NotifyUpdateUI noticed that painting is needed
1672 // outside the current painting rectangle
1673 //Platform::DebugPrintf("Abandoning paint\n");
1674 if (Wrapping()) {
1675 if (paintAbandonedByStyling) {
1676 // Styling has spilled over a line end, such as occurs by starting a multiline
1677 // comment. The width of subsequent text may have changed, so rewrap.
1678 NeedWrapping(cs.DocFromDisplay(topLine));
1681 return;
1684 view.PaintText(surfaceWindow, *this, rcArea, rcClient, vs);
1686 if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) {
1687 if (FineTickerAvailable()) {
1688 scrollWidth = view.lineWidthMaxSeen;
1689 if (!FineTickerRunning(tickWiden)) {
1690 FineTickerStart(tickWiden, 50, 5);
1695 NotifyPainted();
1698 // This is mostly copied from the Paint method but with some things omitted
1699 // such as the margin markers, line numbers, selection and caret
1700 // Should be merged back into a combined Draw method.
1701 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
1702 if (!pfr)
1703 return 0;
1705 AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
1706 if (!surface)
1707 return 0;
1708 AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
1709 if (!surfaceMeasure) {
1710 return 0;
1712 return view.FormatRange(draw, pfr, surface, surfaceMeasure, *this, vs);
1715 int Editor::TextWidth(int style, const char *text) {
1716 RefreshStyleData();
1717 AutoSurface surface(this);
1718 if (surface) {
1719 return static_cast<int>(surface->WidthText(vs.styles[style].font, text, istrlen(text)));
1720 } else {
1721 return 1;
1725 // Empty method is overridden on GTK+ to show / hide scrollbars
1726 void Editor::ReconfigureScrollBars() {}
1728 void Editor::SetScrollBars() {
1729 RefreshStyleData();
1731 int nMax = MaxScrollPos();
1732 int nPage = LinesOnScreen();
1733 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
1734 if (modified) {
1735 DwellEnd(true);
1738 // TODO: ensure always showing as many lines as possible
1739 // May not be, if, for example, window made larger
1740 if (topLine > MaxScrollPos()) {
1741 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
1742 SetVerticalScrollPos();
1743 Redraw();
1745 if (modified) {
1746 if (!AbandonPaint())
1747 Redraw();
1749 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
1752 void Editor::ChangeSize() {
1753 DropGraphics(false);
1754 SetScrollBars();
1755 if (Wrapping()) {
1756 PRectangle rcTextArea = GetClientRectangle();
1757 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1758 rcTextArea.right -= vs.rightMarginWidth;
1759 if (wrapWidth != rcTextArea.Width()) {
1760 NeedWrapping();
1761 Redraw();
1766 int Editor::InsertSpace(int position, unsigned int spaces) {
1767 if (spaces > 0) {
1768 std::string spaceText(spaces, ' ');
1769 const int lengthInserted = pdoc->InsertString(position, spaceText.c_str(), spaces);
1770 position += lengthInserted;
1772 return position;
1775 void Editor::AddChar(char ch) {
1776 char s[2];
1777 s[0] = ch;
1778 s[1] = '\0';
1779 AddCharUTF(s, 1);
1782 void Editor::FilterSelections() {
1783 if (!additionalSelectionTyping && (sel.Count() > 1)) {
1784 SelectionRange rangeOnly = sel.RangeMain();
1785 InvalidateSelection(rangeOnly, true);
1786 sel.SetSelection(rangeOnly);
1790 static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
1791 return *a < *b;
1794 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
1795 void Editor::AddCharUTF(const char *s, unsigned int len, bool treatAsDBCS) {
1796 FilterSelections();
1798 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
1800 // Vector elements point into selection in order to change selection.
1801 std::vector<SelectionRange *> selPtrs;
1802 for (size_t r = 0; r < sel.Count(); r++) {
1803 selPtrs.push_back(&sel.Range(r));
1805 // Order selections by position in document.
1806 std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
1808 // Loop in reverse to avoid disturbing positions of selections yet to be processed.
1809 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
1810 rit != selPtrs.rend(); ++rit) {
1811 SelectionRange *currentSel = *rit;
1812 if (!RangeContainsProtected(currentSel->Start().Position(),
1813 currentSel->End().Position())) {
1814 int positionInsert = currentSel->Start().Position();
1815 if (!currentSel->Empty()) {
1816 if (currentSel->Length()) {
1817 pdoc->DeleteChars(positionInsert, currentSel->Length());
1818 currentSel->ClearVirtualSpace();
1819 } else {
1820 // Range is all virtual so collapse to start of virtual space
1821 currentSel->MinimizeVirtualSpace();
1823 } else if (inOverstrike) {
1824 if (positionInsert < pdoc->Length()) {
1825 if (!pdoc->IsPositionInLineEnd(positionInsert)) {
1826 pdoc->DelChar(positionInsert);
1827 currentSel->ClearVirtualSpace();
1831 positionInsert = InsertSpace(positionInsert, currentSel->caret.VirtualSpace());
1832 const int lengthInserted = pdoc->InsertString(positionInsert, s, len);
1833 if (lengthInserted > 0) {
1834 currentSel->caret.SetPosition(positionInsert + lengthInserted);
1835 currentSel->anchor.SetPosition(positionInsert + lengthInserted);
1837 currentSel->ClearVirtualSpace();
1838 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1839 if (Wrapping()) {
1840 AutoSurface surface(this);
1841 if (surface) {
1842 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
1843 SetScrollBars();
1844 SetVerticalScrollPos();
1845 Redraw();
1852 if (Wrapping()) {
1853 SetScrollBars();
1855 ThinRectangularRange();
1856 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1857 EnsureCaretVisible();
1858 // Avoid blinking during rapid typing:
1859 ShowCaretAtCurrentPosition();
1860 if ((caretSticky == SC_CARETSTICKY_OFF) ||
1861 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
1862 SetLastXChosen();
1865 if (treatAsDBCS) {
1866 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
1867 static_cast<unsigned char>(s[1]));
1868 } else if (len > 0) {
1869 int byte = static_cast<unsigned char>(s[0]);
1870 if ((byte < 0xC0) || (1 == len)) {
1871 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
1872 // characters when not in UTF-8 mode.
1873 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
1874 // characters representing themselves.
1875 } else {
1876 unsigned int utf32[1] = { 0 };
1877 UTF32FromUTF8(s, len, utf32, ELEMENTS(utf32));
1878 byte = utf32[0];
1880 NotifyChar(byte);
1883 if (recordingMacro) {
1884 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
1888 void Editor::FillVirtualSpace() {
1889 const bool tmpOverstrike = inOverstrike;
1890 inOverstrike = false; // not allow to be deleted twice.
1891 AddCharUTF("", 0);
1892 inOverstrike = tmpOverstrike;
1895 void Editor::InsertPaste(const char *text, int len) {
1896 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
1897 SelectionPosition selStart = sel.Start();
1898 selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
1899 const int lengthInserted = pdoc->InsertString(selStart.Position(), text, len);
1900 if (lengthInserted > 0) {
1901 SetEmptySelection(selStart.Position() + lengthInserted);
1903 } else {
1904 // SC_MULTIPASTE_EACH
1905 for (size_t r=0; r<sel.Count(); r++) {
1906 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
1907 sel.Range(r).End().Position())) {
1908 int positionInsert = sel.Range(r).Start().Position();
1909 if (!sel.Range(r).Empty()) {
1910 if (sel.Range(r).Length()) {
1911 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
1912 sel.Range(r).ClearVirtualSpace();
1913 } else {
1914 // Range is all virtual so collapse to start of virtual space
1915 sel.Range(r).MinimizeVirtualSpace();
1918 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
1919 const int lengthInserted = pdoc->InsertString(positionInsert, text, len);
1920 if (lengthInserted > 0) {
1921 sel.Range(r).caret.SetPosition(positionInsert + lengthInserted);
1922 sel.Range(r).anchor.SetPosition(positionInsert + lengthInserted);
1924 sel.Range(r).ClearVirtualSpace();
1930 void Editor::InsertPasteShape(const char *text, int len, PasteShape shape) {
1931 std::string convertedText;
1932 if (convertPastes) {
1933 // Convert line endings of the paste into our local line-endings mode
1934 convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
1935 len = static_cast<int>(convertedText.length());
1936 text = convertedText.c_str();
1938 if (shape == pasteRectangular) {
1939 PasteRectangular(sel.Start(), text, len);
1940 } else {
1941 if (shape == pasteLine) {
1942 int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
1943 int lengthInserted = pdoc->InsertString(insertPos, text, len);
1944 // add the newline if necessary
1945 if ((len > 0) && (text[len - 1] != '\n' && text[len - 1] != '\r')) {
1946 const char *endline = StringFromEOLMode(pdoc->eolMode);
1947 int length = static_cast<int>(strlen(endline));
1948 lengthInserted += pdoc->InsertString(insertPos + lengthInserted, endline, length);
1950 if (sel.MainCaret() == insertPos) {
1951 SetEmptySelection(sel.MainCaret() + lengthInserted);
1953 } else {
1954 InsertPaste(text, len);
1959 void Editor::ClearSelection(bool retainMultipleSelections) {
1960 if (!sel.IsRectangular() && !retainMultipleSelections)
1961 FilterSelections();
1962 UndoGroup ug(pdoc);
1963 for (size_t r=0; r<sel.Count(); r++) {
1964 if (!sel.Range(r).Empty()) {
1965 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
1966 sel.Range(r).End().Position())) {
1967 pdoc->DeleteChars(sel.Range(r).Start().Position(),
1968 sel.Range(r).Length());
1969 sel.Range(r) = SelectionRange(sel.Range(r).Start());
1973 ThinRectangularRange();
1974 sel.RemoveDuplicates();
1975 ClaimSelection();
1976 SetHoverIndicatorPosition(sel.MainCaret());
1979 void Editor::ClearAll() {
1981 UndoGroup ug(pdoc);
1982 if (0 != pdoc->Length()) {
1983 pdoc->DeleteChars(0, pdoc->Length());
1985 if (!pdoc->IsReadOnly()) {
1986 cs.Clear();
1987 pdoc->AnnotationClearAll();
1988 pdoc->MarginClearAll();
1992 view.ClearAllTabstops();
1994 sel.Clear();
1995 SetTopLine(0);
1996 SetVerticalScrollPos();
1997 InvalidateStyleRedraw();
2000 void Editor::ClearDocumentStyle() {
2001 Decoration *deco = pdoc->decorations.root;
2002 while (deco) {
2003 // Save next in case deco deleted
2004 Decoration *decoNext = deco->next;
2005 if (deco->indicator < INDIC_CONTAINER) {
2006 pdoc->decorations.SetCurrentIndicator(deco->indicator);
2007 pdoc->DecorationFillRange(0, 0, pdoc->Length());
2009 deco = decoNext;
2011 pdoc->StartStyling(0, '\377');
2012 pdoc->SetStyleFor(pdoc->Length(), 0);
2013 cs.ShowAll();
2014 SetAnnotationHeights(0, pdoc->LinesTotal());
2015 pdoc->ClearLevels();
2018 void Editor::CopyAllowLine() {
2019 SelectionText selectedText;
2020 CopySelectionRange(&selectedText, true);
2021 CopyToClipboard(selectedText);
2024 void Editor::Cut() {
2025 pdoc->CheckReadOnly();
2026 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
2027 Copy();
2028 ClearSelection();
2032 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
2033 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
2034 return;
2036 sel.Clear();
2037 sel.RangeMain() = SelectionRange(pos);
2038 int line = pdoc->LineFromPosition(sel.MainCaret());
2039 UndoGroup ug(pdoc);
2040 sel.RangeMain().caret = SelectionPosition(
2041 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
2042 int xInsert = XFromPosition(sel.RangeMain().caret);
2043 bool prevCr = false;
2044 while ((len > 0) && IsEOLChar(ptr[len-1]))
2045 len--;
2046 for (int i = 0; i < len; i++) {
2047 if (IsEOLChar(ptr[i])) {
2048 if ((ptr[i] == '\r') || (!prevCr))
2049 line++;
2050 if (line >= pdoc->LinesTotal()) {
2051 if (pdoc->eolMode != SC_EOL_LF)
2052 pdoc->InsertString(pdoc->Length(), "\r", 1);
2053 if (pdoc->eolMode != SC_EOL_CR)
2054 pdoc->InsertString(pdoc->Length(), "\n", 1);
2056 // Pad the end of lines with spaces if required
2057 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
2058 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
2059 while (XFromPosition(sel.MainCaret()) < xInsert) {
2060 const int lengthInserted = pdoc->InsertString(sel.MainCaret(), " ", 1);
2061 sel.RangeMain().caret.Add(lengthInserted);
2064 prevCr = ptr[i] == '\r';
2065 } else {
2066 const int lengthInserted = pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
2067 sel.RangeMain().caret.Add(lengthInserted);
2068 prevCr = false;
2071 SetEmptySelection(pos);
2074 bool Editor::CanPaste() {
2075 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
2078 void Editor::Clear() {
2079 // If multiple selections, don't delete EOLS
2080 if (sel.Empty()) {
2081 bool singleVirtual = false;
2082 if ((sel.Count() == 1) &&
2083 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
2084 sel.RangeMain().Start().VirtualSpace()) {
2085 singleVirtual = true;
2087 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
2088 for (size_t r=0; r<sel.Count(); r++) {
2089 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
2090 if (sel.Range(r).Start().VirtualSpace()) {
2091 if (sel.Range(r).anchor < sel.Range(r).caret)
2092 sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
2093 else
2094 sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
2096 if ((sel.Count() == 1) || !pdoc->IsPositionInLineEnd(sel.Range(r).caret.Position())) {
2097 pdoc->DelChar(sel.Range(r).caret.Position());
2098 sel.Range(r).ClearVirtualSpace();
2099 } // else multiple selection so don't eat line ends
2100 } else {
2101 sel.Range(r).ClearVirtualSpace();
2104 } else {
2105 ClearSelection();
2107 sel.RemoveDuplicates();
2108 ShowCaretAtCurrentPosition(); // Avoid blinking
2111 void Editor::SelectAll() {
2112 sel.Clear();
2113 SetSelection(0, pdoc->Length());
2114 Redraw();
2117 void Editor::Undo() {
2118 if (pdoc->CanUndo()) {
2119 InvalidateCaret();
2120 int newPos = pdoc->Undo();
2121 if (newPos >= 0)
2122 SetEmptySelection(newPos);
2123 EnsureCaretVisible();
2127 void Editor::Redo() {
2128 if (pdoc->CanRedo()) {
2129 int newPos = pdoc->Redo();
2130 if (newPos >= 0)
2131 SetEmptySelection(newPos);
2132 EnsureCaretVisible();
2136 void Editor::DelCharBack(bool allowLineStartDeletion) {
2137 RefreshStyleData();
2138 if (!sel.IsRectangular())
2139 FilterSelections();
2140 if (sel.IsRectangular())
2141 allowLineStartDeletion = false;
2142 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
2143 if (sel.Empty()) {
2144 for (size_t r=0; r<sel.Count(); r++) {
2145 if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
2146 if (sel.Range(r).caret.VirtualSpace()) {
2147 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
2148 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
2149 } else {
2150 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
2151 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
2152 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
2153 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
2154 UndoGroup ugInner(pdoc, !ug.Needed());
2155 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
2156 int indentationStep = pdoc->IndentSize();
2157 int indentationChange = indentation % indentationStep;
2158 if (indentationChange == 0)
2159 indentationChange = indentationStep;
2160 const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationChange);
2161 // SetEmptySelection
2162 sel.Range(r) = SelectionRange(posSelect);
2163 } else {
2164 pdoc->DelCharBack(sel.Range(r).caret.Position());
2168 } else {
2169 sel.Range(r).ClearVirtualSpace();
2172 ThinRectangularRange();
2173 } else {
2174 ClearSelection();
2176 sel.RemoveDuplicates();
2177 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
2178 // Avoid blinking during rapid typing:
2179 ShowCaretAtCurrentPosition();
2182 int Editor::ModifierFlags(bool shift, bool ctrl, bool alt, bool meta) {
2183 return
2184 (shift ? SCI_SHIFT : 0) |
2185 (ctrl ? SCI_CTRL : 0) |
2186 (alt ? SCI_ALT : 0) |
2187 (meta ? SCI_META : 0);
2190 void Editor::NotifyFocus(bool focus) {
2191 SCNotification scn = {};
2192 scn.nmhdr.code = focus ? SCN_FOCUSIN : SCN_FOCUSOUT;
2193 NotifyParent(scn);
2196 void Editor::SetCtrlID(int identifier) {
2197 ctrlID = identifier;
2200 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
2201 SCNotification scn = {};
2202 scn.nmhdr.code = SCN_STYLENEEDED;
2203 scn.position = endStyleNeeded;
2204 NotifyParent(scn);
2207 void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
2208 NotifyStyleToNeeded(endStyleNeeded);
2211 void Editor::NotifyLexerChanged(Document *, void *) {
2214 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
2215 errorStatus = status;
2218 void Editor::NotifyChar(int ch) {
2219 SCNotification scn = {};
2220 scn.nmhdr.code = SCN_CHARADDED;
2221 scn.ch = ch;
2222 NotifyParent(scn);
2225 void Editor::NotifySavePoint(bool isSavePoint) {
2226 SCNotification scn = {};
2227 if (isSavePoint) {
2228 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
2229 } else {
2230 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
2232 NotifyParent(scn);
2235 void Editor::NotifyModifyAttempt() {
2236 SCNotification scn = {};
2237 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
2238 NotifyParent(scn);
2241 void Editor::NotifyDoubleClick(Point pt, int modifiers) {
2242 SCNotification scn = {};
2243 scn.nmhdr.code = SCN_DOUBLECLICK;
2244 scn.line = LineFromLocation(pt);
2245 scn.position = PositionFromLocation(pt, true);
2246 scn.modifiers = modifiers;
2247 NotifyParent(scn);
2250 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
2251 NotifyDoubleClick(pt, ModifierFlags(shift, ctrl, alt));
2254 void Editor::NotifyHotSpotDoubleClicked(int position, int modifiers) {
2255 SCNotification scn = {};
2256 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
2257 scn.position = position;
2258 scn.modifiers = modifiers;
2259 NotifyParent(scn);
2262 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
2263 NotifyHotSpotDoubleClicked(position, ModifierFlags(shift, ctrl, alt));
2266 void Editor::NotifyHotSpotClicked(int position, int modifiers) {
2267 SCNotification scn = {};
2268 scn.nmhdr.code = SCN_HOTSPOTCLICK;
2269 scn.position = position;
2270 scn.modifiers = modifiers;
2271 NotifyParent(scn);
2274 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
2275 NotifyHotSpotClicked(position, ModifierFlags(shift, ctrl, alt));
2278 void Editor::NotifyHotSpotReleaseClick(int position, int modifiers) {
2279 SCNotification scn = {};
2280 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
2281 scn.position = position;
2282 scn.modifiers = modifiers;
2283 NotifyParent(scn);
2286 void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
2287 NotifyHotSpotReleaseClick(position, ModifierFlags(shift, ctrl, alt));
2290 bool Editor::NotifyUpdateUI() {
2291 if (needUpdateUI) {
2292 SCNotification scn = {};
2293 scn.nmhdr.code = SCN_UPDATEUI;
2294 scn.updated = needUpdateUI;
2295 NotifyParent(scn);
2296 needUpdateUI = 0;
2297 return true;
2299 return false;
2302 void Editor::NotifyPainted() {
2303 SCNotification scn = {};
2304 scn.nmhdr.code = SCN_PAINTED;
2305 NotifyParent(scn);
2308 void Editor::NotifyIndicatorClick(bool click, int position, int modifiers) {
2309 int mask = pdoc->decorations.AllOnFor(position);
2310 if ((click && mask) || pdoc->decorations.clickNotified) {
2311 SCNotification scn = {};
2312 pdoc->decorations.clickNotified = click;
2313 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
2314 scn.modifiers = modifiers;
2315 scn.position = position;
2316 NotifyParent(scn);
2320 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
2321 NotifyIndicatorClick(click, position, ModifierFlags(shift, ctrl, alt));
2324 bool Editor::NotifyMarginClick(Point pt, int modifiers) {
2325 int marginClicked = -1;
2326 int x = vs.textStart - vs.fixedColumnWidth;
2327 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
2328 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
2329 marginClicked = margin;
2330 x += vs.ms[margin].width;
2332 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
2333 int position = pdoc->LineStart(LineFromLocation(pt));
2334 if ((vs.ms[marginClicked].mask & SC_MASK_FOLDERS) && (foldAutomatic & SC_AUTOMATICFOLD_CLICK)) {
2335 const bool ctrl = (modifiers & SCI_CTRL) != 0;
2336 const bool shift = (modifiers & SCI_SHIFT) != 0;
2337 int lineClick = pdoc->LineFromPosition(position);
2338 if (shift && ctrl) {
2339 FoldAll(SC_FOLDACTION_TOGGLE);
2340 } else {
2341 int levelClick = pdoc->GetLevel(lineClick);
2342 if (levelClick & SC_FOLDLEVELHEADERFLAG) {
2343 if (shift) {
2344 // Ensure all children visible
2345 FoldExpand(lineClick, SC_FOLDACTION_EXPAND, levelClick);
2346 } else if (ctrl) {
2347 FoldExpand(lineClick, SC_FOLDACTION_TOGGLE, levelClick);
2348 } else {
2349 // Toggle this line
2350 FoldLine(lineClick, SC_FOLDACTION_TOGGLE);
2354 return true;
2356 SCNotification scn = {};
2357 scn.nmhdr.code = SCN_MARGINCLICK;
2358 scn.modifiers = modifiers;
2359 scn.position = position;
2360 scn.margin = marginClicked;
2361 NotifyParent(scn);
2362 return true;
2363 } else {
2364 return false;
2368 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
2369 return NotifyMarginClick(pt, ModifierFlags(shift, ctrl, alt));
2372 void Editor::NotifyNeedShown(int pos, int len) {
2373 SCNotification scn = {};
2374 scn.nmhdr.code = SCN_NEEDSHOWN;
2375 scn.position = pos;
2376 scn.length = len;
2377 NotifyParent(scn);
2380 void Editor::NotifyDwelling(Point pt, bool state) {
2381 SCNotification scn = {};
2382 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
2383 scn.position = PositionFromLocation(pt, true);
2384 scn.x = static_cast<int>(pt.x + vs.ExternalMarginWidth());
2385 scn.y = static_cast<int>(pt.y);
2386 NotifyParent(scn);
2389 void Editor::NotifyZoom() {
2390 SCNotification scn = {};
2391 scn.nmhdr.code = SCN_ZOOM;
2392 NotifyParent(scn);
2395 // Notifications from document
2396 void Editor::NotifyModifyAttempt(Document *, void *) {
2397 //Platform::DebugPrintf("** Modify Attempt\n");
2398 NotifyModifyAttempt();
2401 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
2402 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
2403 NotifySavePoint(atSavePoint);
2406 void Editor::CheckModificationForWrap(DocModification mh) {
2407 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
2408 view.llc.Invalidate(LineLayout::llCheckTextAndStyle);
2409 int lineDoc = pdoc->LineFromPosition(mh.position);
2410 int lines = Platform::Maximum(0, mh.linesAdded);
2411 if (Wrapping()) {
2412 NeedWrapping(lineDoc, lineDoc + lines + 1);
2414 RefreshStyleData();
2415 // Fix up annotation heights
2416 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
2420 // Move a position so it is still after the same character as before the insertion.
2421 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
2422 if (position > startInsertion) {
2423 return position + length;
2425 return position;
2428 // Move a position so it is still after the same character as before the deletion if that
2429 // character is still present else after the previous surviving character.
2430 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
2431 if (position > startDeletion) {
2432 int endDeletion = startDeletion + length;
2433 if (position > endDeletion) {
2434 return position - length;
2435 } else {
2436 return startDeletion;
2438 } else {
2439 return position;
2443 void Editor::NotifyModified(Document *, DocModification mh, void *) {
2444 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
2445 if (paintState == painting) {
2446 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
2448 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
2449 if (paintState == painting) {
2450 CheckForChangeOutsidePaint(
2451 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
2452 } else {
2453 // Could check that change is before last visible line.
2454 Redraw();
2457 if (mh.modificationType & SC_MOD_CHANGETABSTOPS) {
2458 Redraw();
2460 if (mh.modificationType & SC_MOD_LEXERSTATE) {
2461 if (paintState == painting) {
2462 CheckForChangeOutsidePaint(
2463 Range(mh.position, mh.position + mh.length));
2464 } else {
2465 Redraw();
2468 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
2469 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
2470 pdoc->IncrementStyleClock();
2472 if (paintState == notPainting) {
2473 if (mh.position < pdoc->LineStart(topLine)) {
2474 // Styling performed before this view
2475 Redraw();
2476 } else {
2477 InvalidateRange(mh.position, mh.position + mh.length);
2480 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
2481 view.llc.Invalidate(LineLayout::llCheckTextAndStyle);
2483 } else {
2484 // Move selection and brace highlights
2485 if (mh.modificationType & SC_MOD_INSERTTEXT) {
2486 sel.MovePositions(true, mh.position, mh.length);
2487 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
2488 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
2489 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
2490 sel.MovePositions(false, mh.position, mh.length);
2491 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
2492 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
2494 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && cs.HiddenLines()) {
2495 // Some lines are hidden so may need shown.
2496 // TODO: check if the modified area is hidden.
2497 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
2498 int lineOfPos = pdoc->LineFromPosition(mh.position);
2499 bool insertingNewLine = false;
2500 for (int i=0; i < mh.length; i++) {
2501 if ((mh.text[i] == '\n') || (mh.text[i] == '\r'))
2502 insertingNewLine = true;
2504 if (insertingNewLine && (mh.position != pdoc->LineStart(lineOfPos)))
2505 NeedShown(mh.position, pdoc->LineStart(lineOfPos+1) - mh.position);
2506 else
2507 NeedShown(mh.position, 0);
2508 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
2509 NeedShown(mh.position, mh.length);
2512 if (mh.linesAdded != 0) {
2513 // Update contraction state for inserted and removed lines
2514 // lineOfPos should be calculated in context of state before modification, shouldn't it
2515 int lineOfPos = pdoc->LineFromPosition(mh.position);
2516 if (mh.position > pdoc->LineStart(lineOfPos))
2517 lineOfPos++; // Affecting subsequent lines
2518 if (mh.linesAdded > 0) {
2519 cs.InsertLines(lineOfPos, mh.linesAdded);
2520 } else {
2521 cs.DeleteLines(lineOfPos, -mh.linesAdded);
2523 view.LinesAddedOrRemoved(lineOfPos, mh.linesAdded);
2525 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
2526 int lineDoc = pdoc->LineFromPosition(mh.position);
2527 if (vs.annotationVisible) {
2528 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
2529 Redraw();
2532 CheckModificationForWrap(mh);
2533 if (mh.linesAdded != 0) {
2534 // Avoid scrolling of display if change before current display
2535 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
2536 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
2537 if (newTop != topLine) {
2538 SetTopLine(newTop);
2539 SetVerticalScrollPos();
2543 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
2544 QueueIdleWork(WorkNeeded::workStyle, pdoc->Length());
2545 Redraw();
2547 } else {
2548 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
2549 QueueIdleWork(WorkNeeded::workStyle, mh.position + mh.length);
2550 InvalidateRange(mh.position, mh.position + mh.length);
2555 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
2556 SetScrollBars();
2559 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
2560 if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) {
2561 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
2562 // Fold changes can affect the drawing of following lines so redraw whole margin
2563 RedrawSelMargin(marginView.highlightDelimiter.isEnabled ? -1 : mh.line - 1, true);
2564 } else {
2565 RedrawSelMargin(mh.line);
2569 if ((mh.modificationType & SC_MOD_CHANGEFOLD) && (foldAutomatic & SC_AUTOMATICFOLD_CHANGE)) {
2570 FoldChanged(mh.line, mh.foldLevelNow, mh.foldLevelPrev);
2573 // NOW pay the piper WRT "deferred" visual updates
2574 if (IsLastStep(mh)) {
2575 SetScrollBars();
2576 Redraw();
2579 // If client wants to see this modification
2580 if (mh.modificationType & modEventMask) {
2581 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
2582 // Real modification made to text of document.
2583 NotifyChange(); // Send EN_CHANGE
2586 SCNotification scn = {};
2587 scn.nmhdr.code = SCN_MODIFIED;
2588 scn.position = mh.position;
2589 scn.modificationType = mh.modificationType;
2590 scn.text = mh.text;
2591 scn.length = mh.length;
2592 scn.linesAdded = mh.linesAdded;
2593 scn.line = mh.line;
2594 scn.foldLevelNow = mh.foldLevelNow;
2595 scn.foldLevelPrev = mh.foldLevelPrev;
2596 scn.token = mh.token;
2597 scn.annotationLinesAdded = mh.annotationLinesAdded;
2598 NotifyParent(scn);
2602 void Editor::NotifyDeleted(Document *, void *) {
2603 /* Do nothing */
2606 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2608 // Enumerates all macroable messages
2609 switch (iMessage) {
2610 case SCI_CUT:
2611 case SCI_COPY:
2612 case SCI_PASTE:
2613 case SCI_CLEAR:
2614 case SCI_REPLACESEL:
2615 case SCI_ADDTEXT:
2616 case SCI_INSERTTEXT:
2617 case SCI_APPENDTEXT:
2618 case SCI_CLEARALL:
2619 case SCI_SELECTALL:
2620 case SCI_GOTOLINE:
2621 case SCI_GOTOPOS:
2622 case SCI_SEARCHANCHOR:
2623 case SCI_SEARCHNEXT:
2624 case SCI_SEARCHPREV:
2625 case SCI_LINEDOWN:
2626 case SCI_LINEDOWNEXTEND:
2627 case SCI_PARADOWN:
2628 case SCI_PARADOWNEXTEND:
2629 case SCI_LINEUP:
2630 case SCI_LINEUPEXTEND:
2631 case SCI_PARAUP:
2632 case SCI_PARAUPEXTEND:
2633 case SCI_CHARLEFT:
2634 case SCI_CHARLEFTEXTEND:
2635 case SCI_CHARRIGHT:
2636 case SCI_CHARRIGHTEXTEND:
2637 case SCI_WORDLEFT:
2638 case SCI_WORDLEFTEXTEND:
2639 case SCI_WORDRIGHT:
2640 case SCI_WORDRIGHTEXTEND:
2641 case SCI_WORDPARTLEFT:
2642 case SCI_WORDPARTLEFTEXTEND:
2643 case SCI_WORDPARTRIGHT:
2644 case SCI_WORDPARTRIGHTEXTEND:
2645 case SCI_WORDLEFTEND:
2646 case SCI_WORDLEFTENDEXTEND:
2647 case SCI_WORDRIGHTEND:
2648 case SCI_WORDRIGHTENDEXTEND:
2649 case SCI_HOME:
2650 case SCI_HOMEEXTEND:
2651 case SCI_LINEEND:
2652 case SCI_LINEENDEXTEND:
2653 case SCI_HOMEWRAP:
2654 case SCI_HOMEWRAPEXTEND:
2655 case SCI_LINEENDWRAP:
2656 case SCI_LINEENDWRAPEXTEND:
2657 case SCI_DOCUMENTSTART:
2658 case SCI_DOCUMENTSTARTEXTEND:
2659 case SCI_DOCUMENTEND:
2660 case SCI_DOCUMENTENDEXTEND:
2661 case SCI_STUTTEREDPAGEUP:
2662 case SCI_STUTTEREDPAGEUPEXTEND:
2663 case SCI_STUTTEREDPAGEDOWN:
2664 case SCI_STUTTEREDPAGEDOWNEXTEND:
2665 case SCI_PAGEUP:
2666 case SCI_PAGEUPEXTEND:
2667 case SCI_PAGEDOWN:
2668 case SCI_PAGEDOWNEXTEND:
2669 case SCI_EDITTOGGLEOVERTYPE:
2670 case SCI_CANCEL:
2671 case SCI_DELETEBACK:
2672 case SCI_TAB:
2673 case SCI_BACKTAB:
2674 case SCI_FORMFEED:
2675 case SCI_VCHOME:
2676 case SCI_VCHOMEEXTEND:
2677 case SCI_VCHOMEWRAP:
2678 case SCI_VCHOMEWRAPEXTEND:
2679 case SCI_VCHOMEDISPLAY:
2680 case SCI_VCHOMEDISPLAYEXTEND:
2681 case SCI_DELWORDLEFT:
2682 case SCI_DELWORDRIGHT:
2683 case SCI_DELWORDRIGHTEND:
2684 case SCI_DELLINELEFT:
2685 case SCI_DELLINERIGHT:
2686 case SCI_LINECOPY:
2687 case SCI_LINECUT:
2688 case SCI_LINEDELETE:
2689 case SCI_LINETRANSPOSE:
2690 case SCI_LINEDUPLICATE:
2691 case SCI_LOWERCASE:
2692 case SCI_UPPERCASE:
2693 case SCI_LINESCROLLDOWN:
2694 case SCI_LINESCROLLUP:
2695 case SCI_DELETEBACKNOTLINE:
2696 case SCI_HOMEDISPLAY:
2697 case SCI_HOMEDISPLAYEXTEND:
2698 case SCI_LINEENDDISPLAY:
2699 case SCI_LINEENDDISPLAYEXTEND:
2700 case SCI_SETSELECTIONMODE:
2701 case SCI_LINEDOWNRECTEXTEND:
2702 case SCI_LINEUPRECTEXTEND:
2703 case SCI_CHARLEFTRECTEXTEND:
2704 case SCI_CHARRIGHTRECTEXTEND:
2705 case SCI_HOMERECTEXTEND:
2706 case SCI_VCHOMERECTEXTEND:
2707 case SCI_LINEENDRECTEXTEND:
2708 case SCI_PAGEUPRECTEXTEND:
2709 case SCI_PAGEDOWNRECTEXTEND:
2710 case SCI_SELECTIONDUPLICATE:
2711 case SCI_COPYALLOWLINE:
2712 case SCI_VERTICALCENTRECARET:
2713 case SCI_MOVESELECTEDLINESUP:
2714 case SCI_MOVESELECTEDLINESDOWN:
2715 case SCI_SCROLLTOSTART:
2716 case SCI_SCROLLTOEND:
2717 break;
2719 // Filter out all others like display changes. Also, newlines are redundant
2720 // with char insert messages.
2721 case SCI_NEWLINE:
2722 default:
2723 // printf("Filtered out %ld of macro recording\n", iMessage);
2724 return;
2727 // Send notification
2728 SCNotification scn = {};
2729 scn.nmhdr.code = SCN_MACRORECORD;
2730 scn.message = iMessage;
2731 scn.wParam = wParam;
2732 scn.lParam = lParam;
2733 NotifyParent(scn);
2736 // Something has changed that the container should know about
2737 void Editor::ContainerNeedsUpdate(int flags) {
2738 needUpdateUI |= flags;
2742 * Force scroll and keep position relative to top of window.
2744 * If stuttered = true and not already at first/last row, move to first/last row of window.
2745 * If stuttered = true and already at first/last row, scroll as normal.
2747 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
2748 int topLineNew;
2749 SelectionPosition newPos;
2751 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
2752 int topStutterLine = topLine + caretYSlop;
2753 int bottomStutterLine =
2754 pdoc->LineFromPosition(PositionFromLocation(
2755 Point::FromInts(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
2756 - caretYSlop - 1;
2758 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
2759 topLineNew = topLine;
2760 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
2761 false, false, UserVirtualSpace());
2763 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
2764 topLineNew = topLine;
2765 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
2766 false, false, UserVirtualSpace());
2768 } else {
2769 Point pt = LocationFromPosition(sel.MainCaret());
2771 topLineNew = Platform::Clamp(
2772 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
2773 newPos = SPositionFromLocation(
2774 Point::FromInts(lastXChosen - xOffset, static_cast<int>(pt.y) + direction * (vs.lineHeight * LinesToScroll())),
2775 false, false, UserVirtualSpace());
2778 if (topLineNew != topLine) {
2779 SetTopLine(topLineNew);
2780 MovePositionTo(newPos, selt);
2781 Redraw();
2782 SetVerticalScrollPos();
2783 } else {
2784 MovePositionTo(newPos, selt);
2788 void Editor::ChangeCaseOfSelection(int caseMapping) {
2789 UndoGroup ug(pdoc);
2790 for (size_t r=0; r<sel.Count(); r++) {
2791 SelectionRange current = sel.Range(r);
2792 SelectionRange currentNoVS = current;
2793 currentNoVS.ClearVirtualSpace();
2794 size_t rangeBytes = currentNoVS.Length();
2795 if (rangeBytes > 0) {
2796 std::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position());
2798 std::string sMapped = CaseMapString(sText, caseMapping);
2800 if (sMapped != sText) {
2801 size_t firstDifference = 0;
2802 while (sMapped[firstDifference] == sText[firstDifference])
2803 firstDifference++;
2804 size_t lastDifferenceText = sText.size() - 1;
2805 size_t lastDifferenceMapped = sMapped.size() - 1;
2806 while (sMapped[lastDifferenceMapped] == sText[lastDifferenceText]) {
2807 lastDifferenceText--;
2808 lastDifferenceMapped--;
2810 size_t endDifferenceText = sText.size() - 1 - lastDifferenceText;
2811 pdoc->DeleteChars(
2812 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
2813 static_cast<int>(rangeBytes - firstDifference - endDifferenceText));
2814 const int lengthChange = static_cast<int>(lastDifferenceMapped - firstDifference + 1);
2815 const int lengthInserted = pdoc->InsertString(
2816 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
2817 sMapped.c_str() + firstDifference,
2818 lengthChange);
2819 // Automatic movement changes selection so reset to exactly the same as it was.
2820 int diffSizes = static_cast<int>(sMapped.size() - sText.size()) + lengthInserted - lengthChange;
2821 if (diffSizes != 0) {
2822 if (current.anchor > current.caret)
2823 current.anchor.Add(diffSizes);
2824 else
2825 current.caret.Add(diffSizes);
2827 sel.Range(r) = current;
2833 void Editor::LineTranspose() {
2834 int line = pdoc->LineFromPosition(sel.MainCaret());
2835 if (line > 0) {
2836 UndoGroup ug(pdoc);
2838 const int startPrevious = pdoc->LineStart(line - 1);
2839 const std::string linePrevious = RangeText(startPrevious, pdoc->LineEnd(line - 1));
2841 int startCurrent = pdoc->LineStart(line);
2842 const std::string lineCurrent = RangeText(startCurrent, pdoc->LineEnd(line));
2844 pdoc->DeleteChars(startCurrent, static_cast<int>(lineCurrent.length()));
2845 pdoc->DeleteChars(startPrevious, static_cast<int>(linePrevious.length()));
2846 startCurrent -= static_cast<int>(linePrevious.length());
2848 startCurrent += pdoc->InsertString(startPrevious, lineCurrent.c_str(),
2849 static_cast<int>(lineCurrent.length()));
2850 pdoc->InsertString(startCurrent, linePrevious.c_str(),
2851 static_cast<int>(linePrevious.length()));
2852 // Move caret to start of current line
2853 MovePositionTo(SelectionPosition(startCurrent));
2857 void Editor::Duplicate(bool forLine) {
2858 if (sel.Empty()) {
2859 forLine = true;
2861 UndoGroup ug(pdoc);
2862 const char *eol = "";
2863 int eolLen = 0;
2864 if (forLine) {
2865 eol = StringFromEOLMode(pdoc->eolMode);
2866 eolLen = istrlen(eol);
2868 for (size_t r=0; r<sel.Count(); r++) {
2869 SelectionPosition start = sel.Range(r).Start();
2870 SelectionPosition end = sel.Range(r).End();
2871 if (forLine) {
2872 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
2873 start = SelectionPosition(pdoc->LineStart(line));
2874 end = SelectionPosition(pdoc->LineEnd(line));
2876 std::string text = RangeText(start.Position(), end.Position());
2877 int lengthInserted = eolLen;
2878 if (forLine)
2879 lengthInserted = pdoc->InsertString(end.Position(), eol, eolLen);
2880 pdoc->InsertString(end.Position() + lengthInserted, text.c_str(), static_cast<int>(text.length()));
2882 if (sel.Count() && sel.IsRectangular()) {
2883 SelectionPosition last = sel.Last();
2884 if (forLine) {
2885 int line = pdoc->LineFromPosition(last.Position());
2886 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
2888 if (sel.Rectangular().anchor > sel.Rectangular().caret)
2889 sel.Rectangular().anchor = last;
2890 else
2891 sel.Rectangular().caret = last;
2892 SetRectangularRange();
2896 void Editor::CancelModes() {
2897 sel.SetMoveExtends(false);
2900 void Editor::NewLine() {
2901 // Remove non-main ranges
2902 InvalidateSelection(sel.RangeMain(), true);
2903 sel.SetSelection(sel.RangeMain());
2904 sel.RangeMain().ClearVirtualSpace();
2906 // Clear main range and insert line end
2907 bool needGroupUndo = !sel.Empty();
2908 if (needGroupUndo)
2909 pdoc->BeginUndoAction();
2911 if (!sel.Empty())
2912 ClearSelection();
2913 const char *eol = "\n";
2914 if (pdoc->eolMode == SC_EOL_CRLF) {
2915 eol = "\r\n";
2916 } else if (pdoc->eolMode == SC_EOL_CR) {
2917 eol = "\r";
2918 } // else SC_EOL_LF -> "\n" already set
2919 const int insertLength = pdoc->InsertString(sel.MainCaret(), eol, istrlen(eol));
2920 // Want to end undo group before NotifyChar as applications often modify text here
2921 if (needGroupUndo)
2922 pdoc->EndUndoAction();
2923 if (insertLength > 0) {
2924 SetEmptySelection(sel.MainCaret() + insertLength);
2925 while (*eol) {
2926 NotifyChar(*eol);
2927 if (recordingMacro) {
2928 char txt[2];
2929 txt[0] = *eol;
2930 txt[1] = '\0';
2931 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
2933 eol++;
2936 SetLastXChosen();
2937 SetScrollBars();
2938 EnsureCaretVisible();
2939 // Avoid blinking during rapid typing:
2940 ShowCaretAtCurrentPosition();
2943 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
2944 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
2945 if (sel.IsRectangular()) {
2946 if (selt == Selection::noSel) {
2947 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
2948 } else {
2949 caretToUse = sel.Rectangular().caret;
2953 Point pt = LocationFromPosition(caretToUse);
2954 int skipLines = 0;
2956 if (vs.annotationVisible) {
2957 int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
2958 Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
2959 int subLine = static_cast<int>(pt.y - ptStartLine.y) / vs.lineHeight;
2961 if (direction < 0 && subLine == 0) {
2962 int lineDisplay = cs.DisplayFromDoc(lineDoc);
2963 if (lineDisplay > 0) {
2964 skipLines = pdoc->AnnotationLines(cs.DocFromDisplay(lineDisplay - 1));
2966 } else if (direction > 0 && subLine >= (cs.GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
2967 skipLines = pdoc->AnnotationLines(lineDoc);
2971 int newY = static_cast<int>(pt.y) + (1 + skipLines) * direction * vs.lineHeight;
2972 SelectionPosition posNew = SPositionFromLocation(
2973 Point::FromInts(lastXChosen - xOffset, newY), false, false, UserVirtualSpace());
2975 if (direction < 0) {
2976 // Line wrapping may lead to a location on the same line, so
2977 // seek back if that is the case.
2978 Point ptNew = LocationFromPosition(posNew.Position());
2979 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
2980 posNew.Add(-1);
2981 posNew.SetVirtualSpace(0);
2982 ptNew = LocationFromPosition(posNew.Position());
2984 } else if (direction > 0 && posNew.Position() != pdoc->Length()) {
2985 // There is an equivalent case when moving down which skips
2986 // over a line.
2987 Point ptNew = LocationFromPosition(posNew.Position());
2988 while ((posNew.Position() > caretToUse.Position()) && (ptNew.y > newY)) {
2989 posNew.Add(-1);
2990 posNew.SetVirtualSpace(0);
2991 ptNew = LocationFromPosition(posNew.Position());
2995 MovePositionTo(MovePositionSoVisible(posNew, direction), selt);
2998 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
2999 int lineDoc, savedPos = sel.MainCaret();
3000 do {
3001 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
3002 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
3003 if (direction > 0) {
3004 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
3005 if (selt == Selection::noSel) {
3006 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
3008 break;
3011 } while (!cs.GetVisible(lineDoc));
3014 int Editor::StartEndDisplayLine(int pos, bool start) {
3015 RefreshStyleData();
3016 AutoSurface surface(this);
3017 int posRet = view.StartEndDisplayLine(surface, *this, pos, start, vs);
3018 if (posRet == INVALID_POSITION) {
3019 return pos;
3020 } else {
3021 return posRet;
3025 int Editor::KeyCommand(unsigned int iMessage) {
3026 switch (iMessage) {
3027 case SCI_LINEDOWN:
3028 CursorUpOrDown(1);
3029 break;
3030 case SCI_LINEDOWNEXTEND:
3031 CursorUpOrDown(1, Selection::selStream);
3032 break;
3033 case SCI_LINEDOWNRECTEXTEND:
3034 CursorUpOrDown(1, Selection::selRectangle);
3035 break;
3036 case SCI_PARADOWN:
3037 ParaUpOrDown(1);
3038 break;
3039 case SCI_PARADOWNEXTEND:
3040 ParaUpOrDown(1, Selection::selStream);
3041 break;
3042 case SCI_LINESCROLLDOWN:
3043 ScrollTo(topLine + 1);
3044 MoveCaretInsideView(false);
3045 break;
3046 case SCI_LINEUP:
3047 CursorUpOrDown(-1);
3048 break;
3049 case SCI_LINEUPEXTEND:
3050 CursorUpOrDown(-1, Selection::selStream);
3051 break;
3052 case SCI_LINEUPRECTEXTEND:
3053 CursorUpOrDown(-1, Selection::selRectangle);
3054 break;
3055 case SCI_PARAUP:
3056 ParaUpOrDown(-1);
3057 break;
3058 case SCI_PARAUPEXTEND:
3059 ParaUpOrDown(-1, Selection::selStream);
3060 break;
3061 case SCI_LINESCROLLUP:
3062 ScrollTo(topLine - 1);
3063 MoveCaretInsideView(false);
3064 break;
3065 case SCI_CHARLEFT:
3066 if (SelectionEmpty() || sel.MoveExtends()) {
3067 if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
3068 SelectionPosition spCaret = sel.RangeMain().caret;
3069 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3070 MovePositionTo(spCaret);
3071 } else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
3072 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1));
3073 } else {
3074 MovePositionTo(MovePositionSoVisible(
3075 SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
3077 } else {
3078 MovePositionTo(sel.LimitsForRectangularElseMain().start);
3080 SetLastXChosen();
3081 break;
3082 case SCI_CHARLEFTEXTEND:
3083 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
3084 SelectionPosition spCaret = sel.RangeMain().caret;
3085 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3086 MovePositionTo(spCaret, Selection::selStream);
3087 } else {
3088 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
3090 SetLastXChosen();
3091 break;
3092 case SCI_CHARLEFTRECTEXTEND:
3093 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
3094 SelectionPosition spCaret = sel.RangeMain().caret;
3095 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3096 MovePositionTo(spCaret, Selection::selRectangle);
3097 } else {
3098 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
3100 SetLastXChosen();
3101 break;
3102 case SCI_CHARRIGHT:
3103 if (SelectionEmpty() || sel.MoveExtends()) {
3104 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
3105 SelectionPosition spCaret = sel.RangeMain().caret;
3106 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3107 MovePositionTo(spCaret);
3108 } else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
3109 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1));
3110 } else {
3111 MovePositionTo(MovePositionSoVisible(
3112 SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
3114 } else {
3115 MovePositionTo(sel.LimitsForRectangularElseMain().end);
3117 SetLastXChosen();
3118 break;
3119 case SCI_CHARRIGHTEXTEND:
3120 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
3121 SelectionPosition spCaret = sel.RangeMain().caret;
3122 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3123 MovePositionTo(spCaret, Selection::selStream);
3124 } else {
3125 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
3127 SetLastXChosen();
3128 break;
3129 case SCI_CHARRIGHTRECTEXTEND:
3130 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
3131 SelectionPosition spCaret = sel.RangeMain().caret;
3132 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3133 MovePositionTo(spCaret, Selection::selRectangle);
3134 } else {
3135 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
3137 SetLastXChosen();
3138 break;
3139 case SCI_WORDLEFT:
3140 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
3141 SetLastXChosen();
3142 break;
3143 case SCI_WORDLEFTEXTEND:
3144 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
3145 SetLastXChosen();
3146 break;
3147 case SCI_WORDRIGHT:
3148 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
3149 SetLastXChosen();
3150 break;
3151 case SCI_WORDRIGHTEXTEND:
3152 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
3153 SetLastXChosen();
3154 break;
3156 case SCI_WORDLEFTEND:
3157 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
3158 SetLastXChosen();
3159 break;
3160 case SCI_WORDLEFTENDEXTEND:
3161 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
3162 SetLastXChosen();
3163 break;
3164 case SCI_WORDRIGHTEND:
3165 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
3166 SetLastXChosen();
3167 break;
3168 case SCI_WORDRIGHTENDEXTEND:
3169 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
3170 SetLastXChosen();
3171 break;
3173 case SCI_HOME:
3174 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
3175 SetLastXChosen();
3176 break;
3177 case SCI_HOMEEXTEND:
3178 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
3179 SetLastXChosen();
3180 break;
3181 case SCI_HOMERECTEXTEND:
3182 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
3183 SetLastXChosen();
3184 break;
3185 case SCI_LINEEND:
3186 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
3187 SetLastXChosen();
3188 break;
3189 case SCI_LINEENDEXTEND:
3190 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
3191 SetLastXChosen();
3192 break;
3193 case SCI_LINEENDRECTEXTEND:
3194 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
3195 SetLastXChosen();
3196 break;
3197 case SCI_HOMEWRAP: {
3198 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
3199 if (sel.RangeMain().caret <= homePos)
3200 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
3201 MovePositionTo(homePos);
3202 SetLastXChosen();
3204 break;
3205 case SCI_HOMEWRAPEXTEND: {
3206 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
3207 if (sel.RangeMain().caret <= homePos)
3208 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
3209 MovePositionTo(homePos, Selection::selStream);
3210 SetLastXChosen();
3212 break;
3213 case SCI_LINEENDWRAP: {
3214 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
3215 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
3216 if (endPos > realEndPos // if moved past visible EOLs
3217 || sel.RangeMain().caret >= endPos) // if at end of display line already
3218 endPos = realEndPos;
3219 MovePositionTo(endPos);
3220 SetLastXChosen();
3222 break;
3223 case SCI_LINEENDWRAPEXTEND: {
3224 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
3225 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
3226 if (endPos > realEndPos // if moved past visible EOLs
3227 || sel.RangeMain().caret >= endPos) // if at end of display line already
3228 endPos = realEndPos;
3229 MovePositionTo(endPos, Selection::selStream);
3230 SetLastXChosen();
3232 break;
3233 case SCI_DOCUMENTSTART:
3234 MovePositionTo(0);
3235 SetLastXChosen();
3236 break;
3237 case SCI_DOCUMENTSTARTEXTEND:
3238 MovePositionTo(0, Selection::selStream);
3239 SetLastXChosen();
3240 break;
3241 case SCI_DOCUMENTEND:
3242 MovePositionTo(pdoc->Length());
3243 SetLastXChosen();
3244 break;
3245 case SCI_DOCUMENTENDEXTEND:
3246 MovePositionTo(pdoc->Length(), Selection::selStream);
3247 SetLastXChosen();
3248 break;
3249 case SCI_STUTTEREDPAGEUP:
3250 PageMove(-1, Selection::noSel, true);
3251 break;
3252 case SCI_STUTTEREDPAGEUPEXTEND:
3253 PageMove(-1, Selection::selStream, true);
3254 break;
3255 case SCI_STUTTEREDPAGEDOWN:
3256 PageMove(1, Selection::noSel, true);
3257 break;
3258 case SCI_STUTTEREDPAGEDOWNEXTEND:
3259 PageMove(1, Selection::selStream, true);
3260 break;
3261 case SCI_PAGEUP:
3262 PageMove(-1);
3263 break;
3264 case SCI_PAGEUPEXTEND:
3265 PageMove(-1, Selection::selStream);
3266 break;
3267 case SCI_PAGEUPRECTEXTEND:
3268 PageMove(-1, Selection::selRectangle);
3269 break;
3270 case SCI_PAGEDOWN:
3271 PageMove(1);
3272 break;
3273 case SCI_PAGEDOWNEXTEND:
3274 PageMove(1, Selection::selStream);
3275 break;
3276 case SCI_PAGEDOWNRECTEXTEND:
3277 PageMove(1, Selection::selRectangle);
3278 break;
3279 case SCI_EDITTOGGLEOVERTYPE:
3280 inOverstrike = !inOverstrike;
3281 ShowCaretAtCurrentPosition();
3282 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
3283 NotifyUpdateUI();
3284 break;
3285 case SCI_CANCEL: // Cancel any modes - handled in subclass
3286 // Also unselect text
3287 CancelModes();
3288 break;
3289 case SCI_DELETEBACK:
3290 DelCharBack(true);
3291 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3292 SetLastXChosen();
3294 EnsureCaretVisible();
3295 break;
3296 case SCI_DELETEBACKNOTLINE:
3297 DelCharBack(false);
3298 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3299 SetLastXChosen();
3301 EnsureCaretVisible();
3302 break;
3303 case SCI_TAB:
3304 Indent(true);
3305 if (caretSticky == SC_CARETSTICKY_OFF) {
3306 SetLastXChosen();
3308 EnsureCaretVisible();
3309 ShowCaretAtCurrentPosition(); // Avoid blinking
3310 break;
3311 case SCI_BACKTAB:
3312 Indent(false);
3313 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3314 SetLastXChosen();
3316 EnsureCaretVisible();
3317 ShowCaretAtCurrentPosition(); // Avoid blinking
3318 break;
3319 case SCI_NEWLINE:
3320 NewLine();
3321 break;
3322 case SCI_FORMFEED:
3323 AddChar('\f');
3324 break;
3325 case SCI_VCHOME:
3326 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
3327 SetLastXChosen();
3328 break;
3329 case SCI_VCHOMEEXTEND:
3330 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
3331 SetLastXChosen();
3332 break;
3333 case SCI_VCHOMERECTEXTEND:
3334 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
3335 SetLastXChosen();
3336 break;
3337 case SCI_VCHOMEWRAP: {
3338 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
3339 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
3340 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
3341 homePos = viewLineStart;
3343 MovePositionTo(homePos);
3344 SetLastXChosen();
3346 break;
3347 case SCI_VCHOMEWRAPEXTEND: {
3348 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
3349 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
3350 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
3351 homePos = viewLineStart;
3353 MovePositionTo(homePos, Selection::selStream);
3354 SetLastXChosen();
3356 break;
3357 case SCI_ZOOMIN:
3358 if (vs.zoomLevel < 20) {
3359 vs.zoomLevel++;
3360 InvalidateStyleRedraw();
3361 NotifyZoom();
3363 break;
3364 case SCI_ZOOMOUT:
3365 if (vs.zoomLevel > -10) {
3366 vs.zoomLevel--;
3367 InvalidateStyleRedraw();
3368 NotifyZoom();
3370 break;
3371 case SCI_DELWORDLEFT: {
3372 int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
3373 pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
3374 sel.RangeMain().ClearVirtualSpace();
3375 SetLastXChosen();
3377 break;
3378 case SCI_DELWORDRIGHT: {
3379 UndoGroup ug(pdoc);
3380 InvalidateSelection(sel.RangeMain(), true);
3381 sel.RangeMain().caret = SelectionPosition(
3382 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
3383 sel.RangeMain().anchor = sel.RangeMain().caret;
3384 int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
3385 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
3387 break;
3388 case SCI_DELWORDRIGHTEND: {
3389 UndoGroup ug(pdoc);
3390 InvalidateSelection(sel.RangeMain(), true);
3391 sel.RangeMain().caret = SelectionPosition(
3392 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
3393 int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
3394 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
3396 break;
3397 case SCI_DELLINELEFT: {
3398 int line = pdoc->LineFromPosition(sel.MainCaret());
3399 int start = pdoc->LineStart(line);
3400 pdoc->DeleteChars(start, sel.MainCaret() - start);
3401 sel.RangeMain().ClearVirtualSpace();
3402 SetLastXChosen();
3404 break;
3405 case SCI_DELLINERIGHT: {
3406 int line = pdoc->LineFromPosition(sel.MainCaret());
3407 int end = pdoc->LineEnd(line);
3408 pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
3410 break;
3411 case SCI_LINECOPY: {
3412 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
3413 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
3414 CopyRangeToClipboard(pdoc->LineStart(lineStart),
3415 pdoc->LineStart(lineEnd + 1));
3417 break;
3418 case SCI_LINECUT: {
3419 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
3420 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
3421 int start = pdoc->LineStart(lineStart);
3422 int end = pdoc->LineStart(lineEnd + 1);
3423 SetSelection(start, end);
3424 Cut();
3425 SetLastXChosen();
3427 break;
3428 case SCI_LINEDELETE: {
3429 int line = pdoc->LineFromPosition(sel.MainCaret());
3430 int start = pdoc->LineStart(line);
3431 int end = pdoc->LineStart(line + 1);
3432 pdoc->DeleteChars(start, end - start);
3434 break;
3435 case SCI_LINETRANSPOSE:
3436 LineTranspose();
3437 break;
3438 case SCI_LINEDUPLICATE:
3439 Duplicate(true);
3440 break;
3441 case SCI_SELECTIONDUPLICATE:
3442 Duplicate(false);
3443 break;
3444 case SCI_LOWERCASE:
3445 ChangeCaseOfSelection(cmLower);
3446 break;
3447 case SCI_UPPERCASE:
3448 ChangeCaseOfSelection(cmUpper);
3449 break;
3450 case SCI_WORDPARTLEFT:
3451 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
3452 SetLastXChosen();
3453 break;
3454 case SCI_WORDPARTLEFTEXTEND:
3455 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
3456 SetLastXChosen();
3457 break;
3458 case SCI_WORDPARTRIGHT:
3459 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
3460 SetLastXChosen();
3461 break;
3462 case SCI_WORDPARTRIGHTEXTEND:
3463 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
3464 SetLastXChosen();
3465 break;
3466 case SCI_HOMEDISPLAY:
3467 MovePositionTo(MovePositionSoVisible(
3468 StartEndDisplayLine(sel.MainCaret(), true), -1));
3469 SetLastXChosen();
3470 break;
3471 case SCI_VCHOMEDISPLAY: {
3472 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
3473 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
3474 if (viewLineStart > homePos)
3475 homePos = viewLineStart;
3477 MovePositionTo(homePos);
3478 SetLastXChosen();
3480 break;
3481 case SCI_HOMEDISPLAYEXTEND:
3482 MovePositionTo(MovePositionSoVisible(
3483 StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
3484 SetLastXChosen();
3485 break;
3486 case SCI_VCHOMEDISPLAYEXTEND: {
3487 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
3488 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
3489 if (viewLineStart > homePos)
3490 homePos = viewLineStart;
3492 MovePositionTo(homePos, Selection::selStream);
3493 SetLastXChosen();
3495 break;
3496 case SCI_LINEENDDISPLAY:
3497 MovePositionTo(MovePositionSoVisible(
3498 StartEndDisplayLine(sel.MainCaret(), false), 1));
3499 SetLastXChosen();
3500 break;
3501 case SCI_LINEENDDISPLAYEXTEND:
3502 MovePositionTo(MovePositionSoVisible(
3503 StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
3504 SetLastXChosen();
3505 break;
3506 case SCI_SCROLLTOSTART:
3507 ScrollTo(0);
3508 break;
3509 case SCI_SCROLLTOEND:
3510 ScrollTo(MaxScrollPos());
3511 break;
3513 return 0;
3516 int Editor::KeyDefault(int, int) {
3517 return 0;
3520 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
3521 DwellEnd(false);
3522 int msg = kmap.Find(key, modifiers);
3523 if (msg) {
3524 if (consumed)
3525 *consumed = true;
3526 return static_cast<int>(WndProc(msg, 0, 0));
3527 } else {
3528 if (consumed)
3529 *consumed = false;
3530 return KeyDefault(key, modifiers);
3534 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
3535 return KeyDownWithModifiers(key, ModifierFlags(shift, ctrl, alt), consumed);
3538 void Editor::Indent(bool forwards) {
3539 UndoGroup ug(pdoc);
3540 for (size_t r=0; r<sel.Count(); r++) {
3541 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
3542 int caretPosition = sel.Range(r).caret.Position();
3543 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
3544 if (lineOfAnchor == lineCurrentPos) {
3545 if (forwards) {
3546 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
3547 caretPosition = sel.Range(r).caret.Position();
3548 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
3549 pdoc->tabIndents) {
3550 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3551 int indentationStep = pdoc->IndentSize();
3552 const int posSelect = pdoc->SetLineIndentation(
3553 lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
3554 sel.Range(r) = SelectionRange(posSelect);
3555 } else {
3556 if (pdoc->useTabs) {
3557 const int lengthInserted = pdoc->InsertString(caretPosition, "\t", 1);
3558 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
3559 } else {
3560 int numSpaces = (pdoc->tabInChars) -
3561 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
3562 if (numSpaces < 1)
3563 numSpaces = pdoc->tabInChars;
3564 const std::string spaceText(numSpaces, ' ');
3565 const int lengthInserted = pdoc->InsertString(caretPosition, spaceText.c_str(),
3566 static_cast<int>(spaceText.length()));
3567 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
3570 } else {
3571 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
3572 pdoc->tabIndents) {
3573 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3574 int indentationStep = pdoc->IndentSize();
3575 const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
3576 sel.Range(r) = SelectionRange(posSelect);
3577 } else {
3578 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
3579 pdoc->tabInChars;
3580 if (newColumn < 0)
3581 newColumn = 0;
3582 int newPos = caretPosition;
3583 while (pdoc->GetColumn(newPos) > newColumn)
3584 newPos--;
3585 sel.Range(r) = SelectionRange(newPos);
3588 } else { // Multiline
3589 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
3590 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
3591 // Multiple lines selected so indent / dedent
3592 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
3593 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
3594 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
3595 lineBottomSel--; // If not selecting any characters on a line, do not indent
3596 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
3597 if (lineOfAnchor < lineCurrentPos) {
3598 if (currentPosPosOnLine == 0)
3599 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
3600 else
3601 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
3602 } else {
3603 if (anchorPosOnLine == 0)
3604 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
3605 else
3606 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
3610 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
3613 class CaseFolderASCII : public CaseFolderTable {
3614 public:
3615 CaseFolderASCII() {
3616 StandardASCII();
3618 ~CaseFolderASCII() {
3623 CaseFolder *Editor::CaseFolderForEncoding() {
3624 // Simple default that only maps ASCII upper case to lower case.
3625 return new CaseFolderASCII();
3629 * Search of a text in the document, in the given range.
3630 * @return The position of the found text, -1 if not found.
3632 long Editor::FindText(
3633 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
3634 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
3635 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
3637 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
3638 int lengthFound = istrlen(ft->lpstrText);
3639 if (!pdoc->HasCaseFolder())
3640 pdoc->SetCaseFolder(CaseFolderForEncoding());
3641 try {
3642 long pos = pdoc->FindText(
3643 static_cast<int>(ft->chrg.cpMin),
3644 static_cast<int>(ft->chrg.cpMax),
3645 ft->lpstrText,
3646 (wParam & SCFIND_MATCHCASE) != 0,
3647 (wParam & SCFIND_WHOLEWORD) != 0,
3648 (wParam & SCFIND_WORDSTART) != 0,
3649 (wParam & SCFIND_REGEXP) != 0,
3650 static_cast<int>(wParam),
3651 &lengthFound);
3652 if (pos != -1) {
3653 ft->chrgText.cpMin = pos;
3654 ft->chrgText.cpMax = pos + lengthFound;
3656 return static_cast<int>(pos);
3657 } catch (RegexError &) {
3658 errorStatus = SC_STATUS_WARN_REGEX;
3659 return -1;
3664 * Relocatable search support : Searches relative to current selection
3665 * point and sets the selection to the found text range with
3666 * each search.
3669 * Anchor following searches at current selection start: This allows
3670 * multiple incremental interactive searches to be macro recorded
3671 * while still setting the selection to found text so the find/select
3672 * operation is self-contained.
3674 void Editor::SearchAnchor() {
3675 searchAnchor = SelectionStart().Position();
3679 * Find text from current search anchor: Must call @c SearchAnchor first.
3680 * Used for next text and previous text requests.
3681 * @return The position of the found text, -1 if not found.
3683 long Editor::SearchText(
3684 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
3685 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
3686 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
3687 sptr_t lParam) { ///< The text to search for.
3689 const char *txt = reinterpret_cast<char *>(lParam);
3690 long pos;
3691 int lengthFound = istrlen(txt);
3692 if (!pdoc->HasCaseFolder())
3693 pdoc->SetCaseFolder(CaseFolderForEncoding());
3694 try {
3695 if (iMessage == SCI_SEARCHNEXT) {
3696 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
3697 (wParam & SCFIND_MATCHCASE) != 0,
3698 (wParam & SCFIND_WHOLEWORD) != 0,
3699 (wParam & SCFIND_WORDSTART) != 0,
3700 (wParam & SCFIND_REGEXP) != 0,
3701 static_cast<int>(wParam),
3702 &lengthFound);
3703 } else {
3704 pos = pdoc->FindText(searchAnchor, 0, txt,
3705 (wParam & SCFIND_MATCHCASE) != 0,
3706 (wParam & SCFIND_WHOLEWORD) != 0,
3707 (wParam & SCFIND_WORDSTART) != 0,
3708 (wParam & SCFIND_REGEXP) != 0,
3709 static_cast<int>(wParam),
3710 &lengthFound);
3712 } catch (RegexError &) {
3713 errorStatus = SC_STATUS_WARN_REGEX;
3714 return -1;
3716 if (pos != -1) {
3717 SetSelection(static_cast<int>(pos), static_cast<int>(pos + lengthFound));
3720 return pos;
3723 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
3724 std::string ret(s);
3725 for (size_t i=0; i<ret.size(); i++) {
3726 switch (caseMapping) {
3727 case cmUpper:
3728 if (ret[i] >= 'a' && ret[i] <= 'z')
3729 ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
3730 break;
3731 case cmLower:
3732 if (ret[i] >= 'A' && ret[i] <= 'Z')
3733 ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
3734 break;
3737 return ret;
3741 * Search for text in the target range of the document.
3742 * @return The position of the found text, -1 if not found.
3744 long Editor::SearchInTarget(const char *text, int length) {
3745 int lengthFound = length;
3747 if (!pdoc->HasCaseFolder())
3748 pdoc->SetCaseFolder(CaseFolderForEncoding());
3749 try {
3750 long pos = pdoc->FindText(targetStart, targetEnd, text,
3751 (searchFlags & SCFIND_MATCHCASE) != 0,
3752 (searchFlags & SCFIND_WHOLEWORD) != 0,
3753 (searchFlags & SCFIND_WORDSTART) != 0,
3754 (searchFlags & SCFIND_REGEXP) != 0,
3755 searchFlags,
3756 &lengthFound);
3757 if (pos != -1) {
3758 targetStart = static_cast<int>(pos);
3759 targetEnd = static_cast<int>(pos + lengthFound);
3761 return pos;
3762 } catch (RegexError &) {
3763 errorStatus = SC_STATUS_WARN_REGEX;
3764 return -1;
3768 void Editor::GoToLine(int lineNo) {
3769 if (lineNo > pdoc->LinesTotal())
3770 lineNo = pdoc->LinesTotal();
3771 if (lineNo < 0)
3772 lineNo = 0;
3773 SetEmptySelection(pdoc->LineStart(lineNo));
3774 ShowCaretAtCurrentPosition();
3775 EnsureCaretVisible();
3778 static bool Close(Point pt1, Point pt2, Point threshold) {
3779 if (std::abs(pt1.x - pt2.x) > threshold.x)
3780 return false;
3781 if (std::abs(pt1.y - pt2.y) > threshold.y)
3782 return false;
3783 return true;
3786 std::string Editor::RangeText(int start, int end) const {
3787 if (start < end) {
3788 int len = end - start;
3789 std::string ret(len, '\0');
3790 for (int i = 0; i < len; i++) {
3791 ret[i] = pdoc->CharAt(start + i);
3793 return ret;
3795 return std::string();
3798 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
3799 if (sel.Empty()) {
3800 if (allowLineCopy) {
3801 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
3802 int start = pdoc->LineStart(currentLine);
3803 int end = pdoc->LineEnd(currentLine);
3805 std::string text = RangeText(start, end);
3806 if (pdoc->eolMode != SC_EOL_LF)
3807 text.push_back('\r');
3808 if (pdoc->eolMode != SC_EOL_CR)
3809 text.push_back('\n');
3810 ss->Copy(text, pdoc->dbcsCodePage,
3811 vs.styles[STYLE_DEFAULT].characterSet, false, true);
3813 } else {
3814 std::string text;
3815 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
3816 if (sel.selType == Selection::selRectangle)
3817 std::sort(rangesInOrder.begin(), rangesInOrder.end());
3818 for (size_t r=0; r<rangesInOrder.size(); r++) {
3819 SelectionRange current = rangesInOrder[r];
3820 text.append(RangeText(current.Start().Position(), current.End().Position()));
3821 if (sel.selType == Selection::selRectangle) {
3822 if (pdoc->eolMode != SC_EOL_LF)
3823 text.push_back('\r');
3824 if (pdoc->eolMode != SC_EOL_CR)
3825 text.push_back('\n');
3828 ss->Copy(text, pdoc->dbcsCodePage,
3829 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
3833 void Editor::CopyRangeToClipboard(int start, int end) {
3834 start = pdoc->ClampPositionIntoDocument(start);
3835 end = pdoc->ClampPositionIntoDocument(end);
3836 SelectionText selectedText;
3837 std::string text = RangeText(start, end);
3838 selectedText.Copy(text,
3839 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
3840 CopyToClipboard(selectedText);
3843 void Editor::CopyText(int length, const char *text) {
3844 SelectionText selectedText;
3845 selectedText.Copy(std::string(text, length),
3846 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
3847 CopyToClipboard(selectedText);
3850 void Editor::SetDragPosition(SelectionPosition newPos) {
3851 if (newPos.Position() >= 0) {
3852 newPos = MovePositionOutsideChar(newPos, 1);
3853 posDrop = newPos;
3855 if (!(posDrag == newPos)) {
3856 caret.on = true;
3857 if (FineTickerAvailable()) {
3858 FineTickerCancel(tickCaret);
3859 if ((caret.active) && (caret.period > 0) && (newPos.Position() < 0))
3860 FineTickerStart(tickCaret, caret.period, caret.period/10);
3861 } else {
3862 SetTicking(true);
3864 InvalidateCaret();
3865 posDrag = newPos;
3866 InvalidateCaret();
3870 void Editor::DisplayCursor(Window::Cursor c) {
3871 if (cursorMode == SC_CURSORNORMAL)
3872 wMain.SetCursor(c);
3873 else
3874 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
3877 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
3878 int xMove = static_cast<int>(ptStart.x - ptNow.x);
3879 int yMove = static_cast<int>(ptStart.y - ptNow.y);
3880 int distanceSquared = xMove * xMove + yMove * yMove;
3881 return distanceSquared > 16;
3884 void Editor::StartDrag() {
3885 // Always handled by subclasses
3886 //SetMouseCapture(true);
3887 //DisplayCursor(Window::cursorArrow);
3890 void Editor::DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular) {
3891 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
3892 if (inDragDrop == ddDragging)
3893 dropWentOutside = false;
3895 bool positionWasInSelection = PositionInSelection(position.Position());
3897 bool positionOnEdgeOfSelection =
3898 (position == SelectionStart()) || (position == SelectionEnd());
3900 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
3901 (positionOnEdgeOfSelection && !moving)) {
3903 SelectionPosition selStart = SelectionStart();
3904 SelectionPosition selEnd = SelectionEnd();
3906 UndoGroup ug(pdoc);
3908 SelectionPosition positionAfterDeletion = position;
3909 if ((inDragDrop == ddDragging) && moving) {
3910 // Remove dragged out text
3911 if (rectangular || sel.selType == Selection::selLines) {
3912 for (size_t r=0; r<sel.Count(); r++) {
3913 if (position >= sel.Range(r).Start()) {
3914 if (position > sel.Range(r).End()) {
3915 positionAfterDeletion.Add(-sel.Range(r).Length());
3916 } else {
3917 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
3921 } else {
3922 if (position > selStart) {
3923 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
3926 ClearSelection();
3928 position = positionAfterDeletion;
3930 std::string convertedText = Document::TransformLineEnds(value, lengthValue, pdoc->eolMode);
3932 if (rectangular) {
3933 PasteRectangular(position, convertedText.c_str(), static_cast<int>(convertedText.length()));
3934 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
3935 SetEmptySelection(position);
3936 } else {
3937 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
3938 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
3939 const int lengthInserted = pdoc->InsertString(
3940 position.Position(), convertedText.c_str(), static_cast<int>(convertedText.length()));
3941 if (lengthInserted > 0) {
3942 SelectionPosition posAfterInsertion = position;
3943 posAfterInsertion.Add(lengthInserted);
3944 SetSelection(posAfterInsertion, position);
3947 } else if (inDragDrop == ddDragging) {
3948 SetEmptySelection(position);
3952 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
3953 DropAt(position, value, strlen(value), moving, rectangular);
3957 * @return true if given position is inside the selection,
3959 bool Editor::PositionInSelection(int pos) {
3960 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
3961 for (size_t r=0; r<sel.Count(); r++) {
3962 if (sel.Range(r).Contains(pos))
3963 return true;
3965 return false;
3968 bool Editor::PointInSelection(Point pt) {
3969 SelectionPosition pos = SPositionFromLocation(pt, false, true);
3970 Point ptPos = LocationFromPosition(pos);
3971 for (size_t r=0; r<sel.Count(); r++) {
3972 SelectionRange range = sel.Range(r);
3973 if (range.Contains(pos)) {
3974 bool hit = true;
3975 if (pos == range.Start()) {
3976 // see if just before selection
3977 if (pt.x < ptPos.x) {
3978 hit = false;
3981 if (pos == range.End()) {
3982 // see if just after selection
3983 if (pt.x > ptPos.x) {
3984 hit = false;
3987 if (hit)
3988 return true;
3991 return false;
3994 bool Editor::PointInSelMargin(Point pt) const {
3995 // Really means: "Point in a margin"
3996 if (vs.fixedColumnWidth > 0) { // There is a margin
3997 PRectangle rcSelMargin = GetClientRectangle();
3998 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart - vs.leftMarginWidth);
3999 rcSelMargin.left = static_cast<XYPOSITION>(vs.textStart - vs.fixedColumnWidth);
4000 return rcSelMargin.ContainsWholePixel(pt);
4001 } else {
4002 return false;
4006 Window::Cursor Editor::GetMarginCursor(Point pt) const {
4007 int x = 0;
4008 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
4009 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
4010 return static_cast<Window::Cursor>(vs.ms[margin].cursor);
4011 x += vs.ms[margin].width;
4013 return Window::cursorReverseArrow;
4016 void Editor::TrimAndSetSelection(int currentPos_, int anchor_) {
4017 sel.TrimSelection(SelectionRange(currentPos_, anchor_));
4018 SetSelection(currentPos_, anchor_);
4021 void Editor::LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine) {
4022 int selCurrentPos, selAnchorPos;
4023 if (wholeLine) {
4024 int lineCurrent_ = pdoc->LineFromPosition(lineCurrentPos_);
4025 int lineAnchor_ = pdoc->LineFromPosition(lineAnchorPos_);
4026 if (lineAnchorPos_ < lineCurrentPos_) {
4027 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
4028 selAnchorPos = pdoc->LineStart(lineAnchor_);
4029 } else if (lineAnchorPos_ > lineCurrentPos_) {
4030 selCurrentPos = pdoc->LineStart(lineCurrent_);
4031 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
4032 } else { // Same line, select it
4033 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
4034 selAnchorPos = pdoc->LineStart(lineAnchor_);
4036 } else {
4037 if (lineAnchorPos_ < lineCurrentPos_) {
4038 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
4039 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4040 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4041 } else if (lineAnchorPos_ > lineCurrentPos_) {
4042 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
4043 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4044 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
4045 } else { // Same line, select it
4046 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4047 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4048 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4051 TrimAndSetSelection(selCurrentPos, selAnchorPos);
4054 void Editor::WordSelection(int pos) {
4055 if (pos < wordSelectAnchorStartPos) {
4056 // Extend backward to the word containing pos.
4057 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
4058 // This ensures that a series of empty lines isn't counted as a single "word".
4059 if (!pdoc->IsLineEndPosition(pos))
4060 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
4061 TrimAndSetSelection(pos, wordSelectAnchorEndPos);
4062 } else if (pos > wordSelectAnchorEndPos) {
4063 // Extend forward to the word containing the character to the left of pos.
4064 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
4065 // This ensures that a series of empty lines isn't counted as a single "word".
4066 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
4067 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
4068 TrimAndSetSelection(pos, wordSelectAnchorStartPos);
4069 } else {
4070 // Select only the anchored word
4071 if (pos >= originalAnchorPos)
4072 TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
4073 else
4074 TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
4078 void Editor::DwellEnd(bool mouseMoved) {
4079 if (mouseMoved)
4080 ticksToDwell = dwellDelay;
4081 else
4082 ticksToDwell = SC_TIME_FOREVER;
4083 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
4084 dwelling = false;
4085 NotifyDwelling(ptMouseLast, dwelling);
4087 if (FineTickerAvailable()) {
4088 FineTickerCancel(tickDwell);
4089 if (mouseMoved && (dwellDelay < SC_TIME_FOREVER)) {
4090 //FineTickerStart(tickDwell, dwellDelay, dwellDelay/10);
4095 void Editor::MouseLeave() {
4096 SetHotSpotRange(NULL);
4097 if (!HaveMouseCapture()) {
4098 ptMouseLast = Point(-1,-1);
4099 DwellEnd(true);
4103 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
4104 return (!rectangular && ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0))
4105 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
4108 void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) {
4109 SetHoverIndicatorPoint(pt);
4110 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
4111 ptMouseLast = pt;
4112 const bool ctrl = (modifiers & SCI_CTRL) != 0;
4113 const bool shift = (modifiers & SCI_SHIFT) != 0;
4114 const bool alt = (modifiers & SCI_ALT) != 0;
4115 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
4116 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4117 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4118 newCharPos = MovePositionOutsideChar(newCharPos, -1);
4119 inDragDrop = ddNone;
4120 sel.SetMoveExtends(false);
4122 if (NotifyMarginClick(pt, modifiers))
4123 return;
4125 NotifyIndicatorClick(true, newPos.Position(), modifiers);
4127 bool inSelMargin = PointInSelMargin(pt);
4128 // In margin ctrl+(double)click should always select everything
4129 if (ctrl && inSelMargin) {
4130 SelectAll();
4131 lastClickTime = curTime;
4132 lastClick = pt;
4133 return;
4135 if (shift && !inSelMargin) {
4136 SetSelection(newPos);
4138 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick, doubleClickCloseThreshold)) {
4139 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
4140 SetMouseCapture(true);
4141 if (FineTickerAvailable()) {
4142 FineTickerStart(tickScroll, 100, 10);
4144 if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))
4145 SetEmptySelection(newPos.Position());
4146 bool doubleClick = false;
4147 // Stop mouse button bounce changing selection type
4148 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
4149 if (inSelMargin) {
4150 // Inside margin selection type should be either selSubLine or selWholeLine.
4151 if (selectionType == selSubLine) {
4152 // If it is selSubLine, we're inside a *double* click and word wrap is enabled,
4153 // so we switch to selWholeLine in order to select whole line.
4154 selectionType = selWholeLine;
4155 } else if (selectionType != selSubLine && selectionType != selWholeLine) {
4156 // If it is neither, reset selection type to line selection.
4157 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4159 } else {
4160 if (selectionType == selChar) {
4161 selectionType = selWord;
4162 doubleClick = true;
4163 } else if (selectionType == selWord) {
4164 // Since we ended up here, we're inside a *triple* click, which should always select
4165 // whole line regardless of word wrap being enabled or not.
4166 selectionType = selWholeLine;
4167 } else {
4168 selectionType = selChar;
4169 originalAnchorPos = sel.MainCaret();
4174 if (selectionType == selWord) {
4175 int charPos = originalAnchorPos;
4176 if (sel.MainCaret() == originalAnchorPos) {
4177 charPos = PositionFromLocation(pt, false, true);
4178 charPos = MovePositionOutsideChar(charPos, -1);
4181 int startWord, endWord;
4182 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
4183 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
4184 endWord = pdoc->ExtendWordSelect(charPos, 1);
4185 } else {
4186 // Selecting backwards, or anchor beyond last character on line. In these cases,
4187 // we select the word containing the character to the *left* of the anchor.
4188 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
4189 startWord = pdoc->ExtendWordSelect(charPos, -1);
4190 endWord = pdoc->ExtendWordSelect(startWord, 1);
4191 } else {
4192 // Anchor at start of line; select nothing to begin with.
4193 startWord = charPos;
4194 endWord = charPos;
4198 wordSelectAnchorStartPos = startWord;
4199 wordSelectAnchorEndPos = endWord;
4200 wordSelectInitialCaretPos = sel.MainCaret();
4201 WordSelection(wordSelectInitialCaretPos);
4202 } else if (selectionType == selSubLine || selectionType == selWholeLine) {
4203 lineAnchorPos = newPos.Position();
4204 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
4205 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
4206 } else {
4207 SetEmptySelection(sel.MainCaret());
4209 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
4210 if (doubleClick) {
4211 NotifyDoubleClick(pt, modifiers);
4212 if (PositionIsHotspot(newCharPos.Position()))
4213 NotifyHotSpotDoubleClicked(newCharPos.Position(), modifiers);
4215 } else { // Single click
4216 if (inSelMargin) {
4217 sel.selType = Selection::selStream;
4218 if (!shift) {
4219 // Single click in margin: select whole line or only subline if word wrap is enabled
4220 lineAnchorPos = newPos.Position();
4221 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4222 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
4223 } else {
4224 // Single shift+click in margin: select from line anchor to clicked line
4225 if (sel.MainAnchor() > sel.MainCaret())
4226 lineAnchorPos = sel.MainAnchor() - 1;
4227 else
4228 lineAnchorPos = sel.MainAnchor();
4229 // Reset selection type if there is an empty selection.
4230 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
4231 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
4232 // This ensures that we continue selecting in the same selection mode.
4233 if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
4234 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4235 LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
4238 SetDragPosition(SelectionPosition(invalidPosition));
4239 SetMouseCapture(true);
4240 if (FineTickerAvailable()) {
4241 FineTickerStart(tickScroll, 100, 10);
4243 } else {
4244 if (PointIsHotspot(pt)) {
4245 NotifyHotSpotClicked(newCharPos.Position(), modifiers);
4246 hotSpotClickPos = newCharPos.Position();
4248 if (!shift) {
4249 if (PointInSelection(pt) && !SelectionEmpty())
4250 inDragDrop = ddInitial;
4251 else
4252 inDragDrop = ddNone;
4254 SetMouseCapture(true);
4255 if (FineTickerAvailable()) {
4256 FineTickerStart(tickScroll, 100, 10);
4258 if (inDragDrop != ddInitial) {
4259 SetDragPosition(SelectionPosition(invalidPosition));
4260 if (!shift) {
4261 if (ctrl && multipleSelection) {
4262 SelectionRange range(newPos);
4263 sel.TentativeSelection(range);
4264 InvalidateSelection(range, true);
4265 } else {
4266 InvalidateSelection(SelectionRange(newPos), true);
4267 if (sel.Count() > 1)
4268 Redraw();
4269 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
4270 sel.Clear();
4271 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
4272 SetSelection(newPos, newPos);
4275 SelectionPosition anchorCurrent = newPos;
4276 if (shift)
4277 anchorCurrent = sel.IsRectangular() ?
4278 sel.Rectangular().anchor : sel.RangeMain().anchor;
4279 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
4280 selectionType = selChar;
4281 originalAnchorPos = sel.MainCaret();
4282 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
4283 SetRectangularRange();
4287 lastClickTime = curTime;
4288 lastClick = pt;
4289 lastXChosen = static_cast<int>(pt.x) + xOffset;
4290 ShowCaretAtCurrentPosition();
4293 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
4294 return ButtonDownWithModifiers(pt, curTime, ModifierFlags(shift, ctrl, alt));
4297 bool Editor::PositionIsHotspot(int position) const {
4298 return vs.styles[static_cast<unsigned char>(pdoc->StyleAt(position))].hotspot;
4301 bool Editor::PointIsHotspot(Point pt) {
4302 int pos = PositionFromLocation(pt, true, true);
4303 if (pos == INVALID_POSITION)
4304 return false;
4305 return PositionIsHotspot(pos);
4308 void Editor::SetHoverIndicatorPosition(int position) {
4309 int hoverIndicatorPosPrev = hoverIndicatorPos;
4310 hoverIndicatorPos = INVALID_POSITION;
4311 if (vs.indicatorsDynamic == 0)
4312 return;
4313 if (position != INVALID_POSITION) {
4314 for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
4315 if (vs.indicators[deco->indicator].IsDynamic()) {
4316 if (pdoc->decorations.ValueAt(deco->indicator, position)) {
4317 hoverIndicatorPos = position;
4322 if (hoverIndicatorPosPrev != hoverIndicatorPos) {
4323 if (hoverIndicatorPosPrev != INVALID_POSITION)
4324 InvalidateRange(hoverIndicatorPosPrev, hoverIndicatorPosPrev + 1);
4325 if (hoverIndicatorPos != INVALID_POSITION)
4326 InvalidateRange(hoverIndicatorPos, hoverIndicatorPos + 1);
4330 void Editor::SetHoverIndicatorPoint(Point pt) {
4331 if (vs.indicatorsDynamic == 0) {
4332 SetHoverIndicatorPosition(INVALID_POSITION);
4333 } else {
4334 SetHoverIndicatorPosition(PositionFromLocation(pt, true, true));
4338 void Editor::SetHotSpotRange(Point *pt) {
4339 if (pt) {
4340 int pos = PositionFromLocation(*pt, false, true);
4342 // If we don't limit this to word characters then the
4343 // range can encompass more than the run range and then
4344 // the underline will not be drawn properly.
4345 Range hsNew;
4346 hsNew.start = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
4347 hsNew.end = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
4349 // Only invalidate the range if the hotspot range has changed...
4350 if (!(hsNew == hotspot)) {
4351 if (hotspot.Valid()) {
4352 InvalidateRange(hotspot.start, hotspot.end);
4354 hotspot = hsNew;
4355 InvalidateRange(hotspot.start, hotspot.end);
4357 } else {
4358 if (hotspot.Valid()) {
4359 InvalidateRange(hotspot.start, hotspot.end);
4361 hotspot = Range(invalidPosition);
4365 Range Editor::GetHotSpotRange() const {
4366 return hotspot;
4369 void Editor::ButtonMoveWithModifiers(Point pt, int modifiers) {
4370 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
4371 DwellEnd(true);
4374 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
4375 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4376 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
4378 if (inDragDrop == ddInitial) {
4379 if (DragThreshold(ptMouseLast, pt)) {
4380 SetMouseCapture(false);
4381 if (FineTickerAvailable()) {
4382 FineTickerCancel(tickScroll);
4384 SetDragPosition(movePos);
4385 CopySelectionRange(&drag);
4386 StartDrag();
4388 return;
4391 ptMouseLast = pt;
4392 PRectangle rcClient = GetClientRectangle();
4393 Point ptOrigin = GetVisibleOriginInMain();
4394 rcClient.Move(0, -ptOrigin.y);
4395 if (FineTickerAvailable() && (dwellDelay < SC_TIME_FOREVER) && rcClient.Contains(pt)) {
4396 FineTickerStart(tickDwell, dwellDelay, dwellDelay/10);
4398 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
4399 if (HaveMouseCapture()) {
4401 // Slow down autoscrolling/selection
4402 autoScrollTimer.ticksToWait -= timer.tickSize;
4403 if (autoScrollTimer.ticksToWait > 0)
4404 return;
4405 autoScrollTimer.ticksToWait = autoScrollDelay;
4407 // Adjust selection
4408 if (posDrag.IsValid()) {
4409 SetDragPosition(movePos);
4410 } else {
4411 if (selectionType == selChar) {
4412 if (sel.selType == Selection::selStream && (modifiers & SCI_ALT) && mouseSelectionRectangularSwitch) {
4413 sel.selType = Selection::selRectangle;
4415 if (sel.IsRectangular()) {
4416 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
4417 SetSelection(movePos, sel.RangeMain().anchor);
4418 } else if (sel.Count() > 1) {
4419 InvalidateSelection(sel.RangeMain(), false);
4420 SelectionRange range(movePos, sel.RangeMain().anchor);
4421 sel.TentativeSelection(range);
4422 InvalidateSelection(range, true);
4423 } else {
4424 SetSelection(movePos, sel.RangeMain().anchor);
4426 } else if (selectionType == selWord) {
4427 // Continue selecting by word
4428 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
4429 // No need to do anything. Previously this case was lumped
4430 // in with "Moved forward", but that can be harmful in this
4431 // case: a handler for the NotifyDoubleClick re-adjusts
4432 // the selection for a fancier definition of "word" (for
4433 // example, in Perl it is useful to include the leading
4434 // '$', '%' or '@' on variables for word selection). In this
4435 // the ButtonMove() called via Tick() for auto-scrolling
4436 // could result in the fancier word selection adjustment
4437 // being unmade.
4438 } else {
4439 wordSelectInitialCaretPos = -1;
4440 WordSelection(movePos.Position());
4442 } else {
4443 // Continue selecting by line
4444 LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
4448 // Autoscroll
4449 int lineMove = DisplayFromPosition(movePos.Position());
4450 if (pt.y > rcClient.bottom) {
4451 ScrollTo(lineMove - LinesOnScreen() + 1);
4452 Redraw();
4453 } else if (pt.y < rcClient.top) {
4454 ScrollTo(lineMove);
4455 Redraw();
4457 EnsureCaretVisible(false, false, true);
4459 if (hotspot.Valid() && !PointIsHotspot(pt))
4460 SetHotSpotRange(NULL);
4462 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,true) != hotSpotClickPos) {
4463 if (inDragDrop == ddNone) {
4464 DisplayCursor(Window::cursorText);
4466 hotSpotClickPos = INVALID_POSITION;
4469 } else {
4470 if (vs.fixedColumnWidth > 0) { // There is a margin
4471 if (PointInSelMargin(pt)) {
4472 DisplayCursor(GetMarginCursor(pt));
4473 SetHotSpotRange(NULL);
4474 return; // No need to test for selection
4477 // Display regular (drag) cursor over selection
4478 if (PointInSelection(pt) && !SelectionEmpty()) {
4479 DisplayCursor(Window::cursorArrow);
4480 } else {
4481 SetHoverIndicatorPoint(pt);
4482 if (PointIsHotspot(pt)) {
4483 DisplayCursor(Window::cursorHand);
4484 SetHotSpotRange(&pt);
4485 } else {
4486 if (hoverIndicatorPos != invalidPosition)
4487 DisplayCursor(Window::cursorHand);
4488 else
4489 DisplayCursor(Window::cursorText);
4490 SetHotSpotRange(NULL);
4496 void Editor::ButtonMove(Point pt) {
4497 ButtonMoveWithModifiers(pt, 0);
4500 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
4501 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
4502 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
4503 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4504 if (hoverIndicatorPos != INVALID_POSITION)
4505 InvalidateRange(newPos.Position(), newPos.Position() + 1);
4506 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4507 if (inDragDrop == ddInitial) {
4508 inDragDrop = ddNone;
4509 SetEmptySelection(newPos);
4510 selectionType = selChar;
4511 originalAnchorPos = sel.MainCaret();
4513 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
4514 hotSpotClickPos = INVALID_POSITION;
4515 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4516 newCharPos = MovePositionOutsideChar(newCharPos, -1);
4517 NotifyHotSpotReleaseClick(newCharPos.Position(), ctrl ? SCI_CTRL : 0);
4519 if (HaveMouseCapture()) {
4520 if (PointInSelMargin(pt)) {
4521 DisplayCursor(GetMarginCursor(pt));
4522 } else {
4523 DisplayCursor(Window::cursorText);
4524 SetHotSpotRange(NULL);
4526 ptMouseLast = pt;
4527 SetMouseCapture(false);
4528 if (FineTickerAvailable()) {
4529 FineTickerCancel(tickScroll);
4531 NotifyIndicatorClick(false, newPos.Position(), 0);
4532 if (inDragDrop == ddDragging) {
4533 SelectionPosition selStart = SelectionStart();
4534 SelectionPosition selEnd = SelectionEnd();
4535 if (selStart < selEnd) {
4536 if (drag.Length()) {
4537 const int length = static_cast<int>(drag.Length());
4538 if (ctrl) {
4539 const int lengthInserted = pdoc->InsertString(
4540 newPos.Position(), drag.Data(), length);
4541 if (lengthInserted > 0) {
4542 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4544 } else if (newPos < selStart) {
4545 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
4546 const int lengthInserted = pdoc->InsertString(
4547 newPos.Position(), drag.Data(), length);
4548 if (lengthInserted > 0) {
4549 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4551 } else if (newPos > selEnd) {
4552 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
4553 newPos.Add(-static_cast<int>(drag.Length()));
4554 const int lengthInserted = pdoc->InsertString(
4555 newPos.Position(), drag.Data(), length);
4556 if (lengthInserted > 0) {
4557 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4559 } else {
4560 SetEmptySelection(newPos.Position());
4562 drag.Clear();
4564 selectionType = selChar;
4566 } else {
4567 if (selectionType == selChar) {
4568 if (sel.Count() > 1) {
4569 sel.RangeMain() =
4570 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
4571 InvalidateSelection(sel.RangeMain(), true);
4572 } else {
4573 SetSelection(newPos, sel.RangeMain().anchor);
4576 sel.CommitTentative();
4578 SetRectangularRange();
4579 lastClickTime = curTime;
4580 lastClick = pt;
4581 lastXChosen = static_cast<int>(pt.x) + xOffset;
4582 if (sel.selType == Selection::selStream) {
4583 SetLastXChosen();
4585 inDragDrop = ddNone;
4586 EnsureCaretVisible(false);
4590 // Called frequently to perform background UI including
4591 // caret blinking and automatic scrolling.
4592 void Editor::Tick() {
4593 if (HaveMouseCapture()) {
4594 // Auto scroll
4595 ButtonMove(ptMouseLast);
4597 if (caret.period > 0) {
4598 timer.ticksToWait -= timer.tickSize;
4599 if (timer.ticksToWait <= 0) {
4600 caret.on = !caret.on;
4601 timer.ticksToWait = caret.period;
4602 if (caret.active) {
4603 InvalidateCaret();
4607 if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) {
4608 scrollWidth = view.lineWidthMaxSeen;
4609 SetScrollBars();
4611 if ((dwellDelay < SC_TIME_FOREVER) &&
4612 (ticksToDwell > 0) &&
4613 (!HaveMouseCapture()) &&
4614 (ptMouseLast.y >= 0)) {
4615 ticksToDwell -= timer.tickSize;
4616 if (ticksToDwell <= 0) {
4617 dwelling = true;
4618 NotifyDwelling(ptMouseLast, dwelling);
4623 bool Editor::Idle() {
4625 bool idleDone;
4627 bool wrappingDone = !Wrapping();
4629 if (!wrappingDone) {
4630 // Wrap lines during idle.
4631 WrapLines(wsIdle);
4632 // No more wrapping
4633 if (!wrapPending.NeedsWrap())
4634 wrappingDone = true;
4637 // Add more idle things to do here, but make sure idleDone is
4638 // set correctly before the function returns. returning
4639 // false will stop calling this idle function until SetIdle() is
4640 // called again.
4642 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
4644 return !idleDone;
4647 void Editor::SetTicking(bool) {
4648 // SetTicking is deprecated. In the past it was pure virtual and was overridden in each
4649 // derived platform class but fine grained timers should now be implemented.
4650 // Either way, execution should not arrive here so assert failure.
4651 assert(false);
4654 void Editor::TickFor(TickReason reason) {
4655 switch (reason) {
4656 case tickCaret:
4657 caret.on = !caret.on;
4658 if (caret.active) {
4659 InvalidateCaret();
4661 break;
4662 case tickScroll:
4663 // Auto scroll
4664 ButtonMove(ptMouseLast);
4665 break;
4666 case tickWiden:
4667 SetScrollBars();
4668 FineTickerCancel(tickWiden);
4669 break;
4670 case tickDwell:
4671 if ((!HaveMouseCapture()) &&
4672 (ptMouseLast.y >= 0)) {
4673 dwelling = true;
4674 NotifyDwelling(ptMouseLast, dwelling);
4676 FineTickerCancel(tickDwell);
4677 break;
4678 default:
4679 // tickPlatform handled by subclass
4680 break;
4684 bool Editor::FineTickerAvailable() {
4685 return false;
4688 // FineTickerStart is be overridden by subclasses that support fine ticking so
4689 // this method should never be called.
4690 bool Editor::FineTickerRunning(TickReason) {
4691 assert(false);
4692 return false;
4695 // FineTickerStart is be overridden by subclasses that support fine ticking so
4696 // this method should never be called.
4697 void Editor::FineTickerStart(TickReason, int, int) {
4698 assert(false);
4701 // FineTickerCancel is be overridden by subclasses that support fine ticking so
4702 // this method should never be called.
4703 void Editor::FineTickerCancel(TickReason) {
4704 assert(false);
4707 void Editor::SetFocusState(bool focusState) {
4708 hasFocus = focusState;
4709 NotifyFocus(hasFocus);
4710 if (!hasFocus) {
4711 CancelModes();
4713 ShowCaretAtCurrentPosition();
4716 int Editor::PositionAfterArea(PRectangle rcArea) const {
4717 // The start of the document line after the display line after the area
4718 // This often means that the line after a modification is restyled which helps
4719 // detect multiline comment additions and heals single line comments
4720 int lineAfter = TopLineOfMain() + static_cast<int>(rcArea.bottom - 1) / vs.lineHeight + 1;
4721 if (lineAfter < cs.LinesDisplayed())
4722 return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
4723 else
4724 return pdoc->Length();
4727 // Style to a position within the view. If this causes a change at end of last line then
4728 // affects later lines so style all the viewed text.
4729 void Editor::StyleToPositionInView(Position pos) {
4730 int endWindow = PositionAfterArea(GetClientDrawingRectangle());
4731 if (pos > endWindow)
4732 pos = endWindow;
4733 int styleAtEnd = pdoc->StyleAt(pos-1);
4734 pdoc->EnsureStyledTo(pos);
4735 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleAt(pos-1))) {
4736 // Style at end of line changed so is multi-line change like starting a comment
4737 // so require rest of window to be styled.
4738 DiscardOverdraw(); // Prepared bitmaps may be invalid
4739 // DiscardOverdraw may have truncated client drawing area so recalculate endWindow
4740 endWindow = PositionAfterArea(GetClientDrawingRectangle());
4741 pdoc->EnsureStyledTo(endWindow);
4745 void Editor::IdleWork() {
4746 // Style the line after the modification as this allows modifications that change just the
4747 // line of the modification to heal instead of propagating to the rest of the window.
4748 if (workNeeded.items & WorkNeeded::workStyle)
4749 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2));
4751 NotifyUpdateUI();
4752 workNeeded.Reset();
4755 void Editor::QueueIdleWork(WorkNeeded::workItems items, int upTo) {
4756 workNeeded.Need(items, upTo);
4759 bool Editor::PaintContains(PRectangle rc) {
4760 if (rc.Empty()) {
4761 return true;
4762 } else {
4763 return rcPaint.Contains(rc);
4767 bool Editor::PaintContainsMargin() {
4768 if (wMargin.GetID()) {
4769 // With separate margin view, paint of text view
4770 // never contains margin.
4771 return false;
4773 PRectangle rcSelMargin = GetClientRectangle();
4774 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart);
4775 return PaintContains(rcSelMargin);
4778 void Editor::CheckForChangeOutsidePaint(Range r) {
4779 if (paintState == painting && !paintingAllText) {
4780 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
4781 if (!r.Valid())
4782 return;
4784 PRectangle rcRange = RectangleFromRange(r, 0);
4785 PRectangle rcText = GetTextRectangle();
4786 if (rcRange.top < rcText.top) {
4787 rcRange.top = rcText.top;
4789 if (rcRange.bottom > rcText.bottom) {
4790 rcRange.bottom = rcText.bottom;
4793 if (!PaintContains(rcRange)) {
4794 AbandonPaint();
4795 paintAbandonedByStyling = true;
4800 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
4801 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
4802 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
4803 CheckForChangeOutsidePaint(Range(braces[0]));
4804 CheckForChangeOutsidePaint(Range(pos0));
4805 braces[0] = pos0;
4807 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
4808 CheckForChangeOutsidePaint(Range(braces[1]));
4809 CheckForChangeOutsidePaint(Range(pos1));
4810 braces[1] = pos1;
4812 bracesMatchStyle = matchStyle;
4813 if (paintState == notPainting) {
4814 Redraw();
4819 void Editor::SetAnnotationHeights(int start, int end) {
4820 if (vs.annotationVisible) {
4821 RefreshStyleData();
4822 bool changedHeight = false;
4823 for (int line=start; line<end && line<pdoc->LinesTotal(); line++) {
4824 int linesWrapped = 1;
4825 if (Wrapping()) {
4826 AutoSurface surface(this);
4827 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
4828 if (surface && ll) {
4829 view.LayoutLine(*this, line, surface, vs, ll, wrapWidth);
4830 linesWrapped = ll->lines;
4833 if (cs.SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
4834 changedHeight = true;
4836 if (changedHeight) {
4837 Redraw();
4842 void Editor::SetDocPointer(Document *document) {
4843 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
4844 pdoc->RemoveWatcher(this, 0);
4845 pdoc->Release();
4846 if (document == NULL) {
4847 pdoc = new Document();
4848 } else {
4849 pdoc = document;
4851 pdoc->AddRef();
4853 // Ensure all positions within document
4854 sel.Clear();
4855 targetStart = 0;
4856 targetEnd = 0;
4858 braces[0] = invalidPosition;
4859 braces[1] = invalidPosition;
4861 vs.ReleaseAllExtendedStyles();
4863 SetRepresentations();
4865 // Reset the contraction state to fully shown.
4866 cs.Clear();
4867 cs.InsertLines(0, pdoc->LinesTotal() - 1);
4868 SetAnnotationHeights(0, pdoc->LinesTotal());
4869 view.llc.Deallocate();
4870 NeedWrapping();
4872 hotspot = Range(invalidPosition);
4873 hoverIndicatorPos = invalidPosition;
4875 view.ClearAllTabstops();
4877 pdoc->AddWatcher(this, 0);
4878 SetScrollBars();
4879 Redraw();
4882 void Editor::SetAnnotationVisible(int visible) {
4883 if (vs.annotationVisible != visible) {
4884 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
4885 vs.annotationVisible = visible;
4886 if (changedFromOrToHidden) {
4887 int dir = vs.annotationVisible ? 1 : -1;
4888 for (int line=0; line<pdoc->LinesTotal(); line++) {
4889 int annotationLines = pdoc->AnnotationLines(line);
4890 if (annotationLines > 0) {
4891 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
4895 Redraw();
4900 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
4902 int Editor::ExpandLine(int line) {
4903 int lineMaxSubord = pdoc->GetLastChild(line);
4904 line++;
4905 while (line <= lineMaxSubord) {
4906 cs.SetVisible(line, line, true);
4907 int level = pdoc->GetLevel(line);
4908 if (level & SC_FOLDLEVELHEADERFLAG) {
4909 if (cs.GetExpanded(line)) {
4910 line = ExpandLine(line);
4911 } else {
4912 line = pdoc->GetLastChild(line);
4915 line++;
4917 return lineMaxSubord;
4920 void Editor::SetFoldExpanded(int lineDoc, bool expanded) {
4921 if (cs.SetExpanded(lineDoc, expanded)) {
4922 RedrawSelMargin();
4926 void Editor::FoldLine(int line, int action) {
4927 if (line >= 0) {
4928 if (action == SC_FOLDACTION_TOGGLE) {
4929 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
4930 line = pdoc->GetFoldParent(line);
4931 if (line < 0)
4932 return;
4934 action = (cs.GetExpanded(line)) ? SC_FOLDACTION_CONTRACT : SC_FOLDACTION_EXPAND;
4937 if (action == SC_FOLDACTION_CONTRACT) {
4938 int lineMaxSubord = pdoc->GetLastChild(line);
4939 if (lineMaxSubord > line) {
4940 cs.SetExpanded(line, 0);
4941 cs.SetVisible(line + 1, lineMaxSubord, false);
4943 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
4944 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
4945 // This does not re-expand the fold
4946 EnsureCaretVisible();
4950 } else {
4951 if (!(cs.GetVisible(line))) {
4952 EnsureLineVisible(line, false);
4953 GoToLine(line);
4955 cs.SetExpanded(line, 1);
4956 ExpandLine(line);
4959 SetScrollBars();
4960 Redraw();
4964 void Editor::FoldExpand(int line, int action, int level) {
4965 bool expanding = action == SC_FOLDACTION_EXPAND;
4966 if (action == SC_FOLDACTION_TOGGLE) {
4967 expanding = !cs.GetExpanded(line);
4969 SetFoldExpanded(line, expanding);
4970 if (expanding && (cs.HiddenLines() == 0))
4971 // Nothing to do
4972 return;
4973 int lineMaxSubord = pdoc->GetLastChild(line, level & SC_FOLDLEVELNUMBERMASK);
4974 line++;
4975 cs.SetVisible(line, lineMaxSubord, expanding);
4976 while (line <= lineMaxSubord) {
4977 int levelLine = pdoc->GetLevel(line);
4978 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
4979 SetFoldExpanded(line, expanding);
4981 line++;
4983 SetScrollBars();
4984 Redraw();
4987 int Editor::ContractedFoldNext(int lineStart) const {
4988 for (int line = lineStart; line<pdoc->LinesTotal();) {
4989 if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
4990 return line;
4991 line = cs.ContractedNext(line+1);
4992 if (line < 0)
4993 return -1;
4996 return -1;
5000 * Recurse up from this line to find any folds that prevent this line from being visible
5001 * and unfold them all.
5003 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
5005 // In case in need of wrapping to ensure DisplayFromDoc works.
5006 if (lineDoc >= wrapPending.start)
5007 WrapLines(wsAll);
5009 if (!cs.GetVisible(lineDoc)) {
5010 // Back up to find a non-blank line
5011 int lookLine = lineDoc;
5012 int lookLineLevel = pdoc->GetLevel(lookLine);
5013 while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
5014 lookLineLevel = pdoc->GetLevel(--lookLine);
5016 int lineParent = pdoc->GetFoldParent(lookLine);
5017 if (lineParent < 0) {
5018 // Backed up to a top level line, so try to find parent of initial line
5019 lineParent = pdoc->GetFoldParent(lineDoc);
5021 if (lineParent >= 0) {
5022 if (lineDoc != lineParent)
5023 EnsureLineVisible(lineParent, enforcePolicy);
5024 if (!cs.GetExpanded(lineParent)) {
5025 cs.SetExpanded(lineParent, 1);
5026 ExpandLine(lineParent);
5029 SetScrollBars();
5030 Redraw();
5032 if (enforcePolicy) {
5033 int lineDisplay = cs.DisplayFromDoc(lineDoc);
5034 if (visiblePolicy & VISIBLE_SLOP) {
5035 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
5036 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
5037 SetVerticalScrollPos();
5038 Redraw();
5039 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
5040 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
5041 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
5042 SetVerticalScrollPos();
5043 Redraw();
5045 } else {
5046 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
5047 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
5048 SetVerticalScrollPos();
5049 Redraw();
5055 void Editor::FoldAll(int action) {
5056 pdoc->EnsureStyledTo(pdoc->Length());
5057 int maxLine = pdoc->LinesTotal();
5058 bool expanding = action == SC_FOLDACTION_EXPAND;
5059 if (action == SC_FOLDACTION_TOGGLE) {
5060 // Discover current state
5061 for (int lineSeek = 0; lineSeek < maxLine; lineSeek++) {
5062 if (pdoc->GetLevel(lineSeek) & SC_FOLDLEVELHEADERFLAG) {
5063 expanding = !cs.GetExpanded(lineSeek);
5064 break;
5068 if (expanding) {
5069 cs.SetVisible(0, maxLine-1, true);
5070 for (int line = 0; line < maxLine; line++) {
5071 int levelLine = pdoc->GetLevel(line);
5072 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
5073 SetFoldExpanded(line, true);
5076 } else {
5077 for (int line = 0; line < maxLine; line++) {
5078 int level = pdoc->GetLevel(line);
5079 if ((level & SC_FOLDLEVELHEADERFLAG) &&
5080 (SC_FOLDLEVELBASE == (level & SC_FOLDLEVELNUMBERMASK))) {
5081 SetFoldExpanded(line, false);
5082 int lineMaxSubord = pdoc->GetLastChild(line, -1);
5083 if (lineMaxSubord > line) {
5084 cs.SetVisible(line + 1, lineMaxSubord, false);
5089 SetScrollBars();
5090 Redraw();
5093 void Editor::FoldChanged(int line, int levelNow, int levelPrev) {
5094 if (levelNow & SC_FOLDLEVELHEADERFLAG) {
5095 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
5096 // Adding a fold point.
5097 if (cs.SetExpanded(line, true)) {
5098 RedrawSelMargin();
5100 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
5102 } else if (levelPrev & SC_FOLDLEVELHEADERFLAG) {
5103 if (!cs.GetExpanded(line)) {
5104 // Removing the fold from one that has been contracted so should expand
5105 // otherwise lines are left invisible with no way to make them visible
5106 if (cs.SetExpanded(line, true)) {
5107 RedrawSelMargin();
5109 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
5112 if (!(levelNow & SC_FOLDLEVELWHITEFLAG) &&
5113 ((levelPrev & SC_FOLDLEVELNUMBERMASK) > (levelNow & SC_FOLDLEVELNUMBERMASK))) {
5114 if (cs.HiddenLines()) {
5115 // See if should still be hidden
5116 int parentLine = pdoc->GetFoldParent(line);
5117 if ((parentLine < 0) || (cs.GetExpanded(parentLine) && cs.GetVisible(parentLine))) {
5118 cs.SetVisible(line, line, true);
5119 SetScrollBars();
5120 Redraw();
5126 void Editor::NeedShown(int pos, int len) {
5127 if (foldAutomatic & SC_AUTOMATICFOLD_SHOW) {
5128 int lineStart = pdoc->LineFromPosition(pos);
5129 int lineEnd = pdoc->LineFromPosition(pos+len);
5130 for (int line = lineStart; line <= lineEnd; line++) {
5131 EnsureLineVisible(line, false);
5133 } else {
5134 NotifyNeedShown(pos, len);
5138 int Editor::GetTag(char *tagValue, int tagNumber) {
5139 const char *text = 0;
5140 int length = 0;
5141 if ((tagNumber >= 1) && (tagNumber <= 9)) {
5142 char name[3] = "\\?";
5143 name[1] = static_cast<char>(tagNumber + '0');
5144 length = 2;
5145 text = pdoc->SubstituteByPosition(name, &length);
5147 if (tagValue) {
5148 if (text)
5149 memcpy(tagValue, text, length + 1);
5150 else
5151 *tagValue = '\0';
5153 return length;
5156 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
5157 UndoGroup ug(pdoc);
5158 if (length == -1)
5159 length = istrlen(text);
5160 if (replacePatterns) {
5161 text = pdoc->SubstituteByPosition(text, &length);
5162 if (!text) {
5163 return 0;
5166 if (targetStart != targetEnd)
5167 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
5168 targetEnd = targetStart;
5169 const int lengthInserted = pdoc->InsertString(targetStart, text, length);
5170 targetEnd = targetStart + lengthInserted;
5171 return length;
5174 bool Editor::IsUnicodeMode() const {
5175 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
5178 int Editor::CodePage() const {
5179 if (pdoc)
5180 return pdoc->dbcsCodePage;
5181 else
5182 return 0;
5185 int Editor::WrapCount(int line) {
5186 AutoSurface surface(this);
5187 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
5189 if (surface && ll) {
5190 view.LayoutLine(*this, line, surface, vs, ll, wrapWidth);
5191 return ll->lines;
5192 } else {
5193 return 1;
5197 void Editor::AddStyledText(char *buffer, int appendLength) {
5198 // The buffer consists of alternating character bytes and style bytes
5199 int textLength = appendLength / 2;
5200 std::string text(textLength, '\0');
5201 int i;
5202 for (i = 0; i < textLength; i++) {
5203 text[i] = buffer[i*2];
5205 const int lengthInserted = pdoc->InsertString(CurrentPosition(), text.c_str(), textLength);
5206 for (i = 0; i < textLength; i++) {
5207 text[i] = buffer[i*2+1];
5209 pdoc->StartStyling(CurrentPosition(), static_cast<unsigned char>(0xff));
5210 pdoc->SetStyles(textLength, text.c_str());
5211 SetEmptySelection(sel.MainCaret() + lengthInserted);
5214 static bool ValidMargin(uptr_t wParam) {
5215 return wParam <= SC_MAX_MARGIN;
5218 static char *CharPtrFromSPtr(sptr_t lParam) {
5219 return reinterpret_cast<char *>(lParam);
5222 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5223 vs.EnsureStyle(wParam);
5224 switch (iMessage) {
5225 case SCI_STYLESETFORE:
5226 vs.styles[wParam].fore = ColourDesired(static_cast<long>(lParam));
5227 break;
5228 case SCI_STYLESETBACK:
5229 vs.styles[wParam].back = ColourDesired(static_cast<long>(lParam));
5230 break;
5231 case SCI_STYLESETBOLD:
5232 vs.styles[wParam].weight = lParam != 0 ? SC_WEIGHT_BOLD : SC_WEIGHT_NORMAL;
5233 break;
5234 case SCI_STYLESETWEIGHT:
5235 vs.styles[wParam].weight = static_cast<int>(lParam);
5236 break;
5237 case SCI_STYLESETITALIC:
5238 vs.styles[wParam].italic = lParam != 0;
5239 break;
5240 case SCI_STYLESETEOLFILLED:
5241 vs.styles[wParam].eolFilled = lParam != 0;
5242 break;
5243 case SCI_STYLESETSIZE:
5244 vs.styles[wParam].size = static_cast<int>(lParam * SC_FONT_SIZE_MULTIPLIER);
5245 break;
5246 case SCI_STYLESETSIZEFRACTIONAL:
5247 vs.styles[wParam].size = static_cast<int>(lParam);
5248 break;
5249 case SCI_STYLESETFONT:
5250 if (lParam != 0) {
5251 vs.SetStyleFontName(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
5253 break;
5254 case SCI_STYLESETUNDERLINE:
5255 vs.styles[wParam].underline = lParam != 0;
5256 break;
5257 case SCI_STYLESETCASE:
5258 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
5259 break;
5260 case SCI_STYLESETCHARACTERSET:
5261 vs.styles[wParam].characterSet = static_cast<int>(lParam);
5262 pdoc->SetCaseFolder(NULL);
5263 break;
5264 case SCI_STYLESETVISIBLE:
5265 vs.styles[wParam].visible = lParam != 0;
5266 break;
5267 case SCI_STYLESETCHANGEABLE:
5268 vs.styles[wParam].changeable = lParam != 0;
5269 break;
5270 case SCI_STYLESETHOTSPOT:
5271 vs.styles[wParam].hotspot = lParam != 0;
5272 break;
5274 InvalidateStyleRedraw();
5277 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5278 vs.EnsureStyle(wParam);
5279 switch (iMessage) {
5280 case SCI_STYLEGETFORE:
5281 return vs.styles[wParam].fore.AsLong();
5282 case SCI_STYLEGETBACK:
5283 return vs.styles[wParam].back.AsLong();
5284 case SCI_STYLEGETBOLD:
5285 return vs.styles[wParam].weight > SC_WEIGHT_NORMAL;
5286 case SCI_STYLEGETWEIGHT:
5287 return vs.styles[wParam].weight;
5288 case SCI_STYLEGETITALIC:
5289 return vs.styles[wParam].italic ? 1 : 0;
5290 case SCI_STYLEGETEOLFILLED:
5291 return vs.styles[wParam].eolFilled ? 1 : 0;
5292 case SCI_STYLEGETSIZE:
5293 return vs.styles[wParam].size / SC_FONT_SIZE_MULTIPLIER;
5294 case SCI_STYLEGETSIZEFRACTIONAL:
5295 return vs.styles[wParam].size;
5296 case SCI_STYLEGETFONT:
5297 return StringResult(lParam, vs.styles[wParam].fontName);
5298 case SCI_STYLEGETUNDERLINE:
5299 return vs.styles[wParam].underline ? 1 : 0;
5300 case SCI_STYLEGETCASE:
5301 return static_cast<int>(vs.styles[wParam].caseForce);
5302 case SCI_STYLEGETCHARACTERSET:
5303 return vs.styles[wParam].characterSet;
5304 case SCI_STYLEGETVISIBLE:
5305 return vs.styles[wParam].visible ? 1 : 0;
5306 case SCI_STYLEGETCHANGEABLE:
5307 return vs.styles[wParam].changeable ? 1 : 0;
5308 case SCI_STYLEGETHOTSPOT:
5309 return vs.styles[wParam].hotspot ? 1 : 0;
5311 return 0;
5314 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
5315 const size_t len = val ? strlen(val) : 0;
5316 if (lParam) {
5317 char *ptr = CharPtrFromSPtr(lParam);
5318 if (val)
5319 memcpy(ptr, val, len+1);
5320 else
5321 *ptr = 0;
5323 return len; // Not including NUL
5326 sptr_t Editor::BytesResult(sptr_t lParam, const unsigned char *val, size_t len) {
5327 // No NUL termination: len is number of valid/displayed bytes
5328 if ((lParam) && (len > 0)) {
5329 char *ptr = CharPtrFromSPtr(lParam);
5330 if (val)
5331 memcpy(ptr, val, len);
5332 else
5333 *ptr = 0;
5335 return val ? len : 0;
5338 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5339 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
5341 // Optional macro recording hook
5342 if (recordingMacro)
5343 NotifyMacroRecord(iMessage, wParam, lParam);
5345 switch (iMessage) {
5347 case SCI_GETTEXT: {
5348 if (lParam == 0)
5349 return pdoc->Length() + 1;
5350 if (wParam == 0)
5351 return 0;
5352 char *ptr = CharPtrFromSPtr(lParam);
5353 unsigned int iChar = 0;
5354 for (; iChar < wParam - 1; iChar++)
5355 ptr[iChar] = pdoc->CharAt(iChar);
5356 ptr[iChar] = '\0';
5357 return iChar;
5360 case SCI_SETTEXT: {
5361 if (lParam == 0)
5362 return 0;
5363 UndoGroup ug(pdoc);
5364 pdoc->DeleteChars(0, pdoc->Length());
5365 SetEmptySelection(0);
5366 const char *text = CharPtrFromSPtr(lParam);
5367 pdoc->InsertString(0, text, istrlen(text));
5368 return 1;
5371 case SCI_GETTEXTLENGTH:
5372 return pdoc->Length();
5374 case SCI_CUT:
5375 Cut();
5376 SetLastXChosen();
5377 break;
5379 case SCI_COPY:
5380 Copy();
5381 break;
5383 case SCI_COPYALLOWLINE:
5384 CopyAllowLine();
5385 break;
5387 case SCI_VERTICALCENTRECARET:
5388 VerticalCentreCaret();
5389 break;
5391 case SCI_MOVESELECTEDLINESUP:
5392 MoveSelectedLinesUp();
5393 break;
5395 case SCI_MOVESELECTEDLINESDOWN:
5396 MoveSelectedLinesDown();
5397 break;
5399 case SCI_COPYRANGE:
5400 CopyRangeToClipboard(static_cast<int>(wParam), static_cast<int>(lParam));
5401 break;
5403 case SCI_COPYTEXT:
5404 CopyText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
5405 break;
5407 case SCI_PASTE:
5408 Paste();
5409 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5410 SetLastXChosen();
5412 EnsureCaretVisible();
5413 break;
5415 case SCI_CLEAR:
5416 Clear();
5417 SetLastXChosen();
5418 EnsureCaretVisible();
5419 break;
5421 case SCI_UNDO:
5422 Undo();
5423 SetLastXChosen();
5424 break;
5426 case SCI_CANUNDO:
5427 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
5429 case SCI_EMPTYUNDOBUFFER:
5430 pdoc->DeleteUndoHistory();
5431 return 0;
5433 case SCI_GETFIRSTVISIBLELINE:
5434 return topLine;
5436 case SCI_SETFIRSTVISIBLELINE:
5437 ScrollTo(static_cast<int>(wParam));
5438 break;
5440 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
5441 int lineStart = pdoc->LineStart(static_cast<int>(wParam));
5442 int lineEnd = pdoc->LineStart(static_cast<int>(wParam + 1));
5443 if (lParam == 0) {
5444 return lineEnd - lineStart;
5446 char *ptr = CharPtrFromSPtr(lParam);
5447 int iPlace = 0;
5448 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
5449 ptr[iPlace++] = pdoc->CharAt(iChar);
5451 return iPlace;
5454 case SCI_GETLINECOUNT:
5455 if (pdoc->LinesTotal() == 0)
5456 return 1;
5457 else
5458 return pdoc->LinesTotal();
5460 case SCI_GETMODIFY:
5461 return !pdoc->IsSavePoint();
5463 case SCI_SETSEL: {
5464 int nStart = static_cast<int>(wParam);
5465 int nEnd = static_cast<int>(lParam);
5466 if (nEnd < 0)
5467 nEnd = pdoc->Length();
5468 if (nStart < 0)
5469 nStart = nEnd; // Remove selection
5470 InvalidateSelection(SelectionRange(nStart, nEnd));
5471 sel.Clear();
5472 sel.selType = Selection::selStream;
5473 SetSelection(nEnd, nStart);
5474 EnsureCaretVisible();
5476 break;
5478 case SCI_GETSELTEXT: {
5479 SelectionText selectedText;
5480 CopySelectionRange(&selectedText);
5481 if (lParam == 0) {
5482 return selectedText.LengthWithTerminator();
5483 } else {
5484 char *ptr = CharPtrFromSPtr(lParam);
5485 unsigned int iChar = 0;
5486 if (selectedText.Length()) {
5487 for (; iChar < selectedText.LengthWithTerminator(); iChar++)
5488 ptr[iChar] = selectedText.Data()[iChar];
5489 } else {
5490 ptr[0] = '\0';
5492 return iChar;
5496 case SCI_LINEFROMPOSITION:
5497 if (static_cast<int>(wParam) < 0)
5498 return 0;
5499 return pdoc->LineFromPosition(static_cast<int>(wParam));
5501 case SCI_POSITIONFROMLINE:
5502 if (static_cast<int>(wParam) < 0)
5503 wParam = pdoc->LineFromPosition(SelectionStart().Position());
5504 if (wParam == 0)
5505 return 0; // Even if there is no text, there is a first line that starts at 0
5506 if (static_cast<int>(wParam) > pdoc->LinesTotal())
5507 return -1;
5508 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
5509 // return -1;
5510 return pdoc->LineStart(static_cast<int>(wParam));
5512 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
5513 case SCI_LINELENGTH:
5514 if ((static_cast<int>(wParam) < 0) ||
5515 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
5516 return 0;
5517 return pdoc->LineStart(static_cast<int>(wParam) + 1) - pdoc->LineStart(static_cast<int>(wParam));
5519 case SCI_REPLACESEL: {
5520 if (lParam == 0)
5521 return 0;
5522 UndoGroup ug(pdoc);
5523 ClearSelection();
5524 char *replacement = CharPtrFromSPtr(lParam);
5525 const int lengthInserted = pdoc->InsertString(
5526 sel.MainCaret(), replacement, istrlen(replacement));
5527 SetEmptySelection(sel.MainCaret() + lengthInserted);
5528 EnsureCaretVisible();
5530 break;
5532 case SCI_SETTARGETSTART:
5533 targetStart = static_cast<int>(wParam);
5534 break;
5536 case SCI_GETTARGETSTART:
5537 return targetStart;
5539 case SCI_SETTARGETEND:
5540 targetEnd = static_cast<int>(wParam);
5541 break;
5543 case SCI_GETTARGETEND:
5544 return targetEnd;
5546 case SCI_SETTARGETRANGE:
5547 targetStart = static_cast<int>(wParam);
5548 targetEnd = static_cast<int>(lParam);
5549 break;
5551 case SCI_TARGETFROMSELECTION:
5552 if (sel.MainCaret() < sel.MainAnchor()) {
5553 targetStart = sel.MainCaret();
5554 targetEnd = sel.MainAnchor();
5555 } else {
5556 targetStart = sel.MainAnchor();
5557 targetEnd = sel.MainCaret();
5559 break;
5561 case SCI_GETTARGETTEXT: {
5562 std::string text = RangeText(targetStart, targetEnd);
5563 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(text.c_str()), text.length());
5566 case SCI_REPLACETARGET:
5567 PLATFORM_ASSERT(lParam);
5568 return ReplaceTarget(false, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5570 case SCI_REPLACETARGETRE:
5571 PLATFORM_ASSERT(lParam);
5572 return ReplaceTarget(true, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5574 case SCI_SEARCHINTARGET:
5575 PLATFORM_ASSERT(lParam);
5576 return SearchInTarget(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5578 case SCI_SETSEARCHFLAGS:
5579 searchFlags = static_cast<int>(wParam);
5580 break;
5582 case SCI_GETSEARCHFLAGS:
5583 return searchFlags;
5585 case SCI_GETTAG:
5586 return GetTag(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5588 case SCI_POSITIONBEFORE:
5589 return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) - 1, -1, true);
5591 case SCI_POSITIONAFTER:
5592 return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) + 1, 1, true);
5594 case SCI_POSITIONRELATIVE:
5595 return Platform::Clamp(pdoc->GetRelativePosition(static_cast<int>(wParam), static_cast<int>(lParam)), 0, pdoc->Length());
5597 case SCI_LINESCROLL:
5598 ScrollTo(topLine + static_cast<int>(lParam));
5599 HorizontalScrollTo(xOffset + static_cast<int>(wParam)* static_cast<int>(vs.spaceWidth));
5600 return 1;
5602 case SCI_SETXOFFSET:
5603 xOffset = static_cast<int>(wParam);
5604 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
5605 SetHorizontalScrollPos();
5606 Redraw();
5607 break;
5609 case SCI_GETXOFFSET:
5610 return xOffset;
5612 case SCI_CHOOSECARETX:
5613 SetLastXChosen();
5614 break;
5616 case SCI_SCROLLCARET:
5617 EnsureCaretVisible();
5618 break;
5620 case SCI_SETREADONLY:
5621 pdoc->SetReadOnly(wParam != 0);
5622 return 1;
5624 case SCI_GETREADONLY:
5625 return pdoc->IsReadOnly();
5627 case SCI_CANPASTE:
5628 return CanPaste();
5630 case SCI_POINTXFROMPOSITION:
5631 if (lParam < 0) {
5632 return 0;
5633 } else {
5634 Point pt = LocationFromPosition(static_cast<int>(lParam));
5635 // Convert to view-relative
5636 return static_cast<int>(pt.x) - vs.textStart + vs.fixedColumnWidth;
5639 case SCI_POINTYFROMPOSITION:
5640 if (lParam < 0) {
5641 return 0;
5642 } else {
5643 Point pt = LocationFromPosition(static_cast<int>(lParam));
5644 return static_cast<int>(pt.y);
5647 case SCI_FINDTEXT:
5648 return FindText(wParam, lParam);
5650 case SCI_GETTEXTRANGE: {
5651 if (lParam == 0)
5652 return 0;
5653 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
5654 int cpMax = static_cast<int>(tr->chrg.cpMax);
5655 if (cpMax == -1)
5656 cpMax = pdoc->Length();
5657 PLATFORM_ASSERT(cpMax <= pdoc->Length());
5658 int len = static_cast<int>(cpMax - tr->chrg.cpMin); // No -1 as cpMin and cpMax are referring to inter character positions
5659 pdoc->GetCharRange(tr->lpstrText, static_cast<int>(tr->chrg.cpMin), len);
5660 // Spec says copied text is terminated with a NUL
5661 tr->lpstrText[len] = '\0';
5662 return len; // Not including NUL
5665 case SCI_HIDESELECTION:
5666 view.hideSelection = wParam != 0;
5667 Redraw();
5668 break;
5670 case SCI_FORMATRANGE:
5671 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
5673 case SCI_GETMARGINLEFT:
5674 return vs.leftMarginWidth;
5676 case SCI_GETMARGINRIGHT:
5677 return vs.rightMarginWidth;
5679 case SCI_SETMARGINLEFT:
5680 lastXChosen += static_cast<int>(lParam) - vs.leftMarginWidth;
5681 vs.leftMarginWidth = static_cast<int>(lParam);
5682 InvalidateStyleRedraw();
5683 break;
5685 case SCI_SETMARGINRIGHT:
5686 vs.rightMarginWidth = static_cast<int>(lParam);
5687 InvalidateStyleRedraw();
5688 break;
5690 // Control specific mesages
5692 case SCI_ADDTEXT: {
5693 if (lParam == 0)
5694 return 0;
5695 const int lengthInserted = pdoc->InsertString(
5696 CurrentPosition(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5697 SetEmptySelection(sel.MainCaret() + lengthInserted);
5698 return 0;
5701 case SCI_ADDSTYLEDTEXT:
5702 if (lParam)
5703 AddStyledText(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5704 return 0;
5706 case SCI_INSERTTEXT: {
5707 if (lParam == 0)
5708 return 0;
5709 int insertPos = static_cast<int>(wParam);
5710 if (static_cast<int>(wParam) == -1)
5711 insertPos = CurrentPosition();
5712 int newCurrent = CurrentPosition();
5713 char *sz = CharPtrFromSPtr(lParam);
5714 const int lengthInserted = pdoc->InsertString(insertPos, sz, istrlen(sz));
5715 if (newCurrent > insertPos)
5716 newCurrent += lengthInserted;
5717 SetEmptySelection(newCurrent);
5718 return 0;
5721 case SCI_CHANGEINSERTION:
5722 PLATFORM_ASSERT(lParam);
5723 pdoc->ChangeInsertion(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5724 return 0;
5726 case SCI_APPENDTEXT:
5727 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5728 return 0;
5730 case SCI_CLEARALL:
5731 ClearAll();
5732 return 0;
5734 case SCI_DELETERANGE:
5735 pdoc->DeleteChars(static_cast<int>(wParam), static_cast<int>(lParam));
5736 return 0;
5738 case SCI_CLEARDOCUMENTSTYLE:
5739 ClearDocumentStyle();
5740 return 0;
5742 case SCI_SETUNDOCOLLECTION:
5743 pdoc->SetUndoCollection(wParam != 0);
5744 return 0;
5746 case SCI_GETUNDOCOLLECTION:
5747 return pdoc->IsCollectingUndo();
5749 case SCI_BEGINUNDOACTION:
5750 pdoc->BeginUndoAction();
5751 return 0;
5753 case SCI_ENDUNDOACTION:
5754 pdoc->EndUndoAction();
5755 return 0;
5757 case SCI_GETCARETPERIOD:
5758 return caret.period;
5760 case SCI_SETCARETPERIOD:
5761 CaretSetPeriod(static_cast<int>(wParam));
5762 break;
5764 case SCI_GETWORDCHARS:
5765 return pdoc->GetCharsOfClass(CharClassify::ccWord, reinterpret_cast<unsigned char *>(lParam));
5767 case SCI_SETWORDCHARS: {
5768 pdoc->SetDefaultCharClasses(false);
5769 if (lParam == 0)
5770 return 0;
5771 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
5773 break;
5775 case SCI_GETWHITESPACECHARS:
5776 return pdoc->GetCharsOfClass(CharClassify::ccSpace, reinterpret_cast<unsigned char *>(lParam));
5778 case SCI_SETWHITESPACECHARS: {
5779 if (lParam == 0)
5780 return 0;
5781 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
5783 break;
5785 case SCI_GETPUNCTUATIONCHARS:
5786 return pdoc->GetCharsOfClass(CharClassify::ccPunctuation, reinterpret_cast<unsigned char *>(lParam));
5788 case SCI_SETPUNCTUATIONCHARS: {
5789 if (lParam == 0)
5790 return 0;
5791 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccPunctuation);
5793 break;
5795 case SCI_SETCHARSDEFAULT:
5796 pdoc->SetDefaultCharClasses(true);
5797 break;
5799 case SCI_GETLENGTH:
5800 return pdoc->Length();
5802 case SCI_ALLOCATE:
5803 pdoc->Allocate(static_cast<int>(wParam));
5804 break;
5806 case SCI_GETCHARAT:
5807 return pdoc->CharAt(static_cast<int>(wParam));
5809 case SCI_SETCURRENTPOS:
5810 if (sel.IsRectangular()) {
5811 sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
5812 SetRectangularRange();
5813 Redraw();
5814 } else {
5815 SetSelection(static_cast<int>(wParam), sel.MainAnchor());
5817 break;
5819 case SCI_GETCURRENTPOS:
5820 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
5822 case SCI_SETANCHOR:
5823 if (sel.IsRectangular()) {
5824 sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
5825 SetRectangularRange();
5826 Redraw();
5827 } else {
5828 SetSelection(sel.MainCaret(), static_cast<int>(wParam));
5830 break;
5832 case SCI_GETANCHOR:
5833 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
5835 case SCI_SETSELECTIONSTART:
5836 SetSelection(Platform::Maximum(sel.MainCaret(), static_cast<int>(wParam)), static_cast<int>(wParam));
5837 break;
5839 case SCI_GETSELECTIONSTART:
5840 return sel.LimitsForRectangularElseMain().start.Position();
5842 case SCI_SETSELECTIONEND:
5843 SetSelection(static_cast<int>(wParam), Platform::Minimum(sel.MainAnchor(), static_cast<int>(wParam)));
5844 break;
5846 case SCI_GETSELECTIONEND:
5847 return sel.LimitsForRectangularElseMain().end.Position();
5849 case SCI_SETEMPTYSELECTION:
5850 SetEmptySelection(static_cast<int>(wParam));
5851 break;
5853 case SCI_SETPRINTMAGNIFICATION:
5854 view.printParameters.magnification = static_cast<int>(wParam);
5855 break;
5857 case SCI_GETPRINTMAGNIFICATION:
5858 return view.printParameters.magnification;
5860 case SCI_SETPRINTCOLOURMODE:
5861 view.printParameters.colourMode = static_cast<int>(wParam);
5862 break;
5864 case SCI_GETPRINTCOLOURMODE:
5865 return view.printParameters.colourMode;
5867 case SCI_SETPRINTWRAPMODE:
5868 view.printParameters.wrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
5869 break;
5871 case SCI_GETPRINTWRAPMODE:
5872 return view.printParameters.wrapState;
5874 case SCI_GETSTYLEAT:
5875 if (static_cast<int>(wParam) >= pdoc->Length())
5876 return 0;
5877 else
5878 return pdoc->StyleAt(static_cast<int>(wParam));
5880 case SCI_REDO:
5881 Redo();
5882 break;
5884 case SCI_SELECTALL:
5885 SelectAll();
5886 break;
5888 case SCI_SETSAVEPOINT:
5889 pdoc->SetSavePoint();
5890 break;
5892 case SCI_GETSTYLEDTEXT: {
5893 if (lParam == 0)
5894 return 0;
5895 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
5896 int iPlace = 0;
5897 for (long iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
5898 tr->lpstrText[iPlace++] = pdoc->CharAt(static_cast<int>(iChar));
5899 tr->lpstrText[iPlace++] = pdoc->StyleAt(static_cast<int>(iChar));
5901 tr->lpstrText[iPlace] = '\0';
5902 tr->lpstrText[iPlace + 1] = '\0';
5903 return iPlace;
5906 case SCI_CANREDO:
5907 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
5909 case SCI_MARKERLINEFROMHANDLE:
5910 return pdoc->LineFromHandle(static_cast<int>(wParam));
5912 case SCI_MARKERDELETEHANDLE:
5913 pdoc->DeleteMarkFromHandle(static_cast<int>(wParam));
5914 break;
5916 case SCI_GETVIEWWS:
5917 return vs.viewWhitespace;
5919 case SCI_SETVIEWWS:
5920 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
5921 Redraw();
5922 break;
5924 case SCI_GETWHITESPACESIZE:
5925 return vs.whitespaceSize;
5927 case SCI_SETWHITESPACESIZE:
5928 vs.whitespaceSize = static_cast<int>(wParam);
5929 Redraw();
5930 break;
5932 case SCI_POSITIONFROMPOINT:
5933 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
5934 false, false);
5936 case SCI_POSITIONFROMPOINTCLOSE:
5937 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
5938 true, false);
5940 case SCI_CHARPOSITIONFROMPOINT:
5941 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
5942 false, true);
5944 case SCI_CHARPOSITIONFROMPOINTCLOSE:
5945 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
5946 true, true);
5948 case SCI_GOTOLINE:
5949 GoToLine(static_cast<int>(wParam));
5950 break;
5952 case SCI_GOTOPOS:
5953 SetEmptySelection(static_cast<int>(wParam));
5954 EnsureCaretVisible();
5955 break;
5957 case SCI_GETCURLINE: {
5958 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
5959 int lineStart = pdoc->LineStart(lineCurrentPos);
5960 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
5961 if (lParam == 0) {
5962 return 1 + lineEnd - lineStart;
5964 PLATFORM_ASSERT(wParam > 0);
5965 char *ptr = CharPtrFromSPtr(lParam);
5966 unsigned int iPlace = 0;
5967 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
5968 ptr[iPlace++] = pdoc->CharAt(iChar);
5970 ptr[iPlace] = '\0';
5971 return sel.MainCaret() - lineStart;
5974 case SCI_GETENDSTYLED:
5975 return pdoc->GetEndStyled();
5977 case SCI_GETEOLMODE:
5978 return pdoc->eolMode;
5980 case SCI_SETEOLMODE:
5981 pdoc->eolMode = static_cast<int>(wParam);
5982 break;
5984 case SCI_SETLINEENDTYPESALLOWED:
5985 if (pdoc->SetLineEndTypesAllowed(static_cast<int>(wParam))) {
5986 cs.Clear();
5987 cs.InsertLines(0, pdoc->LinesTotal() - 1);
5988 SetAnnotationHeights(0, pdoc->LinesTotal());
5989 InvalidateStyleRedraw();
5991 break;
5993 case SCI_GETLINEENDTYPESALLOWED:
5994 return pdoc->GetLineEndTypesAllowed();
5996 case SCI_GETLINEENDTYPESACTIVE:
5997 return pdoc->GetLineEndTypesActive();
5999 case SCI_STARTSTYLING:
6000 pdoc->StartStyling(static_cast<int>(wParam), static_cast<char>(lParam));
6001 break;
6003 case SCI_SETSTYLING:
6004 pdoc->SetStyleFor(static_cast<int>(wParam), static_cast<char>(lParam));
6005 break;
6007 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
6008 if (lParam == 0)
6009 return 0;
6010 pdoc->SetStyles(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
6011 break;
6013 case SCI_SETBUFFEREDDRAW:
6014 view.bufferedDraw = wParam != 0;
6015 break;
6017 case SCI_GETBUFFEREDDRAW:
6018 return view.bufferedDraw;
6020 case SCI_GETTWOPHASEDRAW:
6021 return view.phasesDraw == EditView::phasesTwo;
6023 case SCI_SETTWOPHASEDRAW:
6024 if (view.SetTwoPhaseDraw(wParam != 0))
6025 InvalidateStyleRedraw();
6026 break;
6028 case SCI_GETPHASESDRAW:
6029 return view.phasesDraw;
6031 case SCI_SETPHASESDRAW:
6032 if (view.SetPhasesDraw(static_cast<int>(wParam)))
6033 InvalidateStyleRedraw();
6034 break;
6036 case SCI_SETFONTQUALITY:
6037 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
6038 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
6039 InvalidateStyleRedraw();
6040 break;
6042 case SCI_GETFONTQUALITY:
6043 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
6045 case SCI_SETTABWIDTH:
6046 if (wParam > 0) {
6047 pdoc->tabInChars = static_cast<int>(wParam);
6048 if (pdoc->indentInChars == 0)
6049 pdoc->actualIndentInChars = pdoc->tabInChars;
6051 InvalidateStyleRedraw();
6052 break;
6054 case SCI_GETTABWIDTH:
6055 return pdoc->tabInChars;
6057 case SCI_CLEARTABSTOPS:
6058 if (view.ClearTabstops(static_cast<int>(wParam))) {
6059 DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, 0, static_cast<int>(wParam));
6060 NotifyModified(pdoc, mh, NULL);
6062 break;
6064 case SCI_ADDTABSTOP:
6065 if (view.AddTabstop(static_cast<int>(wParam), static_cast<int>(lParam))) {
6066 DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, 0, static_cast<int>(wParam));
6067 NotifyModified(pdoc, mh, NULL);
6069 break;
6071 case SCI_GETNEXTTABSTOP:
6072 return view.GetNextTabstop(static_cast<int>(wParam), static_cast<int>(lParam));
6074 case SCI_SETINDENT:
6075 pdoc->indentInChars = static_cast<int>(wParam);
6076 if (pdoc->indentInChars != 0)
6077 pdoc->actualIndentInChars = pdoc->indentInChars;
6078 else
6079 pdoc->actualIndentInChars = pdoc->tabInChars;
6080 InvalidateStyleRedraw();
6081 break;
6083 case SCI_GETINDENT:
6084 return pdoc->indentInChars;
6086 case SCI_SETUSETABS:
6087 pdoc->useTabs = wParam != 0;
6088 InvalidateStyleRedraw();
6089 break;
6091 case SCI_GETUSETABS:
6092 return pdoc->useTabs;
6094 case SCI_SETLINEINDENTATION:
6095 pdoc->SetLineIndentation(static_cast<int>(wParam), static_cast<int>(lParam));
6096 break;
6098 case SCI_GETLINEINDENTATION:
6099 return pdoc->GetLineIndentation(static_cast<int>(wParam));
6101 case SCI_GETLINEINDENTPOSITION:
6102 return pdoc->GetLineIndentPosition(static_cast<int>(wParam));
6104 case SCI_SETTABINDENTS:
6105 pdoc->tabIndents = wParam != 0;
6106 break;
6108 case SCI_GETTABINDENTS:
6109 return pdoc->tabIndents;
6111 case SCI_SETBACKSPACEUNINDENTS:
6112 pdoc->backspaceUnindents = wParam != 0;
6113 break;
6115 case SCI_GETBACKSPACEUNINDENTS:
6116 return pdoc->backspaceUnindents;
6118 case SCI_SETMOUSEDWELLTIME:
6119 dwellDelay = static_cast<int>(wParam);
6120 ticksToDwell = dwellDelay;
6121 break;
6123 case SCI_GETMOUSEDWELLTIME:
6124 return dwellDelay;
6126 case SCI_WORDSTARTPOSITION:
6127 return pdoc->ExtendWordSelect(static_cast<int>(wParam), -1, lParam != 0);
6129 case SCI_WORDENDPOSITION:
6130 return pdoc->ExtendWordSelect(static_cast<int>(wParam), 1, lParam != 0);
6132 case SCI_SETWRAPMODE:
6133 if (vs.SetWrapState(static_cast<int>(wParam))) {
6134 xOffset = 0;
6135 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
6136 InvalidateStyleRedraw();
6137 ReconfigureScrollBars();
6139 break;
6141 case SCI_GETWRAPMODE:
6142 return vs.wrapState;
6144 case SCI_SETWRAPVISUALFLAGS:
6145 if (vs.SetWrapVisualFlags(static_cast<int>(wParam))) {
6146 InvalidateStyleRedraw();
6147 ReconfigureScrollBars();
6149 break;
6151 case SCI_GETWRAPVISUALFLAGS:
6152 return vs.wrapVisualFlags;
6154 case SCI_SETWRAPVISUALFLAGSLOCATION:
6155 if (vs.SetWrapVisualFlagsLocation(static_cast<int>(wParam))) {
6156 InvalidateStyleRedraw();
6158 break;
6160 case SCI_GETWRAPVISUALFLAGSLOCATION:
6161 return vs.wrapVisualFlagsLocation;
6163 case SCI_SETWRAPSTARTINDENT:
6164 if (vs.SetWrapVisualStartIndent(static_cast<int>(wParam))) {
6165 InvalidateStyleRedraw();
6166 ReconfigureScrollBars();
6168 break;
6170 case SCI_GETWRAPSTARTINDENT:
6171 return vs.wrapVisualStartIndent;
6173 case SCI_SETWRAPINDENTMODE:
6174 if (vs.SetWrapIndentMode(static_cast<int>(wParam))) {
6175 InvalidateStyleRedraw();
6176 ReconfigureScrollBars();
6178 break;
6180 case SCI_GETWRAPINDENTMODE:
6181 return vs.wrapIndentMode;
6183 case SCI_SETLAYOUTCACHE:
6184 view.llc.SetLevel(static_cast<int>(wParam));
6185 break;
6187 case SCI_GETLAYOUTCACHE:
6188 return view.llc.GetLevel();
6190 case SCI_SETPOSITIONCACHE:
6191 view.posCache.SetSize(wParam);
6192 break;
6194 case SCI_GETPOSITIONCACHE:
6195 return view.posCache.GetSize();
6197 case SCI_SETSCROLLWIDTH:
6198 PLATFORM_ASSERT(wParam > 0);
6199 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
6200 view.lineWidthMaxSeen = 0;
6201 scrollWidth = static_cast<int>(wParam);
6202 SetScrollBars();
6204 break;
6206 case SCI_GETSCROLLWIDTH:
6207 return scrollWidth;
6209 case SCI_SETSCROLLWIDTHTRACKING:
6210 trackLineWidth = wParam != 0;
6211 break;
6213 case SCI_GETSCROLLWIDTHTRACKING:
6214 return trackLineWidth;
6216 case SCI_LINESJOIN:
6217 LinesJoin();
6218 break;
6220 case SCI_LINESSPLIT:
6221 LinesSplit(static_cast<int>(wParam));
6222 break;
6224 case SCI_TEXTWIDTH:
6225 PLATFORM_ASSERT(wParam < vs.styles.size());
6226 PLATFORM_ASSERT(lParam);
6227 return TextWidth(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
6229 case SCI_TEXTHEIGHT:
6230 return vs.lineHeight;
6232 case SCI_SETENDATLASTLINE:
6233 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
6234 if (endAtLastLine != (wParam != 0)) {
6235 endAtLastLine = wParam != 0;
6236 SetScrollBars();
6238 break;
6240 case SCI_GETENDATLASTLINE:
6241 return endAtLastLine;
6243 case SCI_SETCARETSTICKY:
6244 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
6245 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
6246 caretSticky = static_cast<int>(wParam);
6248 break;
6250 case SCI_GETCARETSTICKY:
6251 return caretSticky;
6253 case SCI_TOGGLECARETSTICKY:
6254 caretSticky = !caretSticky;
6255 break;
6257 case SCI_GETCOLUMN:
6258 return pdoc->GetColumn(static_cast<int>(wParam));
6260 case SCI_FINDCOLUMN:
6261 return pdoc->FindColumn(static_cast<int>(wParam), static_cast<int>(lParam));
6263 case SCI_SETHSCROLLBAR :
6264 if (horizontalScrollBarVisible != (wParam != 0)) {
6265 horizontalScrollBarVisible = wParam != 0;
6266 SetScrollBars();
6267 ReconfigureScrollBars();
6269 break;
6271 case SCI_GETHSCROLLBAR:
6272 return horizontalScrollBarVisible;
6274 case SCI_SETVSCROLLBAR:
6275 if (verticalScrollBarVisible != (wParam != 0)) {
6276 verticalScrollBarVisible = wParam != 0;
6277 SetScrollBars();
6278 ReconfigureScrollBars();
6279 if (verticalScrollBarVisible)
6280 SetVerticalScrollPos();
6282 break;
6284 case SCI_GETVSCROLLBAR:
6285 return verticalScrollBarVisible;
6287 case SCI_SETINDENTATIONGUIDES:
6288 vs.viewIndentationGuides = IndentView(wParam);
6289 Redraw();
6290 break;
6292 case SCI_GETINDENTATIONGUIDES:
6293 return vs.viewIndentationGuides;
6295 case SCI_SETHIGHLIGHTGUIDE:
6296 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
6297 highlightGuideColumn = static_cast<int>(wParam);
6298 Redraw();
6300 break;
6302 case SCI_GETHIGHLIGHTGUIDE:
6303 return highlightGuideColumn;
6305 case SCI_GETLINEENDPOSITION:
6306 return pdoc->LineEnd(static_cast<int>(wParam));
6308 case SCI_SETCODEPAGE:
6309 if (ValidCodePage(static_cast<int>(wParam))) {
6310 if (pdoc->SetDBCSCodePage(static_cast<int>(wParam))) {
6311 cs.Clear();
6312 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6313 SetAnnotationHeights(0, pdoc->LinesTotal());
6314 InvalidateStyleRedraw();
6315 SetRepresentations();
6318 break;
6320 case SCI_GETCODEPAGE:
6321 return pdoc->dbcsCodePage;
6323 case SCI_SETIMEINTERACTION:
6324 imeInteraction = static_cast<EditModel::IMEInteraction>(wParam);
6325 break;
6327 case SCI_GETIMEINTERACTION:
6328 return imeInteraction;
6330 #ifdef INCLUDE_DEPRECATED_FEATURES
6331 case SCI_SETUSEPALETTE:
6332 InvalidateStyleRedraw();
6333 break;
6335 case SCI_GETUSEPALETTE:
6336 return 0;
6337 #endif
6339 // Marker definition and setting
6340 case SCI_MARKERDEFINE:
6341 if (wParam <= MARKER_MAX) {
6342 vs.markers[wParam].markType = static_cast<int>(lParam);
6343 vs.CalcLargestMarkerHeight();
6345 InvalidateStyleData();
6346 RedrawSelMargin();
6347 break;
6349 case SCI_MARKERSYMBOLDEFINED:
6350 if (wParam <= MARKER_MAX)
6351 return vs.markers[wParam].markType;
6352 else
6353 return 0;
6355 case SCI_MARKERSETFORE:
6356 if (wParam <= MARKER_MAX)
6357 vs.markers[wParam].fore = ColourDesired(static_cast<long>(lParam));
6358 InvalidateStyleData();
6359 RedrawSelMargin();
6360 break;
6361 case SCI_MARKERSETBACKSELECTED:
6362 if (wParam <= MARKER_MAX)
6363 vs.markers[wParam].backSelected = ColourDesired(static_cast<long>(lParam));
6364 InvalidateStyleData();
6365 RedrawSelMargin();
6366 break;
6367 case SCI_MARKERENABLEHIGHLIGHT:
6368 marginView.highlightDelimiter.isEnabled = wParam == 1;
6369 RedrawSelMargin();
6370 break;
6371 case SCI_MARKERSETBACK:
6372 if (wParam <= MARKER_MAX)
6373 vs.markers[wParam].back = ColourDesired(static_cast<long>(lParam));
6374 InvalidateStyleData();
6375 RedrawSelMargin();
6376 break;
6377 case SCI_MARKERSETALPHA:
6378 if (wParam <= MARKER_MAX)
6379 vs.markers[wParam].alpha = static_cast<int>(lParam);
6380 InvalidateStyleRedraw();
6381 break;
6382 case SCI_MARKERADD: {
6383 int markerID = pdoc->AddMark(static_cast<int>(wParam), static_cast<int>(lParam));
6384 return markerID;
6386 case SCI_MARKERADDSET:
6387 if (lParam != 0)
6388 pdoc->AddMarkSet(static_cast<int>(wParam), static_cast<int>(lParam));
6389 break;
6391 case SCI_MARKERDELETE:
6392 pdoc->DeleteMark(static_cast<int>(wParam), static_cast<int>(lParam));
6393 break;
6395 case SCI_MARKERDELETEALL:
6396 pdoc->DeleteAllMarks(static_cast<int>(wParam));
6397 break;
6399 case SCI_MARKERGET:
6400 return pdoc->GetMark(static_cast<int>(wParam));
6402 case SCI_MARKERNEXT:
6403 return pdoc->MarkerNext(static_cast<int>(wParam), static_cast<int>(lParam));
6405 case SCI_MARKERPREVIOUS: {
6406 for (int iLine = static_cast<int>(wParam); iLine >= 0; iLine--) {
6407 if ((pdoc->GetMark(iLine) & lParam) != 0)
6408 return iLine;
6411 return -1;
6413 case SCI_MARKERDEFINEPIXMAP:
6414 if (wParam <= MARKER_MAX) {
6415 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
6416 vs.CalcLargestMarkerHeight();
6418 InvalidateStyleData();
6419 RedrawSelMargin();
6420 break;
6422 case SCI_RGBAIMAGESETWIDTH:
6423 sizeRGBAImage.x = static_cast<XYPOSITION>(wParam);
6424 break;
6426 case SCI_RGBAIMAGESETHEIGHT:
6427 sizeRGBAImage.y = static_cast<XYPOSITION>(wParam);
6428 break;
6430 case SCI_RGBAIMAGESETSCALE:
6431 scaleRGBAImage = static_cast<float>(wParam);
6432 break;
6434 case SCI_MARKERDEFINERGBAIMAGE:
6435 if (wParam <= MARKER_MAX) {
6436 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0f, reinterpret_cast<unsigned char *>(lParam));
6437 vs.CalcLargestMarkerHeight();
6439 InvalidateStyleData();
6440 RedrawSelMargin();
6441 break;
6443 case SCI_SETMARGINTYPEN:
6444 if (ValidMargin(wParam)) {
6445 vs.ms[wParam].style = static_cast<int>(lParam);
6446 InvalidateStyleRedraw();
6448 break;
6450 case SCI_GETMARGINTYPEN:
6451 if (ValidMargin(wParam))
6452 return vs.ms[wParam].style;
6453 else
6454 return 0;
6456 case SCI_SETMARGINWIDTHN:
6457 if (ValidMargin(wParam)) {
6458 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
6459 if (vs.ms[wParam].width != lParam) {
6460 lastXChosen += static_cast<int>(lParam) - vs.ms[wParam].width;
6461 vs.ms[wParam].width = static_cast<int>(lParam);
6462 InvalidateStyleRedraw();
6465 break;
6467 case SCI_GETMARGINWIDTHN:
6468 if (ValidMargin(wParam))
6469 return vs.ms[wParam].width;
6470 else
6471 return 0;
6473 case SCI_SETMARGINMASKN:
6474 if (ValidMargin(wParam)) {
6475 vs.ms[wParam].mask = static_cast<int>(lParam);
6476 InvalidateStyleRedraw();
6478 break;
6480 case SCI_GETMARGINMASKN:
6481 if (ValidMargin(wParam))
6482 return vs.ms[wParam].mask;
6483 else
6484 return 0;
6486 case SCI_SETMARGINSENSITIVEN:
6487 if (ValidMargin(wParam)) {
6488 vs.ms[wParam].sensitive = lParam != 0;
6489 InvalidateStyleRedraw();
6491 break;
6493 case SCI_GETMARGINSENSITIVEN:
6494 if (ValidMargin(wParam))
6495 return vs.ms[wParam].sensitive ? 1 : 0;
6496 else
6497 return 0;
6499 case SCI_SETMARGINCURSORN:
6500 if (ValidMargin(wParam))
6501 vs.ms[wParam].cursor = static_cast<int>(lParam);
6502 break;
6504 case SCI_GETMARGINCURSORN:
6505 if (ValidMargin(wParam))
6506 return vs.ms[wParam].cursor;
6507 else
6508 return 0;
6510 case SCI_STYLECLEARALL:
6511 vs.ClearStyles();
6512 InvalidateStyleRedraw();
6513 break;
6515 case SCI_STYLESETFORE:
6516 case SCI_STYLESETBACK:
6517 case SCI_STYLESETBOLD:
6518 case SCI_STYLESETWEIGHT:
6519 case SCI_STYLESETITALIC:
6520 case SCI_STYLESETEOLFILLED:
6521 case SCI_STYLESETSIZE:
6522 case SCI_STYLESETSIZEFRACTIONAL:
6523 case SCI_STYLESETFONT:
6524 case SCI_STYLESETUNDERLINE:
6525 case SCI_STYLESETCASE:
6526 case SCI_STYLESETCHARACTERSET:
6527 case SCI_STYLESETVISIBLE:
6528 case SCI_STYLESETCHANGEABLE:
6529 case SCI_STYLESETHOTSPOT:
6530 StyleSetMessage(iMessage, wParam, lParam);
6531 break;
6533 case SCI_STYLEGETFORE:
6534 case SCI_STYLEGETBACK:
6535 case SCI_STYLEGETBOLD:
6536 case SCI_STYLEGETWEIGHT:
6537 case SCI_STYLEGETITALIC:
6538 case SCI_STYLEGETEOLFILLED:
6539 case SCI_STYLEGETSIZE:
6540 case SCI_STYLEGETSIZEFRACTIONAL:
6541 case SCI_STYLEGETFONT:
6542 case SCI_STYLEGETUNDERLINE:
6543 case SCI_STYLEGETCASE:
6544 case SCI_STYLEGETCHARACTERSET:
6545 case SCI_STYLEGETVISIBLE:
6546 case SCI_STYLEGETCHANGEABLE:
6547 case SCI_STYLEGETHOTSPOT:
6548 return StyleGetMessage(iMessage, wParam, lParam);
6550 case SCI_STYLERESETDEFAULT:
6551 vs.ResetDefaultStyle();
6552 InvalidateStyleRedraw();
6553 break;
6554 case SCI_SETSTYLEBITS:
6555 vs.EnsureStyle(0xff);
6556 break;
6558 case SCI_GETSTYLEBITS:
6559 return 8;
6561 case SCI_SETLINESTATE:
6562 return pdoc->SetLineState(static_cast<int>(wParam), static_cast<int>(lParam));
6564 case SCI_GETLINESTATE:
6565 return pdoc->GetLineState(static_cast<int>(wParam));
6567 case SCI_GETMAXLINESTATE:
6568 return pdoc->GetMaxLineState();
6570 case SCI_GETCARETLINEVISIBLE:
6571 return vs.showCaretLineBackground;
6572 case SCI_SETCARETLINEVISIBLE:
6573 vs.showCaretLineBackground = wParam != 0;
6574 InvalidateStyleRedraw();
6575 break;
6576 case SCI_GETCARETLINEVISIBLEALWAYS:
6577 return vs.alwaysShowCaretLineBackground;
6578 case SCI_SETCARETLINEVISIBLEALWAYS:
6579 vs.alwaysShowCaretLineBackground = wParam != 0;
6580 InvalidateStyleRedraw();
6581 break;
6583 case SCI_GETCARETLINEBACK:
6584 return vs.caretLineBackground.AsLong();
6585 case SCI_SETCARETLINEBACK:
6586 vs.caretLineBackground = static_cast<int>(wParam);
6587 InvalidateStyleRedraw();
6588 break;
6589 case SCI_GETCARETLINEBACKALPHA:
6590 return vs.caretLineAlpha;
6591 case SCI_SETCARETLINEBACKALPHA:
6592 vs.caretLineAlpha = static_cast<int>(wParam);
6593 InvalidateStyleRedraw();
6594 break;
6596 // Folding messages
6598 case SCI_VISIBLEFROMDOCLINE:
6599 return cs.DisplayFromDoc(static_cast<int>(wParam));
6601 case SCI_DOCLINEFROMVISIBLE:
6602 return cs.DocFromDisplay(static_cast<int>(wParam));
6604 case SCI_WRAPCOUNT:
6605 return WrapCount(static_cast<int>(wParam));
6607 case SCI_SETFOLDLEVEL: {
6608 int prev = pdoc->SetLevel(static_cast<int>(wParam), static_cast<int>(lParam));
6609 if (prev != static_cast<int>(lParam))
6610 RedrawSelMargin();
6611 return prev;
6614 case SCI_GETFOLDLEVEL:
6615 return pdoc->GetLevel(static_cast<int>(wParam));
6617 case SCI_GETLASTCHILD:
6618 return pdoc->GetLastChild(static_cast<int>(wParam), static_cast<int>(lParam));
6620 case SCI_GETFOLDPARENT:
6621 return pdoc->GetFoldParent(static_cast<int>(wParam));
6623 case SCI_SHOWLINES:
6624 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), true);
6625 SetScrollBars();
6626 Redraw();
6627 break;
6629 case SCI_HIDELINES:
6630 if (wParam > 0)
6631 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), false);
6632 SetScrollBars();
6633 Redraw();
6634 break;
6636 case SCI_GETLINEVISIBLE:
6637 return cs.GetVisible(static_cast<int>(wParam));
6639 case SCI_GETALLLINESVISIBLE:
6640 return cs.HiddenLines() ? 0 : 1;
6642 case SCI_SETFOLDEXPANDED:
6643 SetFoldExpanded(static_cast<int>(wParam), lParam != 0);
6644 break;
6646 case SCI_GETFOLDEXPANDED:
6647 return cs.GetExpanded(static_cast<int>(wParam));
6649 case SCI_SETAUTOMATICFOLD:
6650 foldAutomatic = static_cast<int>(wParam);
6651 break;
6653 case SCI_GETAUTOMATICFOLD:
6654 return foldAutomatic;
6656 case SCI_SETFOLDFLAGS:
6657 foldFlags = static_cast<int>(wParam);
6658 Redraw();
6659 break;
6661 case SCI_TOGGLEFOLD:
6662 FoldLine(static_cast<int>(wParam), SC_FOLDACTION_TOGGLE);
6663 break;
6665 case SCI_FOLDLINE:
6666 FoldLine(static_cast<int>(wParam), static_cast<int>(lParam));
6667 break;
6669 case SCI_FOLDCHILDREN:
6670 FoldExpand(static_cast<int>(wParam), static_cast<int>(lParam), pdoc->GetLevel(static_cast<int>(wParam)));
6671 break;
6673 case SCI_FOLDALL:
6674 FoldAll(static_cast<int>(wParam));
6675 break;
6677 case SCI_EXPANDCHILDREN:
6678 FoldExpand(static_cast<int>(wParam), SC_FOLDACTION_EXPAND, static_cast<int>(lParam));
6679 break;
6681 case SCI_CONTRACTEDFOLDNEXT:
6682 return ContractedFoldNext(static_cast<int>(wParam));
6684 case SCI_ENSUREVISIBLE:
6685 EnsureLineVisible(static_cast<int>(wParam), false);
6686 break;
6688 case SCI_ENSUREVISIBLEENFORCEPOLICY:
6689 EnsureLineVisible(static_cast<int>(wParam), true);
6690 break;
6692 case SCI_SCROLLRANGE:
6693 ScrollRange(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
6694 break;
6696 case SCI_SEARCHANCHOR:
6697 SearchAnchor();
6698 break;
6700 case SCI_SEARCHNEXT:
6701 case SCI_SEARCHPREV:
6702 return SearchText(iMessage, wParam, lParam);
6704 case SCI_SETXCARETPOLICY:
6705 caretXPolicy = static_cast<int>(wParam);
6706 caretXSlop = static_cast<int>(lParam);
6707 break;
6709 case SCI_SETYCARETPOLICY:
6710 caretYPolicy = static_cast<int>(wParam);
6711 caretYSlop = static_cast<int>(lParam);
6712 break;
6714 case SCI_SETVISIBLEPOLICY:
6715 visiblePolicy = static_cast<int>(wParam);
6716 visibleSlop = static_cast<int>(lParam);
6717 break;
6719 case SCI_LINESONSCREEN:
6720 return LinesOnScreen();
6722 case SCI_SETSELFORE:
6723 vs.selColours.fore = ColourOptional(wParam, lParam);
6724 vs.selAdditionalForeground = ColourDesired(static_cast<long>(lParam));
6725 InvalidateStyleRedraw();
6726 break;
6728 case SCI_SETSELBACK:
6729 vs.selColours.back = ColourOptional(wParam, lParam);
6730 vs.selAdditionalBackground = ColourDesired(static_cast<long>(lParam));
6731 InvalidateStyleRedraw();
6732 break;
6734 case SCI_SETSELALPHA:
6735 vs.selAlpha = static_cast<int>(wParam);
6736 vs.selAdditionalAlpha = static_cast<int>(wParam);
6737 InvalidateStyleRedraw();
6738 break;
6740 case SCI_GETSELALPHA:
6741 return vs.selAlpha;
6743 case SCI_GETSELEOLFILLED:
6744 return vs.selEOLFilled;
6746 case SCI_SETSELEOLFILLED:
6747 vs.selEOLFilled = wParam != 0;
6748 InvalidateStyleRedraw();
6749 break;
6751 case SCI_SETWHITESPACEFORE:
6752 vs.whitespaceColours.fore = ColourOptional(wParam, lParam);
6753 InvalidateStyleRedraw();
6754 break;
6756 case SCI_SETWHITESPACEBACK:
6757 vs.whitespaceColours.back = ColourOptional(wParam, lParam);
6758 InvalidateStyleRedraw();
6759 break;
6761 case SCI_SETCARETFORE:
6762 vs.caretcolour = ColourDesired(static_cast<long>(wParam));
6763 InvalidateStyleRedraw();
6764 break;
6766 case SCI_GETCARETFORE:
6767 return vs.caretcolour.AsLong();
6769 case SCI_SETCARETSTYLE:
6770 if (wParam <= CARETSTYLE_BLOCK)
6771 vs.caretStyle = static_cast<int>(wParam);
6772 else
6773 /* Default to the line caret */
6774 vs.caretStyle = CARETSTYLE_LINE;
6775 InvalidateStyleRedraw();
6776 break;
6778 case SCI_GETCARETSTYLE:
6779 return vs.caretStyle;
6781 case SCI_SETCARETWIDTH:
6782 if (static_cast<int>(wParam) <= 0)
6783 vs.caretWidth = 0;
6784 else if (wParam >= 3)
6785 vs.caretWidth = 3;
6786 else
6787 vs.caretWidth = static_cast<int>(wParam);
6788 InvalidateStyleRedraw();
6789 break;
6791 case SCI_GETCARETWIDTH:
6792 return vs.caretWidth;
6794 case SCI_ASSIGNCMDKEY:
6795 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
6796 Platform::HighShortFromLong(static_cast<long>(wParam)), static_cast<unsigned int>(lParam));
6797 break;
6799 case SCI_CLEARCMDKEY:
6800 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
6801 Platform::HighShortFromLong(static_cast<long>(wParam)), SCI_NULL);
6802 break;
6804 case SCI_CLEARALLCMDKEYS:
6805 kmap.Clear();
6806 break;
6808 case SCI_INDICSETSTYLE:
6809 if (wParam <= INDIC_MAX) {
6810 vs.indicators[wParam].sacNormal.style = static_cast<int>(lParam);
6811 vs.indicators[wParam].sacHover.style = static_cast<int>(lParam);
6812 InvalidateStyleRedraw();
6814 break;
6816 case SCI_INDICGETSTYLE:
6817 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacNormal.style : 0;
6819 case SCI_INDICSETFORE:
6820 if (wParam <= INDIC_MAX) {
6821 vs.indicators[wParam].sacNormal.fore = ColourDesired(static_cast<long>(lParam));
6822 vs.indicators[wParam].sacHover.fore = ColourDesired(static_cast<long>(lParam));
6823 InvalidateStyleRedraw();
6825 break;
6827 case SCI_INDICGETFORE:
6828 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacNormal.fore.AsLong() : 0;
6830 case SCI_INDICSETHOVERSTYLE:
6831 if (wParam <= INDIC_MAX) {
6832 vs.indicators[wParam].sacHover.style = static_cast<int>(lParam);
6833 InvalidateStyleRedraw();
6835 break;
6837 case SCI_INDICGETHOVERSTYLE:
6838 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacHover.style : 0;
6840 case SCI_INDICSETHOVERFORE:
6841 if (wParam <= INDIC_MAX) {
6842 vs.indicators[wParam].sacHover.fore = ColourDesired(static_cast<long>(lParam));
6843 InvalidateStyleRedraw();
6845 break;
6847 case SCI_INDICGETHOVERFORE:
6848 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacHover.fore.AsLong() : 0;
6850 case SCI_INDICSETFLAGS:
6851 if (wParam <= INDIC_MAX) {
6852 vs.indicators[wParam].SetFlags(static_cast<int>(lParam));
6853 InvalidateStyleRedraw();
6855 break;
6857 case SCI_INDICGETFLAGS:
6858 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].Flags() : 0;
6860 case SCI_INDICSETUNDER:
6861 if (wParam <= INDIC_MAX) {
6862 vs.indicators[wParam].under = lParam != 0;
6863 InvalidateStyleRedraw();
6865 break;
6867 case SCI_INDICGETUNDER:
6868 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
6870 case SCI_INDICSETALPHA:
6871 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
6872 vs.indicators[wParam].fillAlpha = static_cast<int>(lParam);
6873 InvalidateStyleRedraw();
6875 break;
6877 case SCI_INDICGETALPHA:
6878 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
6880 case SCI_INDICSETOUTLINEALPHA:
6881 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
6882 vs.indicators[wParam].outlineAlpha = static_cast<int>(lParam);
6883 InvalidateStyleRedraw();
6885 break;
6887 case SCI_INDICGETOUTLINEALPHA:
6888 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
6890 case SCI_SETINDICATORCURRENT:
6891 pdoc->decorations.SetCurrentIndicator(static_cast<int>(wParam));
6892 break;
6893 case SCI_GETINDICATORCURRENT:
6894 return pdoc->decorations.GetCurrentIndicator();
6895 case SCI_SETINDICATORVALUE:
6896 pdoc->decorations.SetCurrentValue(static_cast<int>(wParam));
6897 break;
6898 case SCI_GETINDICATORVALUE:
6899 return pdoc->decorations.GetCurrentValue();
6901 case SCI_INDICATORFILLRANGE:
6902 pdoc->DecorationFillRange(static_cast<int>(wParam), pdoc->decorations.GetCurrentValue(), static_cast<int>(lParam));
6903 break;
6905 case SCI_INDICATORCLEARRANGE:
6906 pdoc->DecorationFillRange(static_cast<int>(wParam), 0, static_cast<int>(lParam));
6907 break;
6909 case SCI_INDICATORALLONFOR:
6910 return pdoc->decorations.AllOnFor(static_cast<int>(wParam));
6912 case SCI_INDICATORVALUEAT:
6913 return pdoc->decorations.ValueAt(static_cast<int>(wParam), static_cast<int>(lParam));
6915 case SCI_INDICATORSTART:
6916 return pdoc->decorations.Start(static_cast<int>(wParam), static_cast<int>(lParam));
6918 case SCI_INDICATOREND:
6919 return pdoc->decorations.End(static_cast<int>(wParam), static_cast<int>(lParam));
6921 case SCI_LINEDOWN:
6922 case SCI_LINEDOWNEXTEND:
6923 case SCI_PARADOWN:
6924 case SCI_PARADOWNEXTEND:
6925 case SCI_LINEUP:
6926 case SCI_LINEUPEXTEND:
6927 case SCI_PARAUP:
6928 case SCI_PARAUPEXTEND:
6929 case SCI_CHARLEFT:
6930 case SCI_CHARLEFTEXTEND:
6931 case SCI_CHARRIGHT:
6932 case SCI_CHARRIGHTEXTEND:
6933 case SCI_WORDLEFT:
6934 case SCI_WORDLEFTEXTEND:
6935 case SCI_WORDRIGHT:
6936 case SCI_WORDRIGHTEXTEND:
6937 case SCI_WORDLEFTEND:
6938 case SCI_WORDLEFTENDEXTEND:
6939 case SCI_WORDRIGHTEND:
6940 case SCI_WORDRIGHTENDEXTEND:
6941 case SCI_HOME:
6942 case SCI_HOMEEXTEND:
6943 case SCI_LINEEND:
6944 case SCI_LINEENDEXTEND:
6945 case SCI_HOMEWRAP:
6946 case SCI_HOMEWRAPEXTEND:
6947 case SCI_LINEENDWRAP:
6948 case SCI_LINEENDWRAPEXTEND:
6949 case SCI_DOCUMENTSTART:
6950 case SCI_DOCUMENTSTARTEXTEND:
6951 case SCI_DOCUMENTEND:
6952 case SCI_DOCUMENTENDEXTEND:
6953 case SCI_SCROLLTOSTART:
6954 case SCI_SCROLLTOEND:
6956 case SCI_STUTTEREDPAGEUP:
6957 case SCI_STUTTEREDPAGEUPEXTEND:
6958 case SCI_STUTTEREDPAGEDOWN:
6959 case SCI_STUTTEREDPAGEDOWNEXTEND:
6961 case SCI_PAGEUP:
6962 case SCI_PAGEUPEXTEND:
6963 case SCI_PAGEDOWN:
6964 case SCI_PAGEDOWNEXTEND:
6965 case SCI_EDITTOGGLEOVERTYPE:
6966 case SCI_CANCEL:
6967 case SCI_DELETEBACK:
6968 case SCI_TAB:
6969 case SCI_BACKTAB:
6970 case SCI_NEWLINE:
6971 case SCI_FORMFEED:
6972 case SCI_VCHOME:
6973 case SCI_VCHOMEEXTEND:
6974 case SCI_VCHOMEWRAP:
6975 case SCI_VCHOMEWRAPEXTEND:
6976 case SCI_VCHOMEDISPLAY:
6977 case SCI_VCHOMEDISPLAYEXTEND:
6978 case SCI_ZOOMIN:
6979 case SCI_ZOOMOUT:
6980 case SCI_DELWORDLEFT:
6981 case SCI_DELWORDRIGHT:
6982 case SCI_DELWORDRIGHTEND:
6983 case SCI_DELLINELEFT:
6984 case SCI_DELLINERIGHT:
6985 case SCI_LINECOPY:
6986 case SCI_LINECUT:
6987 case SCI_LINEDELETE:
6988 case SCI_LINETRANSPOSE:
6989 case SCI_LINEDUPLICATE:
6990 case SCI_LOWERCASE:
6991 case SCI_UPPERCASE:
6992 case SCI_LINESCROLLDOWN:
6993 case SCI_LINESCROLLUP:
6994 case SCI_WORDPARTLEFT:
6995 case SCI_WORDPARTLEFTEXTEND:
6996 case SCI_WORDPARTRIGHT:
6997 case SCI_WORDPARTRIGHTEXTEND:
6998 case SCI_DELETEBACKNOTLINE:
6999 case SCI_HOMEDISPLAY:
7000 case SCI_HOMEDISPLAYEXTEND:
7001 case SCI_LINEENDDISPLAY:
7002 case SCI_LINEENDDISPLAYEXTEND:
7003 case SCI_LINEDOWNRECTEXTEND:
7004 case SCI_LINEUPRECTEXTEND:
7005 case SCI_CHARLEFTRECTEXTEND:
7006 case SCI_CHARRIGHTRECTEXTEND:
7007 case SCI_HOMERECTEXTEND:
7008 case SCI_VCHOMERECTEXTEND:
7009 case SCI_LINEENDRECTEXTEND:
7010 case SCI_PAGEUPRECTEXTEND:
7011 case SCI_PAGEDOWNRECTEXTEND:
7012 case SCI_SELECTIONDUPLICATE:
7013 return KeyCommand(iMessage);
7015 case SCI_BRACEHIGHLIGHT:
7016 SetBraceHighlight(static_cast<int>(wParam), static_cast<int>(lParam), STYLE_BRACELIGHT);
7017 break;
7019 case SCI_BRACEHIGHLIGHTINDICATOR:
7020 if (lParam >= 0 && lParam <= INDIC_MAX) {
7021 vs.braceHighlightIndicatorSet = wParam != 0;
7022 vs.braceHighlightIndicator = static_cast<int>(lParam);
7024 break;
7026 case SCI_BRACEBADLIGHT:
7027 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
7028 break;
7030 case SCI_BRACEBADLIGHTINDICATOR:
7031 if (lParam >= 0 && lParam <= INDIC_MAX) {
7032 vs.braceBadLightIndicatorSet = wParam != 0;
7033 vs.braceBadLightIndicator = static_cast<int>(lParam);
7035 break;
7037 case SCI_BRACEMATCH:
7038 // wParam is position of char to find brace for,
7039 // lParam is maximum amount of text to restyle to find it
7040 return pdoc->BraceMatch(static_cast<int>(wParam), static_cast<int>(lParam));
7042 case SCI_GETVIEWEOL:
7043 return vs.viewEOL;
7045 case SCI_SETVIEWEOL:
7046 vs.viewEOL = wParam != 0;
7047 InvalidateStyleRedraw();
7048 break;
7050 case SCI_SETZOOM:
7051 vs.zoomLevel = static_cast<int>(wParam);
7052 InvalidateStyleRedraw();
7053 NotifyZoom();
7054 break;
7056 case SCI_GETZOOM:
7057 return vs.zoomLevel;
7059 case SCI_GETEDGECOLUMN:
7060 return vs.theEdge;
7062 case SCI_SETEDGECOLUMN:
7063 vs.theEdge = static_cast<int>(wParam);
7064 InvalidateStyleRedraw();
7065 break;
7067 case SCI_GETEDGEMODE:
7068 return vs.edgeState;
7070 case SCI_SETEDGEMODE:
7071 vs.edgeState = static_cast<int>(wParam);
7072 InvalidateStyleRedraw();
7073 break;
7075 case SCI_GETEDGECOLOUR:
7076 return vs.edgecolour.AsLong();
7078 case SCI_SETEDGECOLOUR:
7079 vs.edgecolour = ColourDesired(static_cast<long>(wParam));
7080 InvalidateStyleRedraw();
7081 break;
7083 case SCI_GETDOCPOINTER:
7084 return reinterpret_cast<sptr_t>(pdoc);
7086 case SCI_SETDOCPOINTER:
7087 CancelModes();
7088 SetDocPointer(reinterpret_cast<Document *>(lParam));
7089 return 0;
7091 case SCI_CREATEDOCUMENT: {
7092 Document *doc = new Document();
7093 doc->AddRef();
7094 return reinterpret_cast<sptr_t>(doc);
7097 case SCI_ADDREFDOCUMENT:
7098 (reinterpret_cast<Document *>(lParam))->AddRef();
7099 break;
7101 case SCI_RELEASEDOCUMENT:
7102 (reinterpret_cast<Document *>(lParam))->Release();
7103 break;
7105 case SCI_CREATELOADER: {
7106 Document *doc = new Document();
7107 doc->AddRef();
7108 doc->Allocate(static_cast<int>(wParam));
7109 doc->SetUndoCollection(false);
7110 return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
7113 case SCI_SETMODEVENTMASK:
7114 modEventMask = static_cast<int>(wParam);
7115 return 0;
7117 case SCI_GETMODEVENTMASK:
7118 return modEventMask;
7120 case SCI_CONVERTEOLS:
7121 pdoc->ConvertLineEnds(static_cast<int>(wParam));
7122 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
7123 return 0;
7125 case SCI_SETLENGTHFORENCODE:
7126 lengthForEncode = static_cast<int>(wParam);
7127 return 0;
7129 case SCI_SELECTIONISRECTANGLE:
7130 return sel.selType == Selection::selRectangle ? 1 : 0;
7132 case SCI_SETSELECTIONMODE: {
7133 switch (wParam) {
7134 case SC_SEL_STREAM:
7135 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7136 sel.selType = Selection::selStream;
7137 break;
7138 case SC_SEL_RECTANGLE:
7139 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
7140 sel.selType = Selection::selRectangle;
7141 break;
7142 case SC_SEL_LINES:
7143 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
7144 sel.selType = Selection::selLines;
7145 break;
7146 case SC_SEL_THIN:
7147 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
7148 sel.selType = Selection::selThin;
7149 break;
7150 default:
7151 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7152 sel.selType = Selection::selStream;
7154 InvalidateSelection(sel.RangeMain(), true);
7155 break;
7157 case SCI_GETSELECTIONMODE:
7158 switch (sel.selType) {
7159 case Selection::selStream:
7160 return SC_SEL_STREAM;
7161 case Selection::selRectangle:
7162 return SC_SEL_RECTANGLE;
7163 case Selection::selLines:
7164 return SC_SEL_LINES;
7165 case Selection::selThin:
7166 return SC_SEL_THIN;
7167 default: // ?!
7168 return SC_SEL_STREAM;
7170 case SCI_GETLINESELSTARTPOSITION:
7171 case SCI_GETLINESELENDPOSITION: {
7172 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(static_cast<int>(wParam))),
7173 SelectionPosition(pdoc->LineEnd(static_cast<int>(wParam))));
7174 for (size_t r=0; r<sel.Count(); r++) {
7175 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
7176 if (portion.start.IsValid()) {
7177 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
7180 return INVALID_POSITION;
7183 case SCI_SETOVERTYPE:
7184 inOverstrike = wParam != 0;
7185 break;
7187 case SCI_GETOVERTYPE:
7188 return inOverstrike ? 1 : 0;
7190 case SCI_SETFOCUS:
7191 SetFocusState(wParam != 0);
7192 break;
7194 case SCI_GETFOCUS:
7195 return hasFocus;
7197 case SCI_SETSTATUS:
7198 errorStatus = static_cast<int>(wParam);
7199 break;
7201 case SCI_GETSTATUS:
7202 return errorStatus;
7204 case SCI_SETMOUSEDOWNCAPTURES:
7205 mouseDownCaptures = wParam != 0;
7206 break;
7208 case SCI_GETMOUSEDOWNCAPTURES:
7209 return mouseDownCaptures;
7211 case SCI_SETCURSOR:
7212 cursorMode = static_cast<int>(wParam);
7213 DisplayCursor(Window::cursorText);
7214 break;
7216 case SCI_GETCURSOR:
7217 return cursorMode;
7219 case SCI_SETCONTROLCHARSYMBOL:
7220 vs.controlCharSymbol = static_cast<int>(wParam);
7221 InvalidateStyleRedraw();
7222 break;
7224 case SCI_GETCONTROLCHARSYMBOL:
7225 return vs.controlCharSymbol;
7227 case SCI_SETREPRESENTATION:
7228 reprs.SetRepresentation(reinterpret_cast<const char *>(wParam), CharPtrFromSPtr(lParam));
7229 break;
7231 case SCI_GETREPRESENTATION: {
7232 const Representation *repr = reprs.RepresentationFromCharacter(
7233 reinterpret_cast<const char *>(wParam), UTF8MaxBytes);
7234 if (repr) {
7235 return StringResult(lParam, repr->stringRep.c_str());
7237 return 0;
7240 case SCI_CLEARREPRESENTATION:
7241 reprs.ClearRepresentation(reinterpret_cast<const char *>(wParam));
7242 break;
7244 case SCI_STARTRECORD:
7245 recordingMacro = true;
7246 return 0;
7248 case SCI_STOPRECORD:
7249 recordingMacro = false;
7250 return 0;
7252 case SCI_MOVECARETINSIDEVIEW:
7253 MoveCaretInsideView();
7254 break;
7256 case SCI_SETFOLDMARGINCOLOUR:
7257 vs.foldmarginColour = ColourOptional(wParam, lParam);
7258 InvalidateStyleRedraw();
7259 break;
7261 case SCI_SETFOLDMARGINHICOLOUR:
7262 vs.foldmarginHighlightColour = ColourOptional(wParam, lParam);
7263 InvalidateStyleRedraw();
7264 break;
7266 case SCI_SETHOTSPOTACTIVEFORE:
7267 vs.hotspotColours.fore = ColourOptional(wParam, lParam);
7268 InvalidateStyleRedraw();
7269 break;
7271 case SCI_GETHOTSPOTACTIVEFORE:
7272 return vs.hotspotColours.fore.AsLong();
7274 case SCI_SETHOTSPOTACTIVEBACK:
7275 vs.hotspotColours.back = ColourOptional(wParam, lParam);
7276 InvalidateStyleRedraw();
7277 break;
7279 case SCI_GETHOTSPOTACTIVEBACK:
7280 return vs.hotspotColours.back.AsLong();
7282 case SCI_SETHOTSPOTACTIVEUNDERLINE:
7283 vs.hotspotUnderline = wParam != 0;
7284 InvalidateStyleRedraw();
7285 break;
7287 case SCI_GETHOTSPOTACTIVEUNDERLINE:
7288 return vs.hotspotUnderline ? 1 : 0;
7290 case SCI_SETHOTSPOTSINGLELINE:
7291 vs.hotspotSingleLine = wParam != 0;
7292 InvalidateStyleRedraw();
7293 break;
7295 case SCI_GETHOTSPOTSINGLELINE:
7296 return vs.hotspotSingleLine ? 1 : 0;
7298 case SCI_SETPASTECONVERTENDINGS:
7299 convertPastes = wParam != 0;
7300 break;
7302 case SCI_GETPASTECONVERTENDINGS:
7303 return convertPastes ? 1 : 0;
7305 case SCI_GETCHARACTERPOINTER:
7306 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
7308 case SCI_GETRANGEPOINTER:
7309 return reinterpret_cast<sptr_t>(pdoc->RangePointer(static_cast<int>(wParam), static_cast<int>(lParam)));
7311 case SCI_GETGAPPOSITION:
7312 return pdoc->GapPosition();
7314 case SCI_SETEXTRAASCENT:
7315 vs.extraAscent = static_cast<int>(wParam);
7316 InvalidateStyleRedraw();
7317 break;
7319 case SCI_GETEXTRAASCENT:
7320 return vs.extraAscent;
7322 case SCI_SETEXTRADESCENT:
7323 vs.extraDescent = static_cast<int>(wParam);
7324 InvalidateStyleRedraw();
7325 break;
7327 case SCI_GETEXTRADESCENT:
7328 return vs.extraDescent;
7330 case SCI_MARGINSETSTYLEOFFSET:
7331 vs.marginStyleOffset = static_cast<int>(wParam);
7332 InvalidateStyleRedraw();
7333 break;
7335 case SCI_MARGINGETSTYLEOFFSET:
7336 return vs.marginStyleOffset;
7338 case SCI_SETMARGINOPTIONS:
7339 marginOptions = static_cast<int>(wParam);
7340 break;
7342 case SCI_GETMARGINOPTIONS:
7343 return marginOptions;
7345 case SCI_MARGINSETTEXT:
7346 pdoc->MarginSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7347 break;
7349 case SCI_MARGINGETTEXT: {
7350 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7351 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
7354 case SCI_MARGINSETSTYLE:
7355 pdoc->MarginSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
7356 break;
7358 case SCI_MARGINGETSTYLE: {
7359 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7360 return st.style;
7363 case SCI_MARGINSETSTYLES:
7364 pdoc->MarginSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
7365 break;
7367 case SCI_MARGINGETSTYLES: {
7368 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7369 return BytesResult(lParam, st.styles, st.length);
7372 case SCI_MARGINTEXTCLEARALL:
7373 pdoc->MarginClearAll();
7374 break;
7376 case SCI_ANNOTATIONSETTEXT:
7377 pdoc->AnnotationSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7378 break;
7380 case SCI_ANNOTATIONGETTEXT: {
7381 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7382 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
7385 case SCI_ANNOTATIONGETSTYLE: {
7386 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7387 return st.style;
7390 case SCI_ANNOTATIONSETSTYLE:
7391 pdoc->AnnotationSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
7392 break;
7394 case SCI_ANNOTATIONSETSTYLES:
7395 pdoc->AnnotationSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
7396 break;
7398 case SCI_ANNOTATIONGETSTYLES: {
7399 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7400 return BytesResult(lParam, st.styles, st.length);
7403 case SCI_ANNOTATIONGETLINES:
7404 return pdoc->AnnotationLines(static_cast<int>(wParam));
7406 case SCI_ANNOTATIONCLEARALL:
7407 pdoc->AnnotationClearAll();
7408 break;
7410 case SCI_ANNOTATIONSETVISIBLE:
7411 SetAnnotationVisible(static_cast<int>(wParam));
7412 break;
7414 case SCI_ANNOTATIONGETVISIBLE:
7415 return vs.annotationVisible;
7417 case SCI_ANNOTATIONSETSTYLEOFFSET:
7418 vs.annotationStyleOffset = static_cast<int>(wParam);
7419 InvalidateStyleRedraw();
7420 break;
7422 case SCI_ANNOTATIONGETSTYLEOFFSET:
7423 return vs.annotationStyleOffset;
7425 case SCI_RELEASEALLEXTENDEDSTYLES:
7426 vs.ReleaseAllExtendedStyles();
7427 break;
7429 case SCI_ALLOCATEEXTENDEDSTYLES:
7430 return vs.AllocateExtendedStyles(static_cast<int>(wParam));
7432 case SCI_ADDUNDOACTION:
7433 pdoc->AddUndoAction(static_cast<int>(wParam), lParam & UNDO_MAY_COALESCE);
7434 break;
7436 case SCI_SETMOUSESELECTIONRECTANGULARSWITCH:
7437 mouseSelectionRectangularSwitch = wParam != 0;
7438 break;
7440 case SCI_GETMOUSESELECTIONRECTANGULARSWITCH:
7441 return mouseSelectionRectangularSwitch;
7443 case SCI_SETMULTIPLESELECTION:
7444 multipleSelection = wParam != 0;
7445 InvalidateCaret();
7446 break;
7448 case SCI_GETMULTIPLESELECTION:
7449 return multipleSelection;
7451 case SCI_SETADDITIONALSELECTIONTYPING:
7452 additionalSelectionTyping = wParam != 0;
7453 InvalidateCaret();
7454 break;
7456 case SCI_GETADDITIONALSELECTIONTYPING:
7457 return additionalSelectionTyping;
7459 case SCI_SETMULTIPASTE:
7460 multiPasteMode = static_cast<int>(wParam);
7461 break;
7463 case SCI_GETMULTIPASTE:
7464 return multiPasteMode;
7466 case SCI_SETADDITIONALCARETSBLINK:
7467 view.additionalCaretsBlink = wParam != 0;
7468 InvalidateCaret();
7469 break;
7471 case SCI_GETADDITIONALCARETSBLINK:
7472 return view.additionalCaretsBlink;
7474 case SCI_SETADDITIONALCARETSVISIBLE:
7475 view.additionalCaretsVisible = wParam != 0;
7476 InvalidateCaret();
7477 break;
7479 case SCI_GETADDITIONALCARETSVISIBLE:
7480 return view.additionalCaretsVisible;
7482 case SCI_GETSELECTIONS:
7483 return sel.Count();
7485 case SCI_GETSELECTIONEMPTY:
7486 return sel.Empty();
7488 case SCI_CLEARSELECTIONS:
7489 sel.Clear();
7490 Redraw();
7491 break;
7493 case SCI_SETSELECTION:
7494 sel.SetSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
7495 Redraw();
7496 break;
7498 case SCI_ADDSELECTION:
7499 sel.AddSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
7500 Redraw();
7501 break;
7503 case SCI_DROPSELECTIONN:
7504 sel.DropSelection(static_cast<int>(wParam));
7505 Redraw();
7506 break;
7508 case SCI_SETMAINSELECTION:
7509 sel.SetMain(static_cast<int>(wParam));
7510 Redraw();
7511 break;
7513 case SCI_GETMAINSELECTION:
7514 return sel.Main();
7516 case SCI_SETSELECTIONNCARET:
7517 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
7518 Redraw();
7519 break;
7521 case SCI_GETSELECTIONNCARET:
7522 return sel.Range(wParam).caret.Position();
7524 case SCI_SETSELECTIONNANCHOR:
7525 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
7526 Redraw();
7527 break;
7528 case SCI_GETSELECTIONNANCHOR:
7529 return sel.Range(wParam).anchor.Position();
7531 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
7532 sel.Range(wParam).caret.SetVirtualSpace(static_cast<int>(lParam));
7533 Redraw();
7534 break;
7536 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
7537 return sel.Range(wParam).caret.VirtualSpace();
7539 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
7540 sel.Range(wParam).anchor.SetVirtualSpace(static_cast<int>(lParam));
7541 Redraw();
7542 break;
7544 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
7545 return sel.Range(wParam).anchor.VirtualSpace();
7547 case SCI_SETSELECTIONNSTART:
7548 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
7549 Redraw();
7550 break;
7552 case SCI_GETSELECTIONNSTART:
7553 return sel.Range(wParam).Start().Position();
7555 case SCI_SETSELECTIONNEND:
7556 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
7557 Redraw();
7558 break;
7560 case SCI_GETSELECTIONNEND:
7561 return sel.Range(wParam).End().Position();
7563 case SCI_SETRECTANGULARSELECTIONCARET:
7564 if (!sel.IsRectangular())
7565 sel.Clear();
7566 sel.selType = Selection::selRectangle;
7567 sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
7568 SetRectangularRange();
7569 Redraw();
7570 break;
7572 case SCI_GETRECTANGULARSELECTIONCARET:
7573 return sel.Rectangular().caret.Position();
7575 case SCI_SETRECTANGULARSELECTIONANCHOR:
7576 if (!sel.IsRectangular())
7577 sel.Clear();
7578 sel.selType = Selection::selRectangle;
7579 sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
7580 SetRectangularRange();
7581 Redraw();
7582 break;
7584 case SCI_GETRECTANGULARSELECTIONANCHOR:
7585 return sel.Rectangular().anchor.Position();
7587 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
7588 if (!sel.IsRectangular())
7589 sel.Clear();
7590 sel.selType = Selection::selRectangle;
7591 sel.Rectangular().caret.SetVirtualSpace(static_cast<int>(wParam));
7592 SetRectangularRange();
7593 Redraw();
7594 break;
7596 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
7597 return sel.Rectangular().caret.VirtualSpace();
7599 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
7600 if (!sel.IsRectangular())
7601 sel.Clear();
7602 sel.selType = Selection::selRectangle;
7603 sel.Rectangular().anchor.SetVirtualSpace(static_cast<int>(wParam));
7604 SetRectangularRange();
7605 Redraw();
7606 break;
7608 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
7609 return sel.Rectangular().anchor.VirtualSpace();
7611 case SCI_SETVIRTUALSPACEOPTIONS:
7612 virtualSpaceOptions = static_cast<int>(wParam);
7613 break;
7615 case SCI_GETVIRTUALSPACEOPTIONS:
7616 return virtualSpaceOptions;
7618 case SCI_SETADDITIONALSELFORE:
7619 vs.selAdditionalForeground = ColourDesired(static_cast<long>(wParam));
7620 InvalidateStyleRedraw();
7621 break;
7623 case SCI_SETADDITIONALSELBACK:
7624 vs.selAdditionalBackground = ColourDesired(static_cast<long>(wParam));
7625 InvalidateStyleRedraw();
7626 break;
7628 case SCI_SETADDITIONALSELALPHA:
7629 vs.selAdditionalAlpha = static_cast<int>(wParam);
7630 InvalidateStyleRedraw();
7631 break;
7633 case SCI_GETADDITIONALSELALPHA:
7634 return vs.selAdditionalAlpha;
7636 case SCI_SETADDITIONALCARETFORE:
7637 vs.additionalCaretColour = ColourDesired(static_cast<long>(wParam));
7638 InvalidateStyleRedraw();
7639 break;
7641 case SCI_GETADDITIONALCARETFORE:
7642 return vs.additionalCaretColour.AsLong();
7644 case SCI_ROTATESELECTION:
7645 sel.RotateMain();
7646 InvalidateSelection(sel.RangeMain(), true);
7647 break;
7649 case SCI_SWAPMAINANCHORCARET:
7650 InvalidateSelection(sel.RangeMain());
7651 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
7652 break;
7654 case SCI_CHANGELEXERSTATE:
7655 pdoc->ChangeLexerState(static_cast<int>(wParam), static_cast<int>(lParam));
7656 break;
7658 case SCI_SETIDENTIFIER:
7659 SetCtrlID(static_cast<int>(wParam));
7660 break;
7662 case SCI_GETIDENTIFIER:
7663 return GetCtrlID();
7665 case SCI_SETTECHNOLOGY:
7666 // No action by default
7667 break;
7669 case SCI_GETTECHNOLOGY:
7670 return technology;
7672 case SCI_COUNTCHARACTERS:
7673 return pdoc->CountCharacters(static_cast<int>(wParam), static_cast<int>(lParam));
7675 default:
7676 return DefWndProc(iMessage, wParam, lParam);
7678 //Platform::DebugPrintf("end wnd proc\n");
7679 return 0l;