Fix catalan translation
[geany-mirror.git] / scintilla / src / Editor.cxx
blob77d9ce5e74e4d6941d216d4a22722788716a391c
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 ctrlID = 0;
105 stylesValid = false;
106 technology = SC_TECHNOLOGY_DEFAULT;
107 scaleRGBAImage = 100.0f;
109 cursorMode = SC_CURSORNORMAL;
111 hasFocus = false;
112 errorStatus = 0;
113 mouseDownCaptures = true;
115 lastClickTime = 0;
116 doubleClickCloseThreshold = Point(3, 3);
117 dwellDelay = SC_TIME_FOREVER;
118 ticksToDwell = SC_TIME_FOREVER;
119 dwelling = false;
120 ptMouseLast.x = 0;
121 ptMouseLast.y = 0;
122 inDragDrop = ddNone;
123 dropWentOutside = false;
124 posDrop = SelectionPosition(invalidPosition);
125 hotSpotClickPos = INVALID_POSITION;
126 selectionType = selChar;
128 lastXChosen = 0;
129 lineAnchorPos = 0;
130 originalAnchorPos = 0;
131 wordSelectAnchorStartPos = 0;
132 wordSelectAnchorEndPos = 0;
133 wordSelectInitialCaretPos = -1;
135 caretXPolicy = CARET_SLOP | CARET_EVEN;
136 caretXSlop = 50;
138 caretYPolicy = CARET_EVEN;
139 caretYSlop = 0;
141 visiblePolicy = 0;
142 visibleSlop = 0;
144 searchAnchor = 0;
146 xCaretMargin = 50;
147 horizontalScrollBarVisible = true;
148 scrollWidth = 2000;
149 verticalScrollBarVisible = true;
150 endAtLastLine = true;
151 caretSticky = SC_CARETSTICKY_OFF;
152 marginOptions = SC_MARGINOPTION_NONE;
153 mouseSelectionRectangularSwitch = false;
154 multipleSelection = false;
155 additionalSelectionTyping = false;
156 multiPasteMode = SC_MULTIPASTE_ONCE;
157 virtualSpaceOptions = SCVS_NONE;
159 targetStart = 0;
160 targetEnd = 0;
161 searchFlags = 0;
163 topLine = 0;
164 posTopLine = 0;
166 lengthForEncode = -1;
168 needUpdateUI = 0;
169 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
171 paintState = notPainting;
172 paintAbandonedByStyling = false;
173 paintingAllText = false;
174 willRedrawAll = false;
176 modEventMask = SC_MODEVENTMASKALL;
178 pdoc->AddWatcher(this, 0);
180 recordingMacro = false;
181 foldAutomatic = 0;
183 convertPastes = true;
185 SetRepresentations();
188 Editor::~Editor() {
189 pdoc->RemoveWatcher(this, 0);
190 DropGraphics(true);
193 void Editor::Finalise() {
194 SetIdle(false);
195 CancelModes();
198 void Editor::SetRepresentations() {
199 reprs.Clear();
201 // C0 control set
202 const char *reps[] = {
203 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
204 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
205 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
206 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
208 for (size_t j=0; j < ELEMENTS(reps); j++) {
209 char c[2] = { static_cast<char>(j), 0 };
210 reprs.SetRepresentation(c, reps[j]);
213 // C1 control set
214 // As well as Unicode mode, ISO-8859-1 should use these
215 if (IsUnicodeMode()) {
216 const char *repsC1[] = {
217 "PAD", "HOP", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
218 "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
219 "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
220 "SOS", "SGCI", "SCI", "CSI", "ST", "OSC", "PM", "APC"
222 for (size_t j=0; j < ELEMENTS(repsC1); j++) {
223 char c1[3] = { '\xc2', static_cast<char>(0x80+j), 0 };
224 reprs.SetRepresentation(c1, repsC1[j]);
226 reprs.SetRepresentation("\xe2\x80\xa8", "LS");
227 reprs.SetRepresentation("\xe2\x80\xa9", "PS");
230 // UTF-8 invalid bytes
231 if (IsUnicodeMode()) {
232 for (int k=0x80; k < 0x100; k++) {
233 char hiByte[2] = { static_cast<char>(k), 0 };
234 char hexits[4];
235 sprintf(hexits, "x%2X", k);
236 reprs.SetRepresentation(hiByte, hexits);
241 void Editor::DropGraphics(bool freeObjects) {
242 marginView.DropGraphics(freeObjects);
243 view.DropGraphics(freeObjects);
246 void Editor::AllocateGraphics() {
247 marginView.AllocateGraphics(vs);
248 view.AllocateGraphics(vs);
251 void Editor::InvalidateStyleData() {
252 stylesValid = false;
253 vs.technology = technology;
254 DropGraphics(false);
255 AllocateGraphics();
256 view.llc.Invalidate(LineLayout::llInvalid);
257 view.posCache.Clear();
260 void Editor::InvalidateStyleRedraw() {
261 NeedWrapping();
262 InvalidateStyleData();
263 Redraw();
266 void Editor::RefreshStyleData() {
267 if (!stylesValid) {
268 stylesValid = true;
269 AutoSurface surface(this);
270 if (surface) {
271 vs.Refresh(*surface, pdoc->tabInChars);
273 SetScrollBars();
274 SetRectangularRange();
278 Point Editor::GetVisibleOriginInMain() const {
279 return Point(0,0);
282 Point Editor::DocumentPointFromView(Point ptView) const {
283 Point ptDocument = ptView;
284 if (wMargin.GetID()) {
285 Point ptOrigin = GetVisibleOriginInMain();
286 ptDocument.x += ptOrigin.x;
287 ptDocument.y += ptOrigin.y;
288 } else {
289 ptDocument.x += xOffset;
290 ptDocument.y += topLine * vs.lineHeight;
292 return ptDocument;
295 int Editor::TopLineOfMain() const {
296 if (wMargin.GetID())
297 return 0;
298 else
299 return topLine;
302 PRectangle Editor::GetClientRectangle() const {
303 Window &win = const_cast<Window &>(wMain);
304 return win.GetClientPosition();
307 PRectangle Editor::GetClientDrawingRectangle() {
308 return GetClientRectangle();
311 PRectangle Editor::GetTextRectangle() const {
312 PRectangle rc = GetClientRectangle();
313 rc.left += vs.textStart;
314 rc.right -= vs.rightMarginWidth;
315 return rc;
318 int Editor::LinesOnScreen() const {
319 PRectangle rcClient = GetClientRectangle();
320 int htClient = static_cast<int>(rcClient.bottom - rcClient.top);
321 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
322 return htClient / vs.lineHeight;
325 int Editor::LinesToScroll() const {
326 int retVal = LinesOnScreen() - 1;
327 if (retVal < 1)
328 return 1;
329 else
330 return retVal;
333 int Editor::MaxScrollPos() const {
334 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
335 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
336 int retVal = cs.LinesDisplayed();
337 if (endAtLastLine) {
338 retVal -= LinesOnScreen();
339 } else {
340 retVal--;
342 if (retVal < 0) {
343 return 0;
344 } else {
345 return retVal;
349 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
350 if (sp.Position() < 0) {
351 return SelectionPosition(0);
352 } else if (sp.Position() > pdoc->Length()) {
353 return SelectionPosition(pdoc->Length());
354 } else {
355 // If not at end of line then set offset to 0
356 if (!pdoc->IsLineEndPosition(sp.Position()))
357 sp.SetVirtualSpace(0);
358 return sp;
362 Point Editor::LocationFromPosition(SelectionPosition pos) {
363 RefreshStyleData();
364 AutoSurface surface(this);
365 return view.LocationFromPosition(surface, *this, pos, topLine, vs);
368 Point Editor::LocationFromPosition(int pos) {
369 return LocationFromPosition(SelectionPosition(pos));
372 int Editor::XFromPosition(int pos) {
373 Point pt = LocationFromPosition(pos);
374 return static_cast<int>(pt.x) - vs.textStart + xOffset;
377 int Editor::XFromPosition(SelectionPosition sp) {
378 Point pt = LocationFromPosition(sp);
379 return static_cast<int>(pt.x) - vs.textStart + xOffset;
382 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
383 RefreshStyleData();
384 AutoSurface surface(this);
386 if (canReturnInvalid) {
387 PRectangle rcClient = GetTextRectangle();
388 // May be in scroll view coordinates so translate back to main view
389 Point ptOrigin = GetVisibleOriginInMain();
390 rcClient.Move(-ptOrigin.x, -ptOrigin.y);
391 if (!rcClient.Contains(pt))
392 return SelectionPosition(INVALID_POSITION);
393 if (pt.x < vs.textStart)
394 return SelectionPosition(INVALID_POSITION);
395 if (pt.y < 0)
396 return SelectionPosition(INVALID_POSITION);
398 pt = DocumentPointFromView(pt);
399 return view.SPositionFromLocation(surface, *this, pt, canReturnInvalid, charPosition, virtualSpace, vs);
402 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
403 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
407 * Find the document position corresponding to an x coordinate on a particular document line.
408 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
409 * This method is used for rectangular selections and does not work on wrapped lines.
411 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
412 RefreshStyleData();
413 if (lineDoc >= pdoc->LinesTotal())
414 return SelectionPosition(pdoc->Length());
415 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
416 AutoSurface surface(this);
417 return view.SPositionFromLineX(surface, *this, lineDoc, x, vs);
420 int Editor::PositionFromLineX(int lineDoc, int x) {
421 return SPositionFromLineX(lineDoc, x).Position();
424 int Editor::LineFromLocation(Point pt) const {
425 return cs.DocFromDisplay(static_cast<int>(pt.y) / vs.lineHeight + topLine);
428 void Editor::SetTopLine(int topLineNew) {
429 if ((topLine != topLineNew) && (topLineNew >= 0)) {
430 topLine = topLineNew;
431 ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
433 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
437 * If painting then abandon the painting because a wider redraw is needed.
438 * @return true if calling code should stop drawing.
440 bool Editor::AbandonPaint() {
441 if ((paintState == painting) && !paintingAllText) {
442 paintState = paintAbandoned;
444 return paintState == paintAbandoned;
447 void Editor::RedrawRect(PRectangle rc) {
448 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
450 // Clip the redraw rectangle into the client area
451 PRectangle rcClient = GetClientRectangle();
452 if (rc.top < rcClient.top)
453 rc.top = rcClient.top;
454 if (rc.bottom > rcClient.bottom)
455 rc.bottom = rcClient.bottom;
456 if (rc.left < rcClient.left)
457 rc.left = rcClient.left;
458 if (rc.right > rcClient.right)
459 rc.right = rcClient.right;
461 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
462 wMain.InvalidateRectangle(rc);
466 void Editor::DiscardOverdraw() {
467 // Overridden on platforms that may draw outside visible area.
470 void Editor::Redraw() {
471 //Platform::DebugPrintf("Redraw all\n");
472 PRectangle rcClient = GetClientRectangle();
473 wMain.InvalidateRectangle(rcClient);
474 if (wMargin.GetID())
475 wMargin.InvalidateAll();
476 //wMain.InvalidateAll();
479 void Editor::RedrawSelMargin(int line, bool allAfter) {
480 bool abandonDraw = false;
481 if (!wMargin.GetID()) // Margin in main window so may need to abandon and retry
482 abandonDraw = AbandonPaint();
483 if (!abandonDraw) {
484 if (vs.maskInLine) {
485 Redraw();
486 } else {
487 PRectangle rcSelMargin = GetClientRectangle();
488 rcSelMargin.right = rcSelMargin.left + vs.fixedColumnWidth;
489 if (line != -1) {
490 PRectangle rcLine = RectangleFromRange(Range(pdoc->LineStart(line)), 0);
492 // Inflate line rectangle if there are image markers with height larger than line height
493 if (vs.largestMarkerHeight > vs.lineHeight) {
494 int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
495 rcLine.top -= delta;
496 rcLine.bottom += delta;
497 if (rcLine.top < rcSelMargin.top)
498 rcLine.top = rcSelMargin.top;
499 if (rcLine.bottom > rcSelMargin.bottom)
500 rcLine.bottom = rcSelMargin.bottom;
503 rcSelMargin.top = rcLine.top;
504 if (!allAfter)
505 rcSelMargin.bottom = rcLine.bottom;
506 if (rcSelMargin.Empty())
507 return;
509 if (wMargin.GetID()) {
510 Point ptOrigin = GetVisibleOriginInMain();
511 rcSelMargin.Move(-ptOrigin.x, -ptOrigin.y);
512 wMargin.InvalidateRectangle(rcSelMargin);
513 } else {
514 wMain.InvalidateRectangle(rcSelMargin);
520 PRectangle Editor::RectangleFromRange(Range r, int overlap) {
521 const int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(r.First()));
522 const int maxLine = cs.DisplayLastFromDoc(pdoc->LineFromPosition(r.Last()));
523 const PRectangle rcClientDrawing = GetClientDrawingRectangle();
524 PRectangle rc;
525 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
526 rc.left = static_cast<XYPOSITION>(vs.textStart - leftTextOverlap);
527 rc.top = static_cast<XYPOSITION>((minLine - TopLineOfMain()) * vs.lineHeight - overlap);
528 if (rc.top < rcClientDrawing.top)
529 rc.top = rcClientDrawing.top;
530 // Extend to right of prepared area if any to prevent artifacts from caret line highlight
531 rc.right = rcClientDrawing.right;
532 rc.bottom = static_cast<XYPOSITION>((maxLine - TopLineOfMain() + 1) * vs.lineHeight + overlap);
534 return rc;
537 void Editor::InvalidateRange(int start, int end) {
538 RedrawRect(RectangleFromRange(Range(start, end), view.LinesOverlap() ? vs.lineOverlap : 0));
541 int Editor::CurrentPosition() const {
542 return sel.MainCaret();
545 bool Editor::SelectionEmpty() const {
546 return sel.Empty();
549 SelectionPosition Editor::SelectionStart() {
550 return sel.RangeMain().Start();
553 SelectionPosition Editor::SelectionEnd() {
554 return sel.RangeMain().End();
557 void Editor::SetRectangularRange() {
558 if (sel.IsRectangular()) {
559 int xAnchor = XFromPosition(sel.Rectangular().anchor);
560 int xCaret = XFromPosition(sel.Rectangular().caret);
561 if (sel.selType == Selection::selThin) {
562 xCaret = xAnchor;
564 int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
565 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
566 int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
567 for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
568 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
569 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
570 range.ClearVirtualSpace();
571 if (line == lineAnchorRect)
572 sel.SetSelection(range);
573 else
574 sel.AddSelectionWithoutTrim(range);
579 void Editor::ThinRectangularRange() {
580 if (sel.IsRectangular()) {
581 sel.selType = Selection::selThin;
582 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
583 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
584 } else {
585 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
587 SetRectangularRange();
591 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
592 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
593 invalidateWholeSelection = true;
595 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
596 // +1 for lastAffected ensures caret repainted
597 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
598 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
599 if (invalidateWholeSelection) {
600 for (size_t r=0; r<sel.Count(); r++) {
601 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
602 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
603 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
604 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
607 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
608 InvalidateRange(firstAffected, lastAffected);
611 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
612 currentPos_ = ClampPositionIntoDocument(currentPos_);
613 anchor_ = ClampPositionIntoDocument(anchor_);
614 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
615 /* For Line selection - ensure the anchor and caret are always
616 at the beginning and end of the region lines. */
617 if (sel.selType == Selection::selLines) {
618 if (currentPos_ > anchor_) {
619 anchor_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
620 currentPos_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
621 } else {
622 currentPos_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
623 anchor_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
626 SelectionRange rangeNew(currentPos_, anchor_);
627 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
628 InvalidateSelection(rangeNew);
630 sel.RangeMain() = rangeNew;
631 SetRectangularRange();
632 ClaimSelection();
633 SetHoverIndicatorPosition(sel.MainCaret());
635 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
636 RedrawSelMargin();
638 QueueIdleWork(WorkNeeded::workUpdateUI);
641 void Editor::SetSelection(int currentPos_, int anchor_) {
642 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
645 // Just move the caret on the main selection
646 void Editor::SetSelection(SelectionPosition currentPos_) {
647 currentPos_ = ClampPositionIntoDocument(currentPos_);
648 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
649 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
650 InvalidateSelection(SelectionRange(currentPos_));
652 if (sel.IsRectangular()) {
653 sel.Rectangular() =
654 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
655 SetRectangularRange();
656 } else {
657 sel.RangeMain() =
658 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
660 ClaimSelection();
661 SetHoverIndicatorPosition(sel.MainCaret());
663 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
664 RedrawSelMargin();
666 QueueIdleWork(WorkNeeded::workUpdateUI);
669 void Editor::SetSelection(int currentPos_) {
670 SetSelection(SelectionPosition(currentPos_));
673 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
674 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
675 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
676 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
677 InvalidateSelection(rangeNew);
679 sel.Clear();
680 sel.RangeMain() = rangeNew;
681 SetRectangularRange();
682 ClaimSelection();
683 SetHoverIndicatorPosition(sel.MainCaret());
685 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
686 RedrawSelMargin();
688 QueueIdleWork(WorkNeeded::workUpdateUI);
691 void Editor::SetEmptySelection(int currentPos_) {
692 SetEmptySelection(SelectionPosition(currentPos_));
695 bool Editor::RangeContainsProtected(int start, int end) const {
696 if (vs.ProtectionActive()) {
697 if (start > end) {
698 int t = start;
699 start = end;
700 end = t;
702 for (int pos = start; pos < end; pos++) {
703 if (vs.styles[pdoc->StyleAt(pos)].IsProtected())
704 return true;
707 return false;
710 bool Editor::SelectionContainsProtected() {
711 for (size_t r=0; r<sel.Count(); r++) {
712 if (RangeContainsProtected(sel.Range(r).Start().Position(),
713 sel.Range(r).End().Position())) {
714 return true;
717 return false;
721 * Asks document to find a good position and then moves out of any invisible positions.
723 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
724 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
727 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
728 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
729 if (posMoved != pos.Position())
730 pos.SetPosition(posMoved);
731 if (vs.ProtectionActive()) {
732 if (moveDir > 0) {
733 if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1)].IsProtected()) {
734 while ((pos.Position() < pdoc->Length()) &&
735 (vs.styles[pdoc->StyleAt(pos.Position())].IsProtected()))
736 pos.Add(1);
738 } else if (moveDir < 0) {
739 if (vs.styles[pdoc->StyleAt(pos.Position())].IsProtected()) {
740 while ((pos.Position() > 0) &&
741 (vs.styles[pdoc->StyleAt(pos.Position() - 1)].IsProtected()))
742 pos.Add(-1);
746 return pos;
749 int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
750 bool simpleCaret = (sel.Count() == 1) && sel.Empty();
751 SelectionPosition spCaret = sel.Last();
753 int delta = newPos.Position() - sel.MainCaret();
754 newPos = ClampPositionIntoDocument(newPos);
755 newPos = MovePositionOutsideChar(newPos, delta);
756 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
757 // Can't turn into multiple selection so clear additional selections
758 InvalidateSelection(SelectionRange(newPos), true);
759 SelectionRange rangeMain = sel.RangeMain();
760 sel.SetSelection(rangeMain);
762 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
763 // Switching to rectangular
764 InvalidateSelection(sel.RangeMain(), false);
765 SelectionRange rangeMain = sel.RangeMain();
766 sel.Clear();
767 sel.Rectangular() = rangeMain;
769 if (selt != Selection::noSel) {
770 sel.selType = selt;
772 if (selt != Selection::noSel || sel.MoveExtends()) {
773 SetSelection(newPos);
774 } else {
775 SetEmptySelection(newPos);
777 ShowCaretAtCurrentPosition();
779 int currentLine = pdoc->LineFromPosition(newPos.Position());
780 if (ensureVisible) {
781 // In case in need of wrapping to ensure DisplayFromDoc works.
782 if (currentLine >= wrapPending.start)
783 WrapLines(wsAll);
784 XYScrollPosition newXY = XYScrollToMakeVisible(
785 SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret), xysDefault);
786 if (simpleCaret && (newXY.xOffset == xOffset)) {
787 // simple vertical scroll then invalidate
788 ScrollTo(newXY.topLine);
789 InvalidateSelection(SelectionRange(spCaret), true);
790 } else {
791 SetXYScroll(newXY);
795 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
796 RedrawSelMargin();
798 return 0;
801 int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
802 return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
805 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
806 pos = ClampPositionIntoDocument(pos);
807 pos = MovePositionOutsideChar(pos, moveDir);
808 int lineDoc = pdoc->LineFromPosition(pos.Position());
809 if (cs.GetVisible(lineDoc)) {
810 return pos;
811 } else {
812 int lineDisplay = cs.DisplayFromDoc(lineDoc);
813 if (moveDir > 0) {
814 // lineDisplay is already line before fold as lines in fold use display line of line after fold
815 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
816 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
817 } else {
818 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
819 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
824 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
825 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
828 Point Editor::PointMainCaret() {
829 return LocationFromPosition(sel.Range(sel.Main()).caret);
833 * Choose the x position that the caret will try to stick to
834 * as it moves up and down.
836 void Editor::SetLastXChosen() {
837 Point pt = PointMainCaret();
838 lastXChosen = static_cast<int>(pt.x) + xOffset;
841 void Editor::ScrollTo(int line, bool moveThumb) {
842 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
843 if (topLineNew != topLine) {
844 // Try to optimise small scrolls
845 #ifndef UNDER_CE
846 int linesToMove = topLine - topLineNew;
847 bool performBlit = (abs(linesToMove) <= 10) && (paintState == notPainting);
848 willRedrawAll = !performBlit;
849 #endif
850 SetTopLine(topLineNew);
851 // Optimize by styling the view as this will invalidate any needed area
852 // which could abort the initial paint if discovered later.
853 StyleToPositionInView(PositionAfterArea(GetClientRectangle()));
854 #ifndef UNDER_CE
855 // Perform redraw rather than scroll if many lines would be redrawn anyway.
856 if (performBlit) {
857 ScrollText(linesToMove);
858 } else {
859 Redraw();
861 willRedrawAll = false;
862 #else
863 Redraw();
864 #endif
865 if (moveThumb) {
866 SetVerticalScrollPos();
871 void Editor::ScrollText(int /* linesToMove */) {
872 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
873 Redraw();
876 void Editor::HorizontalScrollTo(int xPos) {
877 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
878 if (xPos < 0)
879 xPos = 0;
880 if (!Wrapping() && (xOffset != xPos)) {
881 xOffset = xPos;
882 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
883 SetHorizontalScrollPos();
884 RedrawRect(GetClientRectangle());
888 void Editor::VerticalCentreCaret() {
889 int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
890 int lineDisplay = cs.DisplayFromDoc(lineDoc);
891 int newTop = lineDisplay - (LinesOnScreen() / 2);
892 if (topLine != newTop) {
893 SetTopLine(newTop > 0 ? newTop : 0);
894 RedrawRect(GetClientRectangle());
898 // Avoid 64 bit compiler warnings.
899 // Scintilla does not support text buffers larger than 2**31
900 static int istrlen(const char *s) {
901 return static_cast<int>(s ? strlen(s) : 0);
904 void Editor::MoveSelectedLines(int lineDelta) {
906 // if selection doesn't start at the beginning of the line, set the new start
907 int selectionStart = SelectionStart().Position();
908 int startLine = pdoc->LineFromPosition(selectionStart);
909 int beginningOfStartLine = pdoc->LineStart(startLine);
910 selectionStart = beginningOfStartLine;
912 // if selection doesn't end at the beginning of a line greater than that of the start,
913 // then set it at the beginning of the next one
914 int selectionEnd = SelectionEnd().Position();
915 int endLine = pdoc->LineFromPosition(selectionEnd);
916 int beginningOfEndLine = pdoc->LineStart(endLine);
917 bool appendEol = false;
918 if (selectionEnd > beginningOfEndLine
919 || selectionStart == selectionEnd) {
920 selectionEnd = pdoc->LineStart(endLine + 1);
921 appendEol = (selectionEnd == pdoc->Length() && pdoc->LineFromPosition(selectionEnd) == endLine);
924 // if there's nowhere for the selection to move
925 // (i.e. at the beginning going up or at the end going down),
926 // stop it right there!
927 if ((selectionStart == 0 && lineDelta < 0)
928 || (selectionEnd == pdoc->Length() && lineDelta > 0)
929 || selectionStart == selectionEnd) {
930 return;
933 UndoGroup ug(pdoc);
935 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
936 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
937 ClearSelection();
938 selectionEnd = CurrentPosition();
940 SetSelection(selectionStart, selectionEnd);
942 SelectionText selectedText;
943 CopySelectionRange(&selectedText);
945 int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
946 Point currentLocation = LocationFromPosition(CurrentPosition());
947 int currentLine = LineFromLocation(currentLocation);
949 if (appendEol)
950 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
951 ClearSelection();
953 const char *eol = StringFromEOLMode(pdoc->eolMode);
954 if (currentLine + lineDelta >= pdoc->LinesTotal())
955 pdoc->InsertString(pdoc->Length(), eol, istrlen(eol));
956 GoToLine(currentLine + lineDelta);
958 selectionLength = pdoc->InsertString(CurrentPosition(), selectedText.Data(), selectionLength);
959 if (appendEol) {
960 const int lengthInserted = pdoc->InsertString(CurrentPosition() + selectionLength, eol, istrlen(eol));
961 selectionLength += lengthInserted;
963 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
966 void Editor::MoveSelectedLinesUp() {
967 MoveSelectedLines(-1);
970 void Editor::MoveSelectedLinesDown() {
971 MoveSelectedLines(1);
974 void Editor::MoveCaretInsideView(bool ensureVisible) {
975 PRectangle rcClient = GetTextRectangle();
976 Point pt = PointMainCaret();
977 if (pt.y < rcClient.top) {
978 MovePositionTo(SPositionFromLocation(
979 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top)),
980 false, false, UserVirtualSpace()),
981 Selection::noSel, ensureVisible);
982 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
983 int yOfLastLineFullyDisplayed = static_cast<int>(rcClient.top) + (LinesOnScreen() - 1) * vs.lineHeight;
984 MovePositionTo(SPositionFromLocation(
985 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top) + yOfLastLineFullyDisplayed),
986 false, false, UserVirtualSpace()),
987 Selection::noSel, ensureVisible);
991 int Editor::DisplayFromPosition(int pos) {
992 AutoSurface surface(this);
993 return view.DisplayFromPosition(surface, *this, pos, vs);
997 * Ensure the caret is reasonably visible in context.
999 Caret policy in SciTE
1001 If slop is set, we can define a slop value.
1002 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1003 This zone is defined as a number of pixels near the vertical margins,
1004 and as a number of lines near the horizontal margins.
1005 By keeping the caret away from the edges, it is seen within its context,
1006 so it is likely that the identifier that the caret is on can be completely seen,
1007 and that the current line is seen with some of the lines following it which are
1008 often dependent on that line.
1010 If strict is set, the policy is enforced... strictly.
1011 The caret is centred on the display if slop is not set,
1012 and cannot go in the UZ if slop is set.
1014 If jumps is set, the display is moved more energetically
1015 so the caret can move in the same direction longer before the policy is applied again.
1016 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1018 If even is not set, instead of having symmetrical UZs,
1019 the left and bottom UZs are extended up to right and top UZs respectively.
1020 This way, we favour the displaying of useful information: the beginning of lines,
1021 where most code reside, and the lines after the caret, eg. the body of a function.
1023 | | | | |
1024 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1025 | | | | | visibility or going into the UZ) display is...
1026 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1027 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1028 0 | 0 | 0 | 1 | Yes | moved by one position
1029 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1030 0 | 0 | 1 | 1 | Yes | centred on the caret
1031 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1032 0 | 1 | - | 1 | No, caret is always centred | -
1033 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1034 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1035 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1036 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1037 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1038 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1039 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1042 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange &range, const XYScrollOptions options) {
1043 PRectangle rcClient = GetTextRectangle();
1044 Point pt = LocationFromPosition(range.caret);
1045 Point ptAnchor = LocationFromPosition(range.anchor);
1046 const Point ptOrigin = GetVisibleOriginInMain();
1047 pt.x += ptOrigin.x;
1048 pt.y += ptOrigin.y;
1049 ptAnchor.x += ptOrigin.x;
1050 ptAnchor.y += ptOrigin.y;
1051 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1053 XYScrollPosition newXY(xOffset, topLine);
1054 if (rcClient.Empty()) {
1055 return newXY;
1058 // Vertical positioning
1059 if ((options & xysVertical) && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1060 const int lineCaret = DisplayFromPosition(range.caret.Position());
1061 const int linesOnScreen = LinesOnScreen();
1062 const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1063 const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
1064 const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
1065 const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
1066 const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
1068 // It should be possible to scroll the window to show the caret,
1069 // but this fails to remove the caret on GTK+
1070 if (bSlop) { // A margin is defined
1071 int yMoveT, yMoveB;
1072 if (bStrict) {
1073 int yMarginT, yMarginB;
1074 if (!(options & xysUseMargin)) {
1075 // In drag mode, avoid moves
1076 // otherwise, a double click will select several lines.
1077 yMarginT = yMarginB = 0;
1078 } else {
1079 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1080 // a maximum of slightly less than half the heigth of the text area.
1081 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1082 if (bEven) {
1083 yMarginB = yMarginT;
1084 } else {
1085 yMarginB = linesOnScreen - yMarginT - 1;
1088 yMoveT = yMarginT;
1089 if (bEven) {
1090 if (bJump) {
1091 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1093 yMoveB = yMoveT;
1094 } else {
1095 yMoveB = linesOnScreen - yMoveT - 1;
1097 if (lineCaret < topLine + yMarginT) {
1098 // Caret goes too high
1099 newXY.topLine = lineCaret - yMoveT;
1100 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1101 // Caret goes too low
1102 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1104 } else { // Not strict
1105 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1106 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1107 if (bEven) {
1108 yMoveB = yMoveT;
1109 } else {
1110 yMoveB = linesOnScreen - yMoveT - 1;
1112 if (lineCaret < topLine) {
1113 // Caret goes too high
1114 newXY.topLine = lineCaret - yMoveT;
1115 } else if (lineCaret > topLine + linesOnScreen - 1) {
1116 // Caret goes too low
1117 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1120 } else { // No slop
1121 if (!bStrict && !bJump) {
1122 // Minimal move
1123 if (lineCaret < topLine) {
1124 // Caret goes too high
1125 newXY.topLine = lineCaret;
1126 } else if (lineCaret > topLine + linesOnScreen - 1) {
1127 // Caret goes too low
1128 if (bEven) {
1129 newXY.topLine = lineCaret - linesOnScreen + 1;
1130 } else {
1131 newXY.topLine = lineCaret;
1134 } else { // Strict or going out of display
1135 if (bEven) {
1136 // Always center caret
1137 newXY.topLine = lineCaret - halfScreen;
1138 } else {
1139 // Always put caret on top of display
1140 newXY.topLine = lineCaret;
1144 if (!(range.caret == range.anchor)) {
1145 const int lineAnchor = DisplayFromPosition(range.anchor.Position());
1146 if (lineAnchor < lineCaret) {
1147 // Shift up to show anchor or as much of range as possible
1148 newXY.topLine = std::min(newXY.topLine, lineAnchor);
1149 newXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());
1150 } else {
1151 // Shift down to show anchor or as much of range as possible
1152 newXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());
1153 newXY.topLine = std::min(newXY.topLine, lineCaret);
1156 newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
1159 // Horizontal positioning
1160 if ((options & xysHorizontal) && !Wrapping()) {
1161 const int halfScreen = Platform::Maximum(static_cast<int>(rcClient.Width()) - 4, 4) / 2;
1162 const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
1163 const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
1164 const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
1165 const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
1167 if (bSlop) { // A margin is defined
1168 int xMoveL, xMoveR;
1169 if (bStrict) {
1170 int xMarginL, xMarginR;
1171 if (!(options & xysUseMargin)) {
1172 // In drag mode, avoid moves unless very near of the margin
1173 // otherwise, a simple click will select text.
1174 xMarginL = xMarginR = 2;
1175 } else {
1176 // xMargin must equal to caretXSlop, with a minimum of 2 and
1177 // a maximum of slightly less than half the width of the text area.
1178 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1179 if (bEven) {
1180 xMarginL = xMarginR;
1181 } else {
1182 xMarginL = static_cast<int>(rcClient.Width()) - xMarginR - 4;
1185 if (bJump && bEven) {
1186 // Jump is used only in even mode
1187 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1188 } else {
1189 xMoveL = xMoveR = 0; // Not used, avoid a warning
1191 if (pt.x < rcClient.left + xMarginL) {
1192 // Caret is on the left of the display
1193 if (bJump && bEven) {
1194 newXY.xOffset -= xMoveL;
1195 } else {
1196 // Move just enough to allow to display the caret
1197 newXY.xOffset -= static_cast<int>((rcClient.left + xMarginL) - pt.x);
1199 } else if (pt.x >= rcClient.right - xMarginR) {
1200 // Caret is on the right of the display
1201 if (bJump && bEven) {
1202 newXY.xOffset += xMoveR;
1203 } else {
1204 // Move just enough to allow to display the caret
1205 newXY.xOffset += static_cast<int>(pt.x - (rcClient.right - xMarginR) + 1);
1208 } else { // Not strict
1209 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1210 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1211 if (bEven) {
1212 xMoveL = xMoveR;
1213 } else {
1214 xMoveL = static_cast<int>(rcClient.Width()) - xMoveR - 4;
1216 if (pt.x < rcClient.left) {
1217 // Caret is on the left of the display
1218 newXY.xOffset -= xMoveL;
1219 } else if (pt.x >= rcClient.right) {
1220 // Caret is on the right of the display
1221 newXY.xOffset += xMoveR;
1224 } else { // No slop
1225 if (bStrict ||
1226 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1227 // Strict or going out of display
1228 if (bEven) {
1229 // Center caret
1230 newXY.xOffset += static_cast<int>(pt.x - rcClient.left - halfScreen);
1231 } else {
1232 // Put caret on right
1233 newXY.xOffset += static_cast<int>(pt.x - rcClient.right + 1);
1235 } else {
1236 // Move just enough to allow to display the caret
1237 if (pt.x < rcClient.left) {
1238 // Caret is on the left of the display
1239 if (bEven) {
1240 newXY.xOffset -= static_cast<int>(rcClient.left - pt.x);
1241 } else {
1242 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1244 } else if (pt.x >= rcClient.right) {
1245 // Caret is on the right of the display
1246 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1250 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1251 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1252 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 2;
1253 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1254 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 2;
1255 if ((vs.caretStyle == CARETSTYLE_BLOCK) || view.imeCaretBlockOverride) {
1256 // Ensure we can see a good portion of the block caret
1257 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1260 if (!(range.caret == range.anchor)) {
1261 if (ptAnchor.x < pt.x) {
1262 // Shift to left to show anchor or as much of range as possible
1263 int maxOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.left) - 1;
1264 int minOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 1;
1265 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1266 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1267 } else {
1268 // Shift to right to show anchor or as much of range as possible
1269 int minOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.right) + 1;
1270 int maxOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 1;
1271 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1272 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1275 if (newXY.xOffset < 0) {
1276 newXY.xOffset = 0;
1280 return newXY;
1283 void Editor::SetXYScroll(XYScrollPosition newXY) {
1284 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1285 if (newXY.topLine != topLine) {
1286 SetTopLine(newXY.topLine);
1287 SetVerticalScrollPos();
1289 if (newXY.xOffset != xOffset) {
1290 xOffset = newXY.xOffset;
1291 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1292 if (newXY.xOffset > 0) {
1293 PRectangle rcText = GetTextRectangle();
1294 if (horizontalScrollBarVisible &&
1295 rcText.Width() + xOffset > scrollWidth) {
1296 scrollWidth = xOffset + static_cast<int>(rcText.Width());
1297 SetScrollBars();
1300 SetHorizontalScrollPos();
1302 Redraw();
1303 UpdateSystemCaret();
1307 void Editor::ScrollRange(SelectionRange range) {
1308 SetXYScroll(XYScrollToMakeVisible(range, xysDefault));
1311 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1312 SetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret),
1313 static_cast<XYScrollOptions>((useMargin?xysUseMargin:0)|(vert?xysVertical:0)|(horiz?xysHorizontal:0))));
1316 void Editor::ShowCaretAtCurrentPosition() {
1317 if (hasFocus) {
1318 caret.active = true;
1319 caret.on = true;
1320 if (FineTickerAvailable()) {
1321 FineTickerCancel(tickCaret);
1322 if (caret.period > 0)
1323 FineTickerStart(tickCaret, caret.period, caret.period/10);
1324 } else {
1325 SetTicking(true);
1327 } else {
1328 caret.active = false;
1329 caret.on = false;
1330 if (FineTickerAvailable()) {
1331 FineTickerCancel(tickCaret);
1334 InvalidateCaret();
1337 void Editor::DropCaret() {
1338 caret.active = false;
1339 if (FineTickerAvailable()) {
1340 FineTickerCancel(tickCaret);
1342 InvalidateCaret();
1345 void Editor::CaretSetPeriod(int period) {
1346 if (caret.period != period) {
1347 caret.period = period;
1348 caret.on = true;
1349 if (FineTickerAvailable()) {
1350 FineTickerCancel(tickCaret);
1351 if ((caret.active) && (caret.period > 0))
1352 FineTickerStart(tickCaret, caret.period, caret.period/10);
1354 InvalidateCaret();
1358 void Editor::InvalidateCaret() {
1359 if (posDrag.IsValid()) {
1360 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1361 } else {
1362 for (size_t r=0; r<sel.Count(); r++) {
1363 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1366 UpdateSystemCaret();
1369 void Editor::UpdateSystemCaret() {
1372 bool Editor::Wrapping() const {
1373 return vs.wrapState != eWrapNone;
1376 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1377 //Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd);
1378 if (wrapPending.AddRange(docLineStart, docLineEnd)) {
1379 view.llc.Invalidate(LineLayout::llPositions);
1381 // Wrap lines during idle.
1382 if (Wrapping() && wrapPending.NeedsWrap()) {
1383 SetIdle(true);
1387 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1388 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(lineToWrap, *this));
1389 int linesWrapped = 1;
1390 if (ll) {
1391 view.LayoutLine(*this, lineToWrap, surface, vs, ll, wrapWidth);
1392 linesWrapped = ll->lines;
1394 return cs.SetHeight(lineToWrap, linesWrapped +
1395 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1398 // Perform wrapping for a subset of the lines needing wrapping.
1399 // wsAll: wrap all lines which need wrapping in this single call
1400 // wsVisible: wrap currently visible lines
1401 // wsIdle: wrap one page + 100 lines
1402 // Return true if wrapping occurred.
1403 bool Editor::WrapLines(enum wrapScope ws) {
1404 int goodTopLine = topLine;
1405 bool wrapOccurred = false;
1406 if (!Wrapping()) {
1407 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1408 wrapWidth = LineLayout::wrapWidthInfinite;
1409 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1410 cs.SetHeight(lineDoc, 1 +
1411 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1413 wrapOccurred = true;
1415 wrapPending.Reset();
1417 } else if (wrapPending.NeedsWrap()) {
1418 wrapPending.start = std::min(wrapPending.start, pdoc->LinesTotal());
1419 if (!SetIdle(true)) {
1420 // Idle processing not supported so full wrap required.
1421 ws = wsAll;
1423 // Decide where to start wrapping
1424 int lineToWrap = wrapPending.start;
1425 int lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal());
1426 const int lineDocTop = cs.DocFromDisplay(topLine);
1427 const int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1428 if (ws == wsVisible) {
1429 lineToWrap = Platform::Clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal());
1430 // Priority wrap to just after visible area.
1431 // Since wrapping could reduce display lines, treat each
1432 // as taking only one display line.
1433 lineToWrapEnd = lineDocTop;
1434 int lines = LinesOnScreen() + 1;
1435 while ((lineToWrapEnd < cs.LinesInDoc()) && (lines>0)) {
1436 if (cs.GetVisible(lineToWrapEnd))
1437 lines--;
1438 lineToWrapEnd++;
1440 // .. and if the paint window is outside pending wraps
1441 if ((lineToWrap > wrapPending.end) || (lineToWrapEnd < wrapPending.start)) {
1442 // Currently visible text does not need wrapping
1443 return false;
1445 } else if (ws == wsIdle) {
1446 lineToWrapEnd = lineToWrap + LinesOnScreen() + 100;
1448 const int lineEndNeedWrap = std::min(wrapPending.end, pdoc->LinesTotal());
1449 lineToWrapEnd = std::min(lineToWrapEnd, lineEndNeedWrap);
1451 // Ensure all lines being wrapped are styled.
1452 pdoc->EnsureStyledTo(pdoc->LineStart(lineToWrapEnd));
1454 if (lineToWrap < lineToWrapEnd) {
1456 PRectangle rcTextArea = GetClientRectangle();
1457 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1458 rcTextArea.right -= vs.rightMarginWidth;
1459 wrapWidth = static_cast<int>(rcTextArea.Width());
1460 RefreshStyleData();
1461 AutoSurface surface(this);
1462 if (surface) {
1463 //Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
1465 while (lineToWrap < lineToWrapEnd) {
1466 if (WrapOneLine(surface, lineToWrap)) {
1467 wrapOccurred = true;
1469 wrapPending.Wrapped(lineToWrap);
1470 lineToWrap++;
1473 goodTopLine = cs.DisplayFromDoc(lineDocTop) + std::min(subLineTop, cs.GetHeight(lineDocTop)-1);
1477 // If wrapping is done, bring it to resting position
1478 if (wrapPending.start >= lineEndNeedWrap) {
1479 wrapPending.Reset();
1483 if (wrapOccurred) {
1484 SetScrollBars();
1485 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1486 SetVerticalScrollPos();
1489 return wrapOccurred;
1492 void Editor::LinesJoin() {
1493 if (!RangeContainsProtected(targetStart, targetEnd)) {
1494 UndoGroup ug(pdoc);
1495 bool prevNonWS = true;
1496 for (int pos = targetStart; pos < targetEnd; pos++) {
1497 if (pdoc->IsPositionInLineEnd(pos)) {
1498 targetEnd -= pdoc->LenChar(pos);
1499 pdoc->DelChar(pos);
1500 if (prevNonWS) {
1501 // Ensure at least one space separating previous lines
1502 const int lengthInserted = pdoc->InsertString(pos, " ", 1);
1503 targetEnd += lengthInserted;
1505 } else {
1506 prevNonWS = pdoc->CharAt(pos) != ' ';
1512 const char *Editor::StringFromEOLMode(int eolMode) {
1513 if (eolMode == SC_EOL_CRLF) {
1514 return "\r\n";
1515 } else if (eolMode == SC_EOL_CR) {
1516 return "\r";
1517 } else {
1518 return "\n";
1522 void Editor::LinesSplit(int pixelWidth) {
1523 if (!RangeContainsProtected(targetStart, targetEnd)) {
1524 if (pixelWidth == 0) {
1525 PRectangle rcText = GetTextRectangle();
1526 pixelWidth = static_cast<int>(rcText.Width());
1528 int lineStart = pdoc->LineFromPosition(targetStart);
1529 int lineEnd = pdoc->LineFromPosition(targetEnd);
1530 const char *eol = StringFromEOLMode(pdoc->eolMode);
1531 UndoGroup ug(pdoc);
1532 for (int line = lineStart; line <= lineEnd; line++) {
1533 AutoSurface surface(this);
1534 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
1535 if (surface && ll) {
1536 unsigned int posLineStart = pdoc->LineStart(line);
1537 view.LayoutLine(*this, line, surface, vs, ll, pixelWidth);
1538 int lengthInsertedTotal = 0;
1539 for (int subLine = 1; subLine < ll->lines; subLine++) {
1540 const int lengthInserted = pdoc->InsertString(
1541 static_cast<int>(posLineStart + lengthInsertedTotal +
1542 ll->LineStart(subLine)),
1543 eol, istrlen(eol));
1544 targetEnd += lengthInserted;
1545 lengthInsertedTotal += lengthInserted;
1548 lineEnd = pdoc->LineFromPosition(targetEnd);
1553 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1554 if (vs.fixedColumnWidth == 0)
1555 return;
1557 AllocateGraphics();
1558 RefreshStyleData();
1559 RefreshPixMaps(surfWindow);
1561 // On GTK+ with Ubuntu overlay scroll bars, the surface may have been finished
1562 // at this point. The Initialised call checks for this case and sets the status
1563 // to be bad which avoids crashes in following calls.
1564 if (!surfWindow->Initialised()) {
1565 return;
1568 PRectangle rcMargin = GetClientRectangle();
1569 Point ptOrigin = GetVisibleOriginInMain();
1570 rcMargin.Move(0, -ptOrigin.y);
1571 rcMargin.left = 0;
1572 rcMargin.right = static_cast<XYPOSITION>(vs.fixedColumnWidth);
1574 if (!rc.Intersects(rcMargin))
1575 return;
1577 Surface *surface;
1578 if (view.bufferedDraw) {
1579 surface = marginView.pixmapSelMargin;
1580 } else {
1581 surface = surfWindow;
1584 // Clip vertically to paint area to avoid drawing line numbers
1585 if (rcMargin.bottom > rc.bottom)
1586 rcMargin.bottom = rc.bottom;
1587 if (rcMargin.top < rc.top)
1588 rcMargin.top = rc.top;
1590 marginView.PaintMargin(surface, topLine, rc, rcMargin, *this, vs);
1592 if (view.bufferedDraw) {
1593 surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *marginView.pixmapSelMargin);
1597 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
1598 view.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1599 marginView.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1600 if (view.bufferedDraw) {
1601 PRectangle rcClient = GetClientRectangle();
1602 if (!view.pixmapLine->Initialised()) {
1604 view.pixmapLine->InitPixMap(static_cast<int>(rcClient.Width()), vs.lineHeight,
1605 surfaceWindow, wMain.GetID());
1607 if (!marginView.pixmapSelMargin->Initialised()) {
1608 marginView.pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
1609 static_cast<int>(rcClient.Height()), surfaceWindow, wMain.GetID());
1614 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
1615 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
1616 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
1617 AllocateGraphics();
1619 RefreshStyleData();
1620 if (paintState == paintAbandoned)
1621 return; // Scroll bars may have changed so need redraw
1622 RefreshPixMaps(surfaceWindow);
1624 paintAbandonedByStyling = false;
1626 StyleToPositionInView(PositionAfterArea(rcArea));
1628 PRectangle rcClient = GetClientRectangle();
1629 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
1630 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1632 if (NotifyUpdateUI()) {
1633 RefreshStyleData();
1634 RefreshPixMaps(surfaceWindow);
1637 // Wrap the visible lines if needed.
1638 if (WrapLines(wsVisible)) {
1639 // The wrapping process has changed the height of some lines so
1640 // abandon this paint for a complete repaint.
1641 if (AbandonPaint()) {
1642 return;
1644 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
1646 PLATFORM_ASSERT(marginView.pixmapSelPattern->Initialised());
1648 if (!view.bufferedDraw)
1649 surfaceWindow->SetClip(rcArea);
1651 if (paintState != paintAbandoned) {
1652 if (vs.marginInside) {
1653 PaintSelMargin(surfaceWindow, rcArea);
1654 PRectangle rcRightMargin = rcClient;
1655 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
1656 if (rcArea.Intersects(rcRightMargin)) {
1657 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
1659 } else { // Else separate view so separate paint event but leftMargin included to allow overlap
1660 PRectangle rcLeftMargin = rcArea;
1661 rcLeftMargin.left = 0;
1662 rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
1663 if (rcArea.Intersects(rcLeftMargin)) {
1664 surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[STYLE_DEFAULT].back);
1669 if (paintState == paintAbandoned) {
1670 // Either styling or NotifyUpdateUI noticed that painting is needed
1671 // outside the current painting rectangle
1672 //Platform::DebugPrintf("Abandoning paint\n");
1673 if (Wrapping()) {
1674 if (paintAbandonedByStyling) {
1675 // Styling has spilled over a line end, such as occurs by starting a multiline
1676 // comment. The width of subsequent text may have changed, so rewrap.
1677 NeedWrapping(cs.DocFromDisplay(topLine));
1680 return;
1683 view.PaintText(surfaceWindow, *this, rcArea, rcClient, vs);
1685 if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) {
1686 if (FineTickerAvailable()) {
1687 scrollWidth = view.lineWidthMaxSeen;
1688 if (!FineTickerRunning(tickWiden)) {
1689 FineTickerStart(tickWiden, 50, 5);
1694 NotifyPainted();
1697 // This is mostly copied from the Paint method but with some things omitted
1698 // such as the margin markers, line numbers, selection and caret
1699 // Should be merged back into a combined Draw method.
1700 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
1701 if (!pfr)
1702 return 0;
1704 AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
1705 if (!surface)
1706 return 0;
1707 AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
1708 if (!surfaceMeasure) {
1709 return 0;
1711 return view.FormatRange(draw, pfr, surface, surfaceMeasure, *this, vs);
1714 int Editor::TextWidth(int style, const char *text) {
1715 RefreshStyleData();
1716 AutoSurface surface(this);
1717 if (surface) {
1718 return static_cast<int>(surface->WidthText(vs.styles[style].font, text, istrlen(text)));
1719 } else {
1720 return 1;
1724 // Empty method is overridden on GTK+ to show / hide scrollbars
1725 void Editor::ReconfigureScrollBars() {}
1727 void Editor::SetScrollBars() {
1728 RefreshStyleData();
1730 int nMax = MaxScrollPos();
1731 int nPage = LinesOnScreen();
1732 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
1733 if (modified) {
1734 DwellEnd(true);
1737 // TODO: ensure always showing as many lines as possible
1738 // May not be, if, for example, window made larger
1739 if (topLine > MaxScrollPos()) {
1740 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
1741 SetVerticalScrollPos();
1742 Redraw();
1744 if (modified) {
1745 if (!AbandonPaint())
1746 Redraw();
1748 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
1751 void Editor::ChangeSize() {
1752 DropGraphics(false);
1753 SetScrollBars();
1754 if (Wrapping()) {
1755 PRectangle rcTextArea = GetClientRectangle();
1756 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1757 rcTextArea.right -= vs.rightMarginWidth;
1758 if (wrapWidth != rcTextArea.Width()) {
1759 NeedWrapping();
1760 Redraw();
1765 int Editor::InsertSpace(int position, unsigned int spaces) {
1766 if (spaces > 0) {
1767 std::string spaceText(spaces, ' ');
1768 const int lengthInserted = pdoc->InsertString(position, spaceText.c_str(), spaces);
1769 position += lengthInserted;
1771 return position;
1774 void Editor::AddChar(char ch) {
1775 char s[2];
1776 s[0] = ch;
1777 s[1] = '\0';
1778 AddCharUTF(s, 1);
1781 void Editor::FilterSelections() {
1782 if (!additionalSelectionTyping && (sel.Count() > 1)) {
1783 SelectionRange rangeOnly = sel.RangeMain();
1784 InvalidateSelection(rangeOnly, true);
1785 sel.SetSelection(rangeOnly);
1789 static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
1790 return *a < *b;
1793 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
1794 void Editor::AddCharUTF(const char *s, unsigned int len, bool treatAsDBCS) {
1795 FilterSelections();
1797 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
1799 // Vector elements point into selection in order to change selection.
1800 std::vector<SelectionRange *> selPtrs;
1801 for (size_t r = 0; r < sel.Count(); r++) {
1802 selPtrs.push_back(&sel.Range(r));
1804 // Order selections by position in document.
1805 std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
1807 // Loop in reverse to avoid disturbing positions of selections yet to be processed.
1808 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
1809 rit != selPtrs.rend(); ++rit) {
1810 SelectionRange *currentSel = *rit;
1811 if (!RangeContainsProtected(currentSel->Start().Position(),
1812 currentSel->End().Position())) {
1813 int positionInsert = currentSel->Start().Position();
1814 if (!currentSel->Empty()) {
1815 if (currentSel->Length()) {
1816 pdoc->DeleteChars(positionInsert, currentSel->Length());
1817 currentSel->ClearVirtualSpace();
1818 } else {
1819 // Range is all virtual so collapse to start of virtual space
1820 currentSel->MinimizeVirtualSpace();
1822 } else if (inOverstrike) {
1823 if (positionInsert < pdoc->Length()) {
1824 if (!pdoc->IsPositionInLineEnd(positionInsert)) {
1825 pdoc->DelChar(positionInsert);
1826 currentSel->ClearVirtualSpace();
1830 positionInsert = InsertSpace(positionInsert, currentSel->caret.VirtualSpace());
1831 const int lengthInserted = pdoc->InsertString(positionInsert, s, len);
1832 if (lengthInserted > 0) {
1833 currentSel->caret.SetPosition(positionInsert + lengthInserted);
1834 currentSel->anchor.SetPosition(positionInsert + lengthInserted);
1836 currentSel->ClearVirtualSpace();
1837 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1838 if (Wrapping()) {
1839 AutoSurface surface(this);
1840 if (surface) {
1841 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
1842 SetScrollBars();
1843 SetVerticalScrollPos();
1844 Redraw();
1851 if (Wrapping()) {
1852 SetScrollBars();
1854 ThinRectangularRange();
1855 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1856 EnsureCaretVisible();
1857 // Avoid blinking during rapid typing:
1858 ShowCaretAtCurrentPosition();
1859 if ((caretSticky == SC_CARETSTICKY_OFF) ||
1860 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
1861 SetLastXChosen();
1864 if (treatAsDBCS) {
1865 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
1866 static_cast<unsigned char>(s[1]));
1867 } else if (len > 0) {
1868 int byte = static_cast<unsigned char>(s[0]);
1869 if ((byte < 0xC0) || (1 == len)) {
1870 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
1871 // characters when not in UTF-8 mode.
1872 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
1873 // characters representing themselves.
1874 } else {
1875 unsigned int utf32[1] = { 0 };
1876 UTF32FromUTF8(s, len, utf32, ELEMENTS(utf32));
1877 byte = utf32[0];
1879 NotifyChar(byte);
1882 if (recordingMacro) {
1883 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
1887 void Editor::FillVirtualSpace() {
1888 const bool tmpOverstrike = inOverstrike;
1889 inOverstrike = false; // not allow to be deleted twice.
1890 AddCharUTF("", 0);
1891 inOverstrike = tmpOverstrike;
1894 void Editor::InsertPaste(const char *text, int len) {
1895 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
1896 SelectionPosition selStart = sel.Start();
1897 selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
1898 const int lengthInserted = pdoc->InsertString(selStart.Position(), text, len);
1899 if (lengthInserted > 0) {
1900 SetEmptySelection(selStart.Position() + lengthInserted);
1902 } else {
1903 // SC_MULTIPASTE_EACH
1904 for (size_t r=0; r<sel.Count(); r++) {
1905 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
1906 sel.Range(r).End().Position())) {
1907 int positionInsert = sel.Range(r).Start().Position();
1908 if (!sel.Range(r).Empty()) {
1909 if (sel.Range(r).Length()) {
1910 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
1911 sel.Range(r).ClearVirtualSpace();
1912 } else {
1913 // Range is all virtual so collapse to start of virtual space
1914 sel.Range(r).MinimizeVirtualSpace();
1917 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
1918 const int lengthInserted = pdoc->InsertString(positionInsert, text, len);
1919 if (lengthInserted > 0) {
1920 sel.Range(r).caret.SetPosition(positionInsert + lengthInserted);
1921 sel.Range(r).anchor.SetPosition(positionInsert + lengthInserted);
1923 sel.Range(r).ClearVirtualSpace();
1929 void Editor::InsertPasteShape(const char *text, int len, PasteShape shape) {
1930 std::string convertedText;
1931 if (convertPastes) {
1932 // Convert line endings of the paste into our local line-endings mode
1933 convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
1934 len = static_cast<int>(convertedText.length());
1935 text = convertedText.c_str();
1937 if (shape == pasteRectangular) {
1938 PasteRectangular(sel.Start(), text, len);
1939 } else {
1940 if (shape == pasteLine) {
1941 int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
1942 int lengthInserted = pdoc->InsertString(insertPos, text, len);
1943 // add the newline if necessary
1944 if ((len > 0) && (text[len - 1] != '\n' && text[len - 1] != '\r')) {
1945 const char *endline = StringFromEOLMode(pdoc->eolMode);
1946 int length = static_cast<int>(strlen(endline));
1947 lengthInserted += pdoc->InsertString(insertPos + lengthInserted, endline, length);
1949 if (sel.MainCaret() == insertPos) {
1950 SetEmptySelection(sel.MainCaret() + lengthInserted);
1952 } else {
1953 InsertPaste(text, len);
1958 void Editor::ClearSelection(bool retainMultipleSelections) {
1959 if (!sel.IsRectangular() && !retainMultipleSelections)
1960 FilterSelections();
1961 UndoGroup ug(pdoc);
1962 for (size_t r=0; r<sel.Count(); r++) {
1963 if (!sel.Range(r).Empty()) {
1964 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
1965 sel.Range(r).End().Position())) {
1966 pdoc->DeleteChars(sel.Range(r).Start().Position(),
1967 sel.Range(r).Length());
1968 sel.Range(r) = SelectionRange(sel.Range(r).Start());
1972 ThinRectangularRange();
1973 sel.RemoveDuplicates();
1974 ClaimSelection();
1975 SetHoverIndicatorPosition(sel.MainCaret());
1978 void Editor::ClearAll() {
1980 UndoGroup ug(pdoc);
1981 if (0 != pdoc->Length()) {
1982 pdoc->DeleteChars(0, pdoc->Length());
1984 if (!pdoc->IsReadOnly()) {
1985 cs.Clear();
1986 pdoc->AnnotationClearAll();
1987 pdoc->MarginClearAll();
1991 view.ClearAllTabstops();
1993 sel.Clear();
1994 SetTopLine(0);
1995 SetVerticalScrollPos();
1996 InvalidateStyleRedraw();
1999 void Editor::ClearDocumentStyle() {
2000 Decoration *deco = pdoc->decorations.root;
2001 while (deco) {
2002 // Save next in case deco deleted
2003 Decoration *decoNext = deco->next;
2004 if (deco->indicator < INDIC_CONTAINER) {
2005 pdoc->decorations.SetCurrentIndicator(deco->indicator);
2006 pdoc->DecorationFillRange(0, 0, pdoc->Length());
2008 deco = decoNext;
2010 pdoc->StartStyling(0, '\377');
2011 pdoc->SetStyleFor(pdoc->Length(), 0);
2012 cs.ShowAll();
2013 SetAnnotationHeights(0, pdoc->LinesTotal());
2014 pdoc->ClearLevels();
2017 void Editor::CopyAllowLine() {
2018 SelectionText selectedText;
2019 CopySelectionRange(&selectedText, true);
2020 CopyToClipboard(selectedText);
2023 void Editor::Cut() {
2024 pdoc->CheckReadOnly();
2025 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
2026 Copy();
2027 ClearSelection();
2031 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
2032 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
2033 return;
2035 sel.Clear();
2036 sel.RangeMain() = SelectionRange(pos);
2037 int line = pdoc->LineFromPosition(sel.MainCaret());
2038 UndoGroup ug(pdoc);
2039 sel.RangeMain().caret = SelectionPosition(
2040 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
2041 int xInsert = XFromPosition(sel.RangeMain().caret);
2042 bool prevCr = false;
2043 while ((len > 0) && IsEOLChar(ptr[len-1]))
2044 len--;
2045 for (int i = 0; i < len; i++) {
2046 if (IsEOLChar(ptr[i])) {
2047 if ((ptr[i] == '\r') || (!prevCr))
2048 line++;
2049 if (line >= pdoc->LinesTotal()) {
2050 if (pdoc->eolMode != SC_EOL_LF)
2051 pdoc->InsertString(pdoc->Length(), "\r", 1);
2052 if (pdoc->eolMode != SC_EOL_CR)
2053 pdoc->InsertString(pdoc->Length(), "\n", 1);
2055 // Pad the end of lines with spaces if required
2056 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
2057 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
2058 while (XFromPosition(sel.MainCaret()) < xInsert) {
2059 assert(pdoc);
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 RefreshStyleData();
6231 return vs.lineHeight;
6233 case SCI_SETENDATLASTLINE:
6234 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
6235 if (endAtLastLine != (wParam != 0)) {
6236 endAtLastLine = wParam != 0;
6237 SetScrollBars();
6239 break;
6241 case SCI_GETENDATLASTLINE:
6242 return endAtLastLine;
6244 case SCI_SETCARETSTICKY:
6245 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
6246 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
6247 caretSticky = static_cast<int>(wParam);
6249 break;
6251 case SCI_GETCARETSTICKY:
6252 return caretSticky;
6254 case SCI_TOGGLECARETSTICKY:
6255 caretSticky = !caretSticky;
6256 break;
6258 case SCI_GETCOLUMN:
6259 return pdoc->GetColumn(static_cast<int>(wParam));
6261 case SCI_FINDCOLUMN:
6262 return pdoc->FindColumn(static_cast<int>(wParam), static_cast<int>(lParam));
6264 case SCI_SETHSCROLLBAR :
6265 if (horizontalScrollBarVisible != (wParam != 0)) {
6266 horizontalScrollBarVisible = wParam != 0;
6267 SetScrollBars();
6268 ReconfigureScrollBars();
6270 break;
6272 case SCI_GETHSCROLLBAR:
6273 return horizontalScrollBarVisible;
6275 case SCI_SETVSCROLLBAR:
6276 if (verticalScrollBarVisible != (wParam != 0)) {
6277 verticalScrollBarVisible = wParam != 0;
6278 SetScrollBars();
6279 ReconfigureScrollBars();
6280 if (verticalScrollBarVisible)
6281 SetVerticalScrollPos();
6283 break;
6285 case SCI_GETVSCROLLBAR:
6286 return verticalScrollBarVisible;
6288 case SCI_SETINDENTATIONGUIDES:
6289 vs.viewIndentationGuides = IndentView(wParam);
6290 Redraw();
6291 break;
6293 case SCI_GETINDENTATIONGUIDES:
6294 return vs.viewIndentationGuides;
6296 case SCI_SETHIGHLIGHTGUIDE:
6297 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
6298 highlightGuideColumn = static_cast<int>(wParam);
6299 Redraw();
6301 break;
6303 case SCI_GETHIGHLIGHTGUIDE:
6304 return highlightGuideColumn;
6306 case SCI_GETLINEENDPOSITION:
6307 return pdoc->LineEnd(static_cast<int>(wParam));
6309 case SCI_SETCODEPAGE:
6310 if (ValidCodePage(static_cast<int>(wParam))) {
6311 if (pdoc->SetDBCSCodePage(static_cast<int>(wParam))) {
6312 cs.Clear();
6313 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6314 SetAnnotationHeights(0, pdoc->LinesTotal());
6315 InvalidateStyleRedraw();
6316 SetRepresentations();
6319 break;
6321 case SCI_GETCODEPAGE:
6322 return pdoc->dbcsCodePage;
6324 case SCI_SETIMEINTERACTION:
6325 imeInteraction = static_cast<EditModel::IMEInteraction>(wParam);
6326 break;
6328 case SCI_GETIMEINTERACTION:
6329 return imeInteraction;
6331 #ifdef INCLUDE_DEPRECATED_FEATURES
6332 case SCI_SETUSEPALETTE:
6333 InvalidateStyleRedraw();
6334 break;
6336 case SCI_GETUSEPALETTE:
6337 return 0;
6338 #endif
6340 // Marker definition and setting
6341 case SCI_MARKERDEFINE:
6342 if (wParam <= MARKER_MAX) {
6343 vs.markers[wParam].markType = static_cast<int>(lParam);
6344 vs.CalcLargestMarkerHeight();
6346 InvalidateStyleData();
6347 RedrawSelMargin();
6348 break;
6350 case SCI_MARKERSYMBOLDEFINED:
6351 if (wParam <= MARKER_MAX)
6352 return vs.markers[wParam].markType;
6353 else
6354 return 0;
6356 case SCI_MARKERSETFORE:
6357 if (wParam <= MARKER_MAX)
6358 vs.markers[wParam].fore = ColourDesired(static_cast<long>(lParam));
6359 InvalidateStyleData();
6360 RedrawSelMargin();
6361 break;
6362 case SCI_MARKERSETBACKSELECTED:
6363 if (wParam <= MARKER_MAX)
6364 vs.markers[wParam].backSelected = ColourDesired(static_cast<long>(lParam));
6365 InvalidateStyleData();
6366 RedrawSelMargin();
6367 break;
6368 case SCI_MARKERENABLEHIGHLIGHT:
6369 marginView.highlightDelimiter.isEnabled = wParam == 1;
6370 RedrawSelMargin();
6371 break;
6372 case SCI_MARKERSETBACK:
6373 if (wParam <= MARKER_MAX)
6374 vs.markers[wParam].back = ColourDesired(static_cast<long>(lParam));
6375 InvalidateStyleData();
6376 RedrawSelMargin();
6377 break;
6378 case SCI_MARKERSETALPHA:
6379 if (wParam <= MARKER_MAX)
6380 vs.markers[wParam].alpha = static_cast<int>(lParam);
6381 InvalidateStyleRedraw();
6382 break;
6383 case SCI_MARKERADD: {
6384 int markerID = pdoc->AddMark(static_cast<int>(wParam), static_cast<int>(lParam));
6385 return markerID;
6387 case SCI_MARKERADDSET:
6388 if (lParam != 0)
6389 pdoc->AddMarkSet(static_cast<int>(wParam), static_cast<int>(lParam));
6390 break;
6392 case SCI_MARKERDELETE:
6393 pdoc->DeleteMark(static_cast<int>(wParam), static_cast<int>(lParam));
6394 break;
6396 case SCI_MARKERDELETEALL:
6397 pdoc->DeleteAllMarks(static_cast<int>(wParam));
6398 break;
6400 case SCI_MARKERGET:
6401 return pdoc->GetMark(static_cast<int>(wParam));
6403 case SCI_MARKERNEXT:
6404 return pdoc->MarkerNext(static_cast<int>(wParam), static_cast<int>(lParam));
6406 case SCI_MARKERPREVIOUS: {
6407 for (int iLine = static_cast<int>(wParam); iLine >= 0; iLine--) {
6408 if ((pdoc->GetMark(iLine) & lParam) != 0)
6409 return iLine;
6412 return -1;
6414 case SCI_MARKERDEFINEPIXMAP:
6415 if (wParam <= MARKER_MAX) {
6416 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
6417 vs.CalcLargestMarkerHeight();
6419 InvalidateStyleData();
6420 RedrawSelMargin();
6421 break;
6423 case SCI_RGBAIMAGESETWIDTH:
6424 sizeRGBAImage.x = static_cast<XYPOSITION>(wParam);
6425 break;
6427 case SCI_RGBAIMAGESETHEIGHT:
6428 sizeRGBAImage.y = static_cast<XYPOSITION>(wParam);
6429 break;
6431 case SCI_RGBAIMAGESETSCALE:
6432 scaleRGBAImage = static_cast<float>(wParam);
6433 break;
6435 case SCI_MARKERDEFINERGBAIMAGE:
6436 if (wParam <= MARKER_MAX) {
6437 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0f, reinterpret_cast<unsigned char *>(lParam));
6438 vs.CalcLargestMarkerHeight();
6440 InvalidateStyleData();
6441 RedrawSelMargin();
6442 break;
6444 case SCI_SETMARGINTYPEN:
6445 if (ValidMargin(wParam)) {
6446 vs.ms[wParam].style = static_cast<int>(lParam);
6447 InvalidateStyleRedraw();
6449 break;
6451 case SCI_GETMARGINTYPEN:
6452 if (ValidMargin(wParam))
6453 return vs.ms[wParam].style;
6454 else
6455 return 0;
6457 case SCI_SETMARGINWIDTHN:
6458 if (ValidMargin(wParam)) {
6459 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
6460 if (vs.ms[wParam].width != lParam) {
6461 lastXChosen += static_cast<int>(lParam) - vs.ms[wParam].width;
6462 vs.ms[wParam].width = static_cast<int>(lParam);
6463 InvalidateStyleRedraw();
6466 break;
6468 case SCI_GETMARGINWIDTHN:
6469 if (ValidMargin(wParam))
6470 return vs.ms[wParam].width;
6471 else
6472 return 0;
6474 case SCI_SETMARGINMASKN:
6475 if (ValidMargin(wParam)) {
6476 vs.ms[wParam].mask = static_cast<int>(lParam);
6477 InvalidateStyleRedraw();
6479 break;
6481 case SCI_GETMARGINMASKN:
6482 if (ValidMargin(wParam))
6483 return vs.ms[wParam].mask;
6484 else
6485 return 0;
6487 case SCI_SETMARGINSENSITIVEN:
6488 if (ValidMargin(wParam)) {
6489 vs.ms[wParam].sensitive = lParam != 0;
6490 InvalidateStyleRedraw();
6492 break;
6494 case SCI_GETMARGINSENSITIVEN:
6495 if (ValidMargin(wParam))
6496 return vs.ms[wParam].sensitive ? 1 : 0;
6497 else
6498 return 0;
6500 case SCI_SETMARGINCURSORN:
6501 if (ValidMargin(wParam))
6502 vs.ms[wParam].cursor = static_cast<int>(lParam);
6503 break;
6505 case SCI_GETMARGINCURSORN:
6506 if (ValidMargin(wParam))
6507 return vs.ms[wParam].cursor;
6508 else
6509 return 0;
6511 case SCI_STYLECLEARALL:
6512 vs.ClearStyles();
6513 InvalidateStyleRedraw();
6514 break;
6516 case SCI_STYLESETFORE:
6517 case SCI_STYLESETBACK:
6518 case SCI_STYLESETBOLD:
6519 case SCI_STYLESETWEIGHT:
6520 case SCI_STYLESETITALIC:
6521 case SCI_STYLESETEOLFILLED:
6522 case SCI_STYLESETSIZE:
6523 case SCI_STYLESETSIZEFRACTIONAL:
6524 case SCI_STYLESETFONT:
6525 case SCI_STYLESETUNDERLINE:
6526 case SCI_STYLESETCASE:
6527 case SCI_STYLESETCHARACTERSET:
6528 case SCI_STYLESETVISIBLE:
6529 case SCI_STYLESETCHANGEABLE:
6530 case SCI_STYLESETHOTSPOT:
6531 StyleSetMessage(iMessage, wParam, lParam);
6532 break;
6534 case SCI_STYLEGETFORE:
6535 case SCI_STYLEGETBACK:
6536 case SCI_STYLEGETBOLD:
6537 case SCI_STYLEGETWEIGHT:
6538 case SCI_STYLEGETITALIC:
6539 case SCI_STYLEGETEOLFILLED:
6540 case SCI_STYLEGETSIZE:
6541 case SCI_STYLEGETSIZEFRACTIONAL:
6542 case SCI_STYLEGETFONT:
6543 case SCI_STYLEGETUNDERLINE:
6544 case SCI_STYLEGETCASE:
6545 case SCI_STYLEGETCHARACTERSET:
6546 case SCI_STYLEGETVISIBLE:
6547 case SCI_STYLEGETCHANGEABLE:
6548 case SCI_STYLEGETHOTSPOT:
6549 return StyleGetMessage(iMessage, wParam, lParam);
6551 case SCI_STYLERESETDEFAULT:
6552 vs.ResetDefaultStyle();
6553 InvalidateStyleRedraw();
6554 break;
6555 case SCI_SETSTYLEBITS:
6556 vs.EnsureStyle(0xff);
6557 break;
6559 case SCI_GETSTYLEBITS:
6560 return 8;
6562 case SCI_SETLINESTATE:
6563 return pdoc->SetLineState(static_cast<int>(wParam), static_cast<int>(lParam));
6565 case SCI_GETLINESTATE:
6566 return pdoc->GetLineState(static_cast<int>(wParam));
6568 case SCI_GETMAXLINESTATE:
6569 return pdoc->GetMaxLineState();
6571 case SCI_GETCARETLINEVISIBLE:
6572 return vs.showCaretLineBackground;
6573 case SCI_SETCARETLINEVISIBLE:
6574 vs.showCaretLineBackground = wParam != 0;
6575 InvalidateStyleRedraw();
6576 break;
6577 case SCI_GETCARETLINEVISIBLEALWAYS:
6578 return vs.alwaysShowCaretLineBackground;
6579 case SCI_SETCARETLINEVISIBLEALWAYS:
6580 vs.alwaysShowCaretLineBackground = wParam != 0;
6581 InvalidateStyleRedraw();
6582 break;
6584 case SCI_GETCARETLINEBACK:
6585 return vs.caretLineBackground.AsLong();
6586 case SCI_SETCARETLINEBACK:
6587 vs.caretLineBackground = static_cast<int>(wParam);
6588 InvalidateStyleRedraw();
6589 break;
6590 case SCI_GETCARETLINEBACKALPHA:
6591 return vs.caretLineAlpha;
6592 case SCI_SETCARETLINEBACKALPHA:
6593 vs.caretLineAlpha = static_cast<int>(wParam);
6594 InvalidateStyleRedraw();
6595 break;
6597 // Folding messages
6599 case SCI_VISIBLEFROMDOCLINE:
6600 return cs.DisplayFromDoc(static_cast<int>(wParam));
6602 case SCI_DOCLINEFROMVISIBLE:
6603 return cs.DocFromDisplay(static_cast<int>(wParam));
6605 case SCI_WRAPCOUNT:
6606 return WrapCount(static_cast<int>(wParam));
6608 case SCI_SETFOLDLEVEL: {
6609 int prev = pdoc->SetLevel(static_cast<int>(wParam), static_cast<int>(lParam));
6610 if (prev != static_cast<int>(lParam))
6611 RedrawSelMargin();
6612 return prev;
6615 case SCI_GETFOLDLEVEL:
6616 return pdoc->GetLevel(static_cast<int>(wParam));
6618 case SCI_GETLASTCHILD:
6619 return pdoc->GetLastChild(static_cast<int>(wParam), static_cast<int>(lParam));
6621 case SCI_GETFOLDPARENT:
6622 return pdoc->GetFoldParent(static_cast<int>(wParam));
6624 case SCI_SHOWLINES:
6625 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), true);
6626 SetScrollBars();
6627 Redraw();
6628 break;
6630 case SCI_HIDELINES:
6631 if (wParam > 0)
6632 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), false);
6633 SetScrollBars();
6634 Redraw();
6635 break;
6637 case SCI_GETLINEVISIBLE:
6638 return cs.GetVisible(static_cast<int>(wParam));
6640 case SCI_GETALLLINESVISIBLE:
6641 return cs.HiddenLines() ? 0 : 1;
6643 case SCI_SETFOLDEXPANDED:
6644 SetFoldExpanded(static_cast<int>(wParam), lParam != 0);
6645 break;
6647 case SCI_GETFOLDEXPANDED:
6648 return cs.GetExpanded(static_cast<int>(wParam));
6650 case SCI_SETAUTOMATICFOLD:
6651 foldAutomatic = static_cast<int>(wParam);
6652 break;
6654 case SCI_GETAUTOMATICFOLD:
6655 return foldAutomatic;
6657 case SCI_SETFOLDFLAGS:
6658 foldFlags = static_cast<int>(wParam);
6659 Redraw();
6660 break;
6662 case SCI_TOGGLEFOLD:
6663 FoldLine(static_cast<int>(wParam), SC_FOLDACTION_TOGGLE);
6664 break;
6666 case SCI_FOLDLINE:
6667 FoldLine(static_cast<int>(wParam), static_cast<int>(lParam));
6668 break;
6670 case SCI_FOLDCHILDREN:
6671 FoldExpand(static_cast<int>(wParam), static_cast<int>(lParam), pdoc->GetLevel(static_cast<int>(wParam)));
6672 break;
6674 case SCI_FOLDALL:
6675 FoldAll(static_cast<int>(wParam));
6676 break;
6678 case SCI_EXPANDCHILDREN:
6679 FoldExpand(static_cast<int>(wParam), SC_FOLDACTION_EXPAND, static_cast<int>(lParam));
6680 break;
6682 case SCI_CONTRACTEDFOLDNEXT:
6683 return ContractedFoldNext(static_cast<int>(wParam));
6685 case SCI_ENSUREVISIBLE:
6686 EnsureLineVisible(static_cast<int>(wParam), false);
6687 break;
6689 case SCI_ENSUREVISIBLEENFORCEPOLICY:
6690 EnsureLineVisible(static_cast<int>(wParam), true);
6691 break;
6693 case SCI_SCROLLRANGE:
6694 ScrollRange(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
6695 break;
6697 case SCI_SEARCHANCHOR:
6698 SearchAnchor();
6699 break;
6701 case SCI_SEARCHNEXT:
6702 case SCI_SEARCHPREV:
6703 return SearchText(iMessage, wParam, lParam);
6705 case SCI_SETXCARETPOLICY:
6706 caretXPolicy = static_cast<int>(wParam);
6707 caretXSlop = static_cast<int>(lParam);
6708 break;
6710 case SCI_SETYCARETPOLICY:
6711 caretYPolicy = static_cast<int>(wParam);
6712 caretYSlop = static_cast<int>(lParam);
6713 break;
6715 case SCI_SETVISIBLEPOLICY:
6716 visiblePolicy = static_cast<int>(wParam);
6717 visibleSlop = static_cast<int>(lParam);
6718 break;
6720 case SCI_LINESONSCREEN:
6721 return LinesOnScreen();
6723 case SCI_SETSELFORE:
6724 vs.selColours.fore = ColourOptional(wParam, lParam);
6725 vs.selAdditionalForeground = ColourDesired(static_cast<long>(lParam));
6726 InvalidateStyleRedraw();
6727 break;
6729 case SCI_SETSELBACK:
6730 vs.selColours.back = ColourOptional(wParam, lParam);
6731 vs.selAdditionalBackground = ColourDesired(static_cast<long>(lParam));
6732 InvalidateStyleRedraw();
6733 break;
6735 case SCI_SETSELALPHA:
6736 vs.selAlpha = static_cast<int>(wParam);
6737 vs.selAdditionalAlpha = static_cast<int>(wParam);
6738 InvalidateStyleRedraw();
6739 break;
6741 case SCI_GETSELALPHA:
6742 return vs.selAlpha;
6744 case SCI_GETSELEOLFILLED:
6745 return vs.selEOLFilled;
6747 case SCI_SETSELEOLFILLED:
6748 vs.selEOLFilled = wParam != 0;
6749 InvalidateStyleRedraw();
6750 break;
6752 case SCI_SETWHITESPACEFORE:
6753 vs.whitespaceColours.fore = ColourOptional(wParam, lParam);
6754 InvalidateStyleRedraw();
6755 break;
6757 case SCI_SETWHITESPACEBACK:
6758 vs.whitespaceColours.back = ColourOptional(wParam, lParam);
6759 InvalidateStyleRedraw();
6760 break;
6762 case SCI_SETCARETFORE:
6763 vs.caretcolour = ColourDesired(static_cast<long>(wParam));
6764 InvalidateStyleRedraw();
6765 break;
6767 case SCI_GETCARETFORE:
6768 return vs.caretcolour.AsLong();
6770 case SCI_SETCARETSTYLE:
6771 if (wParam <= CARETSTYLE_BLOCK)
6772 vs.caretStyle = static_cast<int>(wParam);
6773 else
6774 /* Default to the line caret */
6775 vs.caretStyle = CARETSTYLE_LINE;
6776 InvalidateStyleRedraw();
6777 break;
6779 case SCI_GETCARETSTYLE:
6780 return vs.caretStyle;
6782 case SCI_SETCARETWIDTH:
6783 if (static_cast<int>(wParam) <= 0)
6784 vs.caretWidth = 0;
6785 else if (wParam >= 3)
6786 vs.caretWidth = 3;
6787 else
6788 vs.caretWidth = static_cast<int>(wParam);
6789 InvalidateStyleRedraw();
6790 break;
6792 case SCI_GETCARETWIDTH:
6793 return vs.caretWidth;
6795 case SCI_ASSIGNCMDKEY:
6796 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
6797 Platform::HighShortFromLong(static_cast<long>(wParam)), static_cast<unsigned int>(lParam));
6798 break;
6800 case SCI_CLEARCMDKEY:
6801 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
6802 Platform::HighShortFromLong(static_cast<long>(wParam)), SCI_NULL);
6803 break;
6805 case SCI_CLEARALLCMDKEYS:
6806 kmap.Clear();
6807 break;
6809 case SCI_INDICSETSTYLE:
6810 if (wParam <= INDIC_MAX) {
6811 vs.indicators[wParam].sacNormal.style = static_cast<int>(lParam);
6812 vs.indicators[wParam].sacHover.style = static_cast<int>(lParam);
6813 InvalidateStyleRedraw();
6815 break;
6817 case SCI_INDICGETSTYLE:
6818 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacNormal.style : 0;
6820 case SCI_INDICSETFORE:
6821 if (wParam <= INDIC_MAX) {
6822 vs.indicators[wParam].sacNormal.fore = ColourDesired(static_cast<long>(lParam));
6823 vs.indicators[wParam].sacHover.fore = ColourDesired(static_cast<long>(lParam));
6824 InvalidateStyleRedraw();
6826 break;
6828 case SCI_INDICGETFORE:
6829 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacNormal.fore.AsLong() : 0;
6831 case SCI_INDICSETHOVERSTYLE:
6832 if (wParam <= INDIC_MAX) {
6833 vs.indicators[wParam].sacHover.style = static_cast<int>(lParam);
6834 InvalidateStyleRedraw();
6836 break;
6838 case SCI_INDICGETHOVERSTYLE:
6839 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacHover.style : 0;
6841 case SCI_INDICSETHOVERFORE:
6842 if (wParam <= INDIC_MAX) {
6843 vs.indicators[wParam].sacHover.fore = ColourDesired(static_cast<long>(lParam));
6844 InvalidateStyleRedraw();
6846 break;
6848 case SCI_INDICGETHOVERFORE:
6849 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].sacHover.fore.AsLong() : 0;
6851 case SCI_INDICSETFLAGS:
6852 if (wParam <= INDIC_MAX) {
6853 vs.indicators[wParam].SetFlags(static_cast<int>(lParam));
6854 InvalidateStyleRedraw();
6856 break;
6858 case SCI_INDICGETFLAGS:
6859 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].Flags() : 0;
6861 case SCI_INDICSETUNDER:
6862 if (wParam <= INDIC_MAX) {
6863 vs.indicators[wParam].under = lParam != 0;
6864 InvalidateStyleRedraw();
6866 break;
6868 case SCI_INDICGETUNDER:
6869 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
6871 case SCI_INDICSETALPHA:
6872 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
6873 vs.indicators[wParam].fillAlpha = static_cast<int>(lParam);
6874 InvalidateStyleRedraw();
6876 break;
6878 case SCI_INDICGETALPHA:
6879 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
6881 case SCI_INDICSETOUTLINEALPHA:
6882 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
6883 vs.indicators[wParam].outlineAlpha = static_cast<int>(lParam);
6884 InvalidateStyleRedraw();
6886 break;
6888 case SCI_INDICGETOUTLINEALPHA:
6889 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
6891 case SCI_SETINDICATORCURRENT:
6892 pdoc->decorations.SetCurrentIndicator(static_cast<int>(wParam));
6893 break;
6894 case SCI_GETINDICATORCURRENT:
6895 return pdoc->decorations.GetCurrentIndicator();
6896 case SCI_SETINDICATORVALUE:
6897 pdoc->decorations.SetCurrentValue(static_cast<int>(wParam));
6898 break;
6899 case SCI_GETINDICATORVALUE:
6900 return pdoc->decorations.GetCurrentValue();
6902 case SCI_INDICATORFILLRANGE:
6903 pdoc->DecorationFillRange(static_cast<int>(wParam), pdoc->decorations.GetCurrentValue(), static_cast<int>(lParam));
6904 break;
6906 case SCI_INDICATORCLEARRANGE:
6907 pdoc->DecorationFillRange(static_cast<int>(wParam), 0, static_cast<int>(lParam));
6908 break;
6910 case SCI_INDICATORALLONFOR:
6911 return pdoc->decorations.AllOnFor(static_cast<int>(wParam));
6913 case SCI_INDICATORVALUEAT:
6914 return pdoc->decorations.ValueAt(static_cast<int>(wParam), static_cast<int>(lParam));
6916 case SCI_INDICATORSTART:
6917 return pdoc->decorations.Start(static_cast<int>(wParam), static_cast<int>(lParam));
6919 case SCI_INDICATOREND:
6920 return pdoc->decorations.End(static_cast<int>(wParam), static_cast<int>(lParam));
6922 case SCI_LINEDOWN:
6923 case SCI_LINEDOWNEXTEND:
6924 case SCI_PARADOWN:
6925 case SCI_PARADOWNEXTEND:
6926 case SCI_LINEUP:
6927 case SCI_LINEUPEXTEND:
6928 case SCI_PARAUP:
6929 case SCI_PARAUPEXTEND:
6930 case SCI_CHARLEFT:
6931 case SCI_CHARLEFTEXTEND:
6932 case SCI_CHARRIGHT:
6933 case SCI_CHARRIGHTEXTEND:
6934 case SCI_WORDLEFT:
6935 case SCI_WORDLEFTEXTEND:
6936 case SCI_WORDRIGHT:
6937 case SCI_WORDRIGHTEXTEND:
6938 case SCI_WORDLEFTEND:
6939 case SCI_WORDLEFTENDEXTEND:
6940 case SCI_WORDRIGHTEND:
6941 case SCI_WORDRIGHTENDEXTEND:
6942 case SCI_HOME:
6943 case SCI_HOMEEXTEND:
6944 case SCI_LINEEND:
6945 case SCI_LINEENDEXTEND:
6946 case SCI_HOMEWRAP:
6947 case SCI_HOMEWRAPEXTEND:
6948 case SCI_LINEENDWRAP:
6949 case SCI_LINEENDWRAPEXTEND:
6950 case SCI_DOCUMENTSTART:
6951 case SCI_DOCUMENTSTARTEXTEND:
6952 case SCI_DOCUMENTEND:
6953 case SCI_DOCUMENTENDEXTEND:
6954 case SCI_SCROLLTOSTART:
6955 case SCI_SCROLLTOEND:
6957 case SCI_STUTTEREDPAGEUP:
6958 case SCI_STUTTEREDPAGEUPEXTEND:
6959 case SCI_STUTTEREDPAGEDOWN:
6960 case SCI_STUTTEREDPAGEDOWNEXTEND:
6962 case SCI_PAGEUP:
6963 case SCI_PAGEUPEXTEND:
6964 case SCI_PAGEDOWN:
6965 case SCI_PAGEDOWNEXTEND:
6966 case SCI_EDITTOGGLEOVERTYPE:
6967 case SCI_CANCEL:
6968 case SCI_DELETEBACK:
6969 case SCI_TAB:
6970 case SCI_BACKTAB:
6971 case SCI_NEWLINE:
6972 case SCI_FORMFEED:
6973 case SCI_VCHOME:
6974 case SCI_VCHOMEEXTEND:
6975 case SCI_VCHOMEWRAP:
6976 case SCI_VCHOMEWRAPEXTEND:
6977 case SCI_VCHOMEDISPLAY:
6978 case SCI_VCHOMEDISPLAYEXTEND:
6979 case SCI_ZOOMIN:
6980 case SCI_ZOOMOUT:
6981 case SCI_DELWORDLEFT:
6982 case SCI_DELWORDRIGHT:
6983 case SCI_DELWORDRIGHTEND:
6984 case SCI_DELLINELEFT:
6985 case SCI_DELLINERIGHT:
6986 case SCI_LINECOPY:
6987 case SCI_LINECUT:
6988 case SCI_LINEDELETE:
6989 case SCI_LINETRANSPOSE:
6990 case SCI_LINEDUPLICATE:
6991 case SCI_LOWERCASE:
6992 case SCI_UPPERCASE:
6993 case SCI_LINESCROLLDOWN:
6994 case SCI_LINESCROLLUP:
6995 case SCI_WORDPARTLEFT:
6996 case SCI_WORDPARTLEFTEXTEND:
6997 case SCI_WORDPARTRIGHT:
6998 case SCI_WORDPARTRIGHTEXTEND:
6999 case SCI_DELETEBACKNOTLINE:
7000 case SCI_HOMEDISPLAY:
7001 case SCI_HOMEDISPLAYEXTEND:
7002 case SCI_LINEENDDISPLAY:
7003 case SCI_LINEENDDISPLAYEXTEND:
7004 case SCI_LINEDOWNRECTEXTEND:
7005 case SCI_LINEUPRECTEXTEND:
7006 case SCI_CHARLEFTRECTEXTEND:
7007 case SCI_CHARRIGHTRECTEXTEND:
7008 case SCI_HOMERECTEXTEND:
7009 case SCI_VCHOMERECTEXTEND:
7010 case SCI_LINEENDRECTEXTEND:
7011 case SCI_PAGEUPRECTEXTEND:
7012 case SCI_PAGEDOWNRECTEXTEND:
7013 case SCI_SELECTIONDUPLICATE:
7014 return KeyCommand(iMessage);
7016 case SCI_BRACEHIGHLIGHT:
7017 SetBraceHighlight(static_cast<int>(wParam), static_cast<int>(lParam), STYLE_BRACELIGHT);
7018 break;
7020 case SCI_BRACEHIGHLIGHTINDICATOR:
7021 if (lParam >= 0 && lParam <= INDIC_MAX) {
7022 vs.braceHighlightIndicatorSet = wParam != 0;
7023 vs.braceHighlightIndicator = static_cast<int>(lParam);
7025 break;
7027 case SCI_BRACEBADLIGHT:
7028 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
7029 break;
7031 case SCI_BRACEBADLIGHTINDICATOR:
7032 if (lParam >= 0 && lParam <= INDIC_MAX) {
7033 vs.braceBadLightIndicatorSet = wParam != 0;
7034 vs.braceBadLightIndicator = static_cast<int>(lParam);
7036 break;
7038 case SCI_BRACEMATCH:
7039 // wParam is position of char to find brace for,
7040 // lParam is maximum amount of text to restyle to find it
7041 return pdoc->BraceMatch(static_cast<int>(wParam), static_cast<int>(lParam));
7043 case SCI_GETVIEWEOL:
7044 return vs.viewEOL;
7046 case SCI_SETVIEWEOL:
7047 vs.viewEOL = wParam != 0;
7048 InvalidateStyleRedraw();
7049 break;
7051 case SCI_SETZOOM:
7052 vs.zoomLevel = static_cast<int>(wParam);
7053 InvalidateStyleRedraw();
7054 NotifyZoom();
7055 break;
7057 case SCI_GETZOOM:
7058 return vs.zoomLevel;
7060 case SCI_GETEDGECOLUMN:
7061 return vs.theEdge;
7063 case SCI_SETEDGECOLUMN:
7064 vs.theEdge = static_cast<int>(wParam);
7065 InvalidateStyleRedraw();
7066 break;
7068 case SCI_GETEDGEMODE:
7069 return vs.edgeState;
7071 case SCI_SETEDGEMODE:
7072 vs.edgeState = static_cast<int>(wParam);
7073 InvalidateStyleRedraw();
7074 break;
7076 case SCI_GETEDGECOLOUR:
7077 return vs.edgecolour.AsLong();
7079 case SCI_SETEDGECOLOUR:
7080 vs.edgecolour = ColourDesired(static_cast<long>(wParam));
7081 InvalidateStyleRedraw();
7082 break;
7084 case SCI_GETDOCPOINTER:
7085 return reinterpret_cast<sptr_t>(pdoc);
7087 case SCI_SETDOCPOINTER:
7088 CancelModes();
7089 SetDocPointer(reinterpret_cast<Document *>(lParam));
7090 return 0;
7092 case SCI_CREATEDOCUMENT: {
7093 Document *doc = new Document();
7094 doc->AddRef();
7095 return reinterpret_cast<sptr_t>(doc);
7098 case SCI_ADDREFDOCUMENT:
7099 (reinterpret_cast<Document *>(lParam))->AddRef();
7100 break;
7102 case SCI_RELEASEDOCUMENT:
7103 (reinterpret_cast<Document *>(lParam))->Release();
7104 break;
7106 case SCI_CREATELOADER: {
7107 Document *doc = new Document();
7108 doc->AddRef();
7109 doc->Allocate(static_cast<int>(wParam));
7110 doc->SetUndoCollection(false);
7111 return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
7114 case SCI_SETMODEVENTMASK:
7115 modEventMask = static_cast<int>(wParam);
7116 return 0;
7118 case SCI_GETMODEVENTMASK:
7119 return modEventMask;
7121 case SCI_CONVERTEOLS:
7122 pdoc->ConvertLineEnds(static_cast<int>(wParam));
7123 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
7124 return 0;
7126 case SCI_SETLENGTHFORENCODE:
7127 lengthForEncode = static_cast<int>(wParam);
7128 return 0;
7130 case SCI_SELECTIONISRECTANGLE:
7131 return sel.selType == Selection::selRectangle ? 1 : 0;
7133 case SCI_SETSELECTIONMODE: {
7134 switch (wParam) {
7135 case SC_SEL_STREAM:
7136 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7137 sel.selType = Selection::selStream;
7138 break;
7139 case SC_SEL_RECTANGLE:
7140 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
7141 sel.selType = Selection::selRectangle;
7142 break;
7143 case SC_SEL_LINES:
7144 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
7145 sel.selType = Selection::selLines;
7146 break;
7147 case SC_SEL_THIN:
7148 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
7149 sel.selType = Selection::selThin;
7150 break;
7151 default:
7152 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7153 sel.selType = Selection::selStream;
7155 InvalidateSelection(sel.RangeMain(), true);
7156 break;
7158 case SCI_GETSELECTIONMODE:
7159 switch (sel.selType) {
7160 case Selection::selStream:
7161 return SC_SEL_STREAM;
7162 case Selection::selRectangle:
7163 return SC_SEL_RECTANGLE;
7164 case Selection::selLines:
7165 return SC_SEL_LINES;
7166 case Selection::selThin:
7167 return SC_SEL_THIN;
7168 default: // ?!
7169 return SC_SEL_STREAM;
7171 case SCI_GETLINESELSTARTPOSITION:
7172 case SCI_GETLINESELENDPOSITION: {
7173 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(static_cast<int>(wParam))),
7174 SelectionPosition(pdoc->LineEnd(static_cast<int>(wParam))));
7175 for (size_t r=0; r<sel.Count(); r++) {
7176 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
7177 if (portion.start.IsValid()) {
7178 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
7181 return INVALID_POSITION;
7184 case SCI_SETOVERTYPE:
7185 inOverstrike = wParam != 0;
7186 break;
7188 case SCI_GETOVERTYPE:
7189 return inOverstrike ? 1 : 0;
7191 case SCI_SETFOCUS:
7192 SetFocusState(wParam != 0);
7193 break;
7195 case SCI_GETFOCUS:
7196 return hasFocus;
7198 case SCI_SETSTATUS:
7199 errorStatus = static_cast<int>(wParam);
7200 break;
7202 case SCI_GETSTATUS:
7203 return errorStatus;
7205 case SCI_SETMOUSEDOWNCAPTURES:
7206 mouseDownCaptures = wParam != 0;
7207 break;
7209 case SCI_GETMOUSEDOWNCAPTURES:
7210 return mouseDownCaptures;
7212 case SCI_SETCURSOR:
7213 cursorMode = static_cast<int>(wParam);
7214 DisplayCursor(Window::cursorText);
7215 break;
7217 case SCI_GETCURSOR:
7218 return cursorMode;
7220 case SCI_SETCONTROLCHARSYMBOL:
7221 vs.controlCharSymbol = static_cast<int>(wParam);
7222 InvalidateStyleRedraw();
7223 break;
7225 case SCI_GETCONTROLCHARSYMBOL:
7226 return vs.controlCharSymbol;
7228 case SCI_SETREPRESENTATION:
7229 reprs.SetRepresentation(reinterpret_cast<const char *>(wParam), CharPtrFromSPtr(lParam));
7230 break;
7232 case SCI_GETREPRESENTATION: {
7233 const Representation *repr = reprs.RepresentationFromCharacter(
7234 reinterpret_cast<const char *>(wParam), UTF8MaxBytes);
7235 if (repr) {
7236 return StringResult(lParam, repr->stringRep.c_str());
7238 return 0;
7241 case SCI_CLEARREPRESENTATION:
7242 reprs.ClearRepresentation(reinterpret_cast<const char *>(wParam));
7243 break;
7245 case SCI_STARTRECORD:
7246 recordingMacro = true;
7247 return 0;
7249 case SCI_STOPRECORD:
7250 recordingMacro = false;
7251 return 0;
7253 case SCI_MOVECARETINSIDEVIEW:
7254 MoveCaretInsideView();
7255 break;
7257 case SCI_SETFOLDMARGINCOLOUR:
7258 vs.foldmarginColour = ColourOptional(wParam, lParam);
7259 InvalidateStyleRedraw();
7260 break;
7262 case SCI_SETFOLDMARGINHICOLOUR:
7263 vs.foldmarginHighlightColour = ColourOptional(wParam, lParam);
7264 InvalidateStyleRedraw();
7265 break;
7267 case SCI_SETHOTSPOTACTIVEFORE:
7268 vs.hotspotColours.fore = ColourOptional(wParam, lParam);
7269 InvalidateStyleRedraw();
7270 break;
7272 case SCI_GETHOTSPOTACTIVEFORE:
7273 return vs.hotspotColours.fore.AsLong();
7275 case SCI_SETHOTSPOTACTIVEBACK:
7276 vs.hotspotColours.back = ColourOptional(wParam, lParam);
7277 InvalidateStyleRedraw();
7278 break;
7280 case SCI_GETHOTSPOTACTIVEBACK:
7281 return vs.hotspotColours.back.AsLong();
7283 case SCI_SETHOTSPOTACTIVEUNDERLINE:
7284 vs.hotspotUnderline = wParam != 0;
7285 InvalidateStyleRedraw();
7286 break;
7288 case SCI_GETHOTSPOTACTIVEUNDERLINE:
7289 return vs.hotspotUnderline ? 1 : 0;
7291 case SCI_SETHOTSPOTSINGLELINE:
7292 vs.hotspotSingleLine = wParam != 0;
7293 InvalidateStyleRedraw();
7294 break;
7296 case SCI_GETHOTSPOTSINGLELINE:
7297 return vs.hotspotSingleLine ? 1 : 0;
7299 case SCI_SETPASTECONVERTENDINGS:
7300 convertPastes = wParam != 0;
7301 break;
7303 case SCI_GETPASTECONVERTENDINGS:
7304 return convertPastes ? 1 : 0;
7306 case SCI_GETCHARACTERPOINTER:
7307 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
7309 case SCI_GETRANGEPOINTER:
7310 return reinterpret_cast<sptr_t>(pdoc->RangePointer(static_cast<int>(wParam), static_cast<int>(lParam)));
7312 case SCI_GETGAPPOSITION:
7313 return pdoc->GapPosition();
7315 case SCI_SETEXTRAASCENT:
7316 vs.extraAscent = static_cast<int>(wParam);
7317 InvalidateStyleRedraw();
7318 break;
7320 case SCI_GETEXTRAASCENT:
7321 return vs.extraAscent;
7323 case SCI_SETEXTRADESCENT:
7324 vs.extraDescent = static_cast<int>(wParam);
7325 InvalidateStyleRedraw();
7326 break;
7328 case SCI_GETEXTRADESCENT:
7329 return vs.extraDescent;
7331 case SCI_MARGINSETSTYLEOFFSET:
7332 vs.marginStyleOffset = static_cast<int>(wParam);
7333 InvalidateStyleRedraw();
7334 break;
7336 case SCI_MARGINGETSTYLEOFFSET:
7337 return vs.marginStyleOffset;
7339 case SCI_SETMARGINOPTIONS:
7340 marginOptions = static_cast<int>(wParam);
7341 break;
7343 case SCI_GETMARGINOPTIONS:
7344 return marginOptions;
7346 case SCI_MARGINSETTEXT:
7347 pdoc->MarginSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7348 break;
7350 case SCI_MARGINGETTEXT: {
7351 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7352 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
7355 case SCI_MARGINSETSTYLE:
7356 pdoc->MarginSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
7357 break;
7359 case SCI_MARGINGETSTYLE: {
7360 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7361 return st.style;
7364 case SCI_MARGINSETSTYLES:
7365 pdoc->MarginSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
7366 break;
7368 case SCI_MARGINGETSTYLES: {
7369 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7370 return BytesResult(lParam, st.styles, st.length);
7373 case SCI_MARGINTEXTCLEARALL:
7374 pdoc->MarginClearAll();
7375 break;
7377 case SCI_ANNOTATIONSETTEXT:
7378 pdoc->AnnotationSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7379 break;
7381 case SCI_ANNOTATIONGETTEXT: {
7382 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7383 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
7386 case SCI_ANNOTATIONGETSTYLE: {
7387 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7388 return st.style;
7391 case SCI_ANNOTATIONSETSTYLE:
7392 pdoc->AnnotationSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
7393 break;
7395 case SCI_ANNOTATIONSETSTYLES:
7396 pdoc->AnnotationSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
7397 break;
7399 case SCI_ANNOTATIONGETSTYLES: {
7400 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7401 return BytesResult(lParam, st.styles, st.length);
7404 case SCI_ANNOTATIONGETLINES:
7405 return pdoc->AnnotationLines(static_cast<int>(wParam));
7407 case SCI_ANNOTATIONCLEARALL:
7408 pdoc->AnnotationClearAll();
7409 break;
7411 case SCI_ANNOTATIONSETVISIBLE:
7412 SetAnnotationVisible(static_cast<int>(wParam));
7413 break;
7415 case SCI_ANNOTATIONGETVISIBLE:
7416 return vs.annotationVisible;
7418 case SCI_ANNOTATIONSETSTYLEOFFSET:
7419 vs.annotationStyleOffset = static_cast<int>(wParam);
7420 InvalidateStyleRedraw();
7421 break;
7423 case SCI_ANNOTATIONGETSTYLEOFFSET:
7424 return vs.annotationStyleOffset;
7426 case SCI_RELEASEALLEXTENDEDSTYLES:
7427 vs.ReleaseAllExtendedStyles();
7428 break;
7430 case SCI_ALLOCATEEXTENDEDSTYLES:
7431 return vs.AllocateExtendedStyles(static_cast<int>(wParam));
7433 case SCI_ADDUNDOACTION:
7434 pdoc->AddUndoAction(static_cast<int>(wParam), lParam & UNDO_MAY_COALESCE);
7435 break;
7437 case SCI_SETMOUSESELECTIONRECTANGULARSWITCH:
7438 mouseSelectionRectangularSwitch = wParam != 0;
7439 break;
7441 case SCI_GETMOUSESELECTIONRECTANGULARSWITCH:
7442 return mouseSelectionRectangularSwitch;
7444 case SCI_SETMULTIPLESELECTION:
7445 multipleSelection = wParam != 0;
7446 InvalidateCaret();
7447 break;
7449 case SCI_GETMULTIPLESELECTION:
7450 return multipleSelection;
7452 case SCI_SETADDITIONALSELECTIONTYPING:
7453 additionalSelectionTyping = wParam != 0;
7454 InvalidateCaret();
7455 break;
7457 case SCI_GETADDITIONALSELECTIONTYPING:
7458 return additionalSelectionTyping;
7460 case SCI_SETMULTIPASTE:
7461 multiPasteMode = static_cast<int>(wParam);
7462 break;
7464 case SCI_GETMULTIPASTE:
7465 return multiPasteMode;
7467 case SCI_SETADDITIONALCARETSBLINK:
7468 view.additionalCaretsBlink = wParam != 0;
7469 InvalidateCaret();
7470 break;
7472 case SCI_GETADDITIONALCARETSBLINK:
7473 return view.additionalCaretsBlink;
7475 case SCI_SETADDITIONALCARETSVISIBLE:
7476 view.additionalCaretsVisible = wParam != 0;
7477 InvalidateCaret();
7478 break;
7480 case SCI_GETADDITIONALCARETSVISIBLE:
7481 return view.additionalCaretsVisible;
7483 case SCI_GETSELECTIONS:
7484 return sel.Count();
7486 case SCI_GETSELECTIONEMPTY:
7487 return sel.Empty();
7489 case SCI_CLEARSELECTIONS:
7490 sel.Clear();
7491 Redraw();
7492 break;
7494 case SCI_SETSELECTION:
7495 sel.SetSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
7496 Redraw();
7497 break;
7499 case SCI_ADDSELECTION:
7500 sel.AddSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
7501 Redraw();
7502 break;
7504 case SCI_DROPSELECTIONN:
7505 sel.DropSelection(static_cast<int>(wParam));
7506 Redraw();
7507 break;
7509 case SCI_SETMAINSELECTION:
7510 sel.SetMain(static_cast<int>(wParam));
7511 Redraw();
7512 break;
7514 case SCI_GETMAINSELECTION:
7515 return sel.Main();
7517 case SCI_SETSELECTIONNCARET:
7518 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
7519 Redraw();
7520 break;
7522 case SCI_GETSELECTIONNCARET:
7523 return sel.Range(wParam).caret.Position();
7525 case SCI_SETSELECTIONNANCHOR:
7526 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
7527 Redraw();
7528 break;
7529 case SCI_GETSELECTIONNANCHOR:
7530 return sel.Range(wParam).anchor.Position();
7532 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
7533 sel.Range(wParam).caret.SetVirtualSpace(static_cast<int>(lParam));
7534 Redraw();
7535 break;
7537 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
7538 return sel.Range(wParam).caret.VirtualSpace();
7540 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
7541 sel.Range(wParam).anchor.SetVirtualSpace(static_cast<int>(lParam));
7542 Redraw();
7543 break;
7545 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
7546 return sel.Range(wParam).anchor.VirtualSpace();
7548 case SCI_SETSELECTIONNSTART:
7549 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
7550 Redraw();
7551 break;
7553 case SCI_GETSELECTIONNSTART:
7554 return sel.Range(wParam).Start().Position();
7556 case SCI_SETSELECTIONNEND:
7557 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
7558 Redraw();
7559 break;
7561 case SCI_GETSELECTIONNEND:
7562 return sel.Range(wParam).End().Position();
7564 case SCI_SETRECTANGULARSELECTIONCARET:
7565 if (!sel.IsRectangular())
7566 sel.Clear();
7567 sel.selType = Selection::selRectangle;
7568 sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
7569 SetRectangularRange();
7570 Redraw();
7571 break;
7573 case SCI_GETRECTANGULARSELECTIONCARET:
7574 return sel.Rectangular().caret.Position();
7576 case SCI_SETRECTANGULARSELECTIONANCHOR:
7577 if (!sel.IsRectangular())
7578 sel.Clear();
7579 sel.selType = Selection::selRectangle;
7580 sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
7581 SetRectangularRange();
7582 Redraw();
7583 break;
7585 case SCI_GETRECTANGULARSELECTIONANCHOR:
7586 return sel.Rectangular().anchor.Position();
7588 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
7589 if (!sel.IsRectangular())
7590 sel.Clear();
7591 sel.selType = Selection::selRectangle;
7592 sel.Rectangular().caret.SetVirtualSpace(static_cast<int>(wParam));
7593 SetRectangularRange();
7594 Redraw();
7595 break;
7597 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
7598 return sel.Rectangular().caret.VirtualSpace();
7600 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
7601 if (!sel.IsRectangular())
7602 sel.Clear();
7603 sel.selType = Selection::selRectangle;
7604 sel.Rectangular().anchor.SetVirtualSpace(static_cast<int>(wParam));
7605 SetRectangularRange();
7606 Redraw();
7607 break;
7609 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
7610 return sel.Rectangular().anchor.VirtualSpace();
7612 case SCI_SETVIRTUALSPACEOPTIONS:
7613 virtualSpaceOptions = static_cast<int>(wParam);
7614 break;
7616 case SCI_GETVIRTUALSPACEOPTIONS:
7617 return virtualSpaceOptions;
7619 case SCI_SETADDITIONALSELFORE:
7620 vs.selAdditionalForeground = ColourDesired(static_cast<long>(wParam));
7621 InvalidateStyleRedraw();
7622 break;
7624 case SCI_SETADDITIONALSELBACK:
7625 vs.selAdditionalBackground = ColourDesired(static_cast<long>(wParam));
7626 InvalidateStyleRedraw();
7627 break;
7629 case SCI_SETADDITIONALSELALPHA:
7630 vs.selAdditionalAlpha = static_cast<int>(wParam);
7631 InvalidateStyleRedraw();
7632 break;
7634 case SCI_GETADDITIONALSELALPHA:
7635 return vs.selAdditionalAlpha;
7637 case SCI_SETADDITIONALCARETFORE:
7638 vs.additionalCaretColour = ColourDesired(static_cast<long>(wParam));
7639 InvalidateStyleRedraw();
7640 break;
7642 case SCI_GETADDITIONALCARETFORE:
7643 return vs.additionalCaretColour.AsLong();
7645 case SCI_ROTATESELECTION:
7646 sel.RotateMain();
7647 InvalidateSelection(sel.RangeMain(), true);
7648 break;
7650 case SCI_SWAPMAINANCHORCARET:
7651 InvalidateSelection(sel.RangeMain());
7652 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
7653 break;
7655 case SCI_CHANGELEXERSTATE:
7656 pdoc->ChangeLexerState(static_cast<int>(wParam), static_cast<int>(lParam));
7657 break;
7659 case SCI_SETIDENTIFIER:
7660 SetCtrlID(static_cast<int>(wParam));
7661 break;
7663 case SCI_GETIDENTIFIER:
7664 return GetCtrlID();
7666 case SCI_SETTECHNOLOGY:
7667 // No action by default
7668 break;
7670 case SCI_GETTECHNOLOGY:
7671 return technology;
7673 case SCI_COUNTCHARACTERS:
7674 return pdoc->CountCharacters(static_cast<int>(wParam), static_cast<int>(lParam));
7676 default:
7677 return DefWndProc(iMessage, wParam, lParam);
7679 //Platform::DebugPrintf("end wnd proc\n");
7680 return 0l;