Update Scintilla to version 3.5.2
[TortoiseGit.git] / ext / scintilla / src / Editor.cxx
blobf9dfc6c37725e3e537ce37aaf7bba89caf93a761
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 <math.h>
12 #include <assert.h>
13 #include <ctype.h>
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();
634 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
635 RedrawSelMargin();
637 QueueIdleWork(WorkNeeded::workUpdateUI);
640 void Editor::SetSelection(int currentPos_, int anchor_) {
641 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
644 // Just move the caret on the main selection
645 void Editor::SetSelection(SelectionPosition currentPos_) {
646 currentPos_ = ClampPositionIntoDocument(currentPos_);
647 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
648 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
649 InvalidateSelection(SelectionRange(currentPos_));
651 if (sel.IsRectangular()) {
652 sel.Rectangular() =
653 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
654 SetRectangularRange();
655 } else {
656 sel.RangeMain() =
657 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
659 ClaimSelection();
661 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
662 RedrawSelMargin();
664 QueueIdleWork(WorkNeeded::workUpdateUI);
667 void Editor::SetSelection(int currentPos_) {
668 SetSelection(SelectionPosition(currentPos_));
671 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
672 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
673 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
674 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
675 InvalidateSelection(rangeNew);
677 sel.Clear();
678 sel.RangeMain() = rangeNew;
679 SetRectangularRange();
680 ClaimSelection();
682 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
683 RedrawSelMargin();
685 QueueIdleWork(WorkNeeded::workUpdateUI);
688 void Editor::SetEmptySelection(int currentPos_) {
689 SetEmptySelection(SelectionPosition(currentPos_));
692 bool Editor::RangeContainsProtected(int start, int end) const {
693 if (vs.ProtectionActive()) {
694 if (start > end) {
695 int t = start;
696 start = end;
697 end = t;
699 for (int pos = start; pos < end; pos++) {
700 if (vs.styles[pdoc->StyleAt(pos)].IsProtected())
701 return true;
704 return false;
707 bool Editor::SelectionContainsProtected() {
708 for (size_t r=0; r<sel.Count(); r++) {
709 if (RangeContainsProtected(sel.Range(r).Start().Position(),
710 sel.Range(r).End().Position())) {
711 return true;
714 return false;
718 * Asks document to find a good position and then moves out of any invisible positions.
720 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
721 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
724 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
725 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
726 if (posMoved != pos.Position())
727 pos.SetPosition(posMoved);
728 if (vs.ProtectionActive()) {
729 if (moveDir > 0) {
730 if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1)].IsProtected()) {
731 while ((pos.Position() < pdoc->Length()) &&
732 (vs.styles[pdoc->StyleAt(pos.Position())].IsProtected()))
733 pos.Add(1);
735 } else if (moveDir < 0) {
736 if (vs.styles[pdoc->StyleAt(pos.Position())].IsProtected()) {
737 while ((pos.Position() > 0) &&
738 (vs.styles[pdoc->StyleAt(pos.Position() - 1)].IsProtected()))
739 pos.Add(-1);
743 return pos;
746 int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
747 bool simpleCaret = (sel.Count() == 1) && sel.Empty();
748 SelectionPosition spCaret = sel.Last();
750 int delta = newPos.Position() - sel.MainCaret();
751 newPos = ClampPositionIntoDocument(newPos);
752 newPos = MovePositionOutsideChar(newPos, delta);
753 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
754 // Can't turn into multiple selection so clear additional selections
755 InvalidateSelection(SelectionRange(newPos), true);
756 SelectionRange rangeMain = sel.RangeMain();
757 sel.SetSelection(rangeMain);
759 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
760 // Switching to rectangular
761 InvalidateSelection(sel.RangeMain(), false);
762 SelectionRange rangeMain = sel.RangeMain();
763 sel.Clear();
764 sel.Rectangular() = rangeMain;
766 if (selt != Selection::noSel) {
767 sel.selType = selt;
769 if (selt != Selection::noSel || sel.MoveExtends()) {
770 SetSelection(newPos);
771 } else {
772 SetEmptySelection(newPos);
774 ShowCaretAtCurrentPosition();
776 int currentLine = pdoc->LineFromPosition(newPos.Position());
777 if (ensureVisible) {
778 // In case in need of wrapping to ensure DisplayFromDoc works.
779 if (currentLine >= wrapPending.start)
780 WrapLines(wsAll);
781 XYScrollPosition newXY = XYScrollToMakeVisible(
782 SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret), xysDefault);
783 if (simpleCaret && (newXY.xOffset == xOffset)) {
784 // simple vertical scroll then invalidate
785 ScrollTo(newXY.topLine);
786 InvalidateSelection(SelectionRange(spCaret), true);
787 } else {
788 SetXYScroll(newXY);
792 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
793 RedrawSelMargin();
795 return 0;
798 int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
799 return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
802 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
803 pos = ClampPositionIntoDocument(pos);
804 pos = MovePositionOutsideChar(pos, moveDir);
805 int lineDoc = pdoc->LineFromPosition(pos.Position());
806 if (cs.GetVisible(lineDoc)) {
807 return pos;
808 } else {
809 int lineDisplay = cs.DisplayFromDoc(lineDoc);
810 if (moveDir > 0) {
811 // lineDisplay is already line before fold as lines in fold use display line of line after fold
812 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
813 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
814 } else {
815 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
816 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
821 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
822 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
825 Point Editor::PointMainCaret() {
826 return LocationFromPosition(sel.Range(sel.Main()).caret);
830 * Choose the x position that the caret will try to stick to
831 * as it moves up and down.
833 void Editor::SetLastXChosen() {
834 Point pt = PointMainCaret();
835 lastXChosen = static_cast<int>(pt.x) + xOffset;
838 void Editor::ScrollTo(int line, bool moveThumb) {
839 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
840 if (topLineNew != topLine) {
841 // Try to optimise small scrolls
842 #ifndef UNDER_CE
843 int linesToMove = topLine - topLineNew;
844 bool performBlit = (abs(linesToMove) <= 10) && (paintState == notPainting);
845 willRedrawAll = !performBlit;
846 #endif
847 SetTopLine(topLineNew);
848 // Optimize by styling the view as this will invalidate any needed area
849 // which could abort the initial paint if discovered later.
850 StyleToPositionInView(PositionAfterArea(GetClientRectangle()));
851 #ifndef UNDER_CE
852 // Perform redraw rather than scroll if many lines would be redrawn anyway.
853 if (performBlit) {
854 ScrollText(linesToMove);
855 } else {
856 Redraw();
858 willRedrawAll = false;
859 #else
860 Redraw();
861 #endif
862 if (moveThumb) {
863 SetVerticalScrollPos();
868 void Editor::ScrollText(int /* linesToMove */) {
869 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
870 Redraw();
873 void Editor::HorizontalScrollTo(int xPos) {
874 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
875 if (xPos < 0)
876 xPos = 0;
877 if (!Wrapping() && (xOffset != xPos)) {
878 xOffset = xPos;
879 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
880 SetHorizontalScrollPos();
881 RedrawRect(GetClientRectangle());
885 void Editor::VerticalCentreCaret() {
886 int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
887 int lineDisplay = cs.DisplayFromDoc(lineDoc);
888 int newTop = lineDisplay - (LinesOnScreen() / 2);
889 if (topLine != newTop) {
890 SetTopLine(newTop > 0 ? newTop : 0);
891 RedrawRect(GetClientRectangle());
895 // Avoid 64 bit compiler warnings.
896 // Scintilla does not support text buffers larger than 2**31
897 static int istrlen(const char *s) {
898 return static_cast<int>(s ? strlen(s) : 0);
901 void Editor::MoveSelectedLines(int lineDelta) {
903 // if selection doesn't start at the beginning of the line, set the new start
904 int selectionStart = SelectionStart().Position();
905 int startLine = pdoc->LineFromPosition(selectionStart);
906 int beginningOfStartLine = pdoc->LineStart(startLine);
907 selectionStart = beginningOfStartLine;
909 // if selection doesn't end at the beginning of a line greater than that of the start,
910 // then set it at the beginning of the next one
911 int selectionEnd = SelectionEnd().Position();
912 int endLine = pdoc->LineFromPosition(selectionEnd);
913 int beginningOfEndLine = pdoc->LineStart(endLine);
914 bool appendEol = false;
915 if (selectionEnd > beginningOfEndLine
916 || selectionStart == selectionEnd) {
917 selectionEnd = pdoc->LineStart(endLine + 1);
918 appendEol = (selectionEnd == pdoc->Length() && pdoc->LineFromPosition(selectionEnd) == endLine);
921 // if there's nowhere for the selection to move
922 // (i.e. at the beginning going up or at the end going down),
923 // stop it right there!
924 if ((selectionStart == 0 && lineDelta < 0)
925 || (selectionEnd == pdoc->Length() && lineDelta > 0)
926 || selectionStart == selectionEnd) {
927 return;
930 UndoGroup ug(pdoc);
932 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
933 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
934 ClearSelection();
935 selectionEnd = CurrentPosition();
937 SetSelection(selectionStart, selectionEnd);
939 SelectionText selectedText;
940 CopySelectionRange(&selectedText);
942 int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
943 Point currentLocation = LocationFromPosition(CurrentPosition());
944 int currentLine = LineFromLocation(currentLocation);
946 if (appendEol)
947 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
948 ClearSelection();
950 const char *eol = StringFromEOLMode(pdoc->eolMode);
951 if (currentLine + lineDelta >= pdoc->LinesTotal())
952 pdoc->InsertString(pdoc->Length(), eol, istrlen(eol));
953 GoToLine(currentLine + lineDelta);
955 selectionLength = pdoc->InsertString(CurrentPosition(), selectedText.Data(), selectionLength);
956 if (appendEol) {
957 const int lengthInserted = pdoc->InsertString(CurrentPosition() + selectionLength, eol, istrlen(eol));
958 selectionLength += lengthInserted;
960 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
963 void Editor::MoveSelectedLinesUp() {
964 MoveSelectedLines(-1);
967 void Editor::MoveSelectedLinesDown() {
968 MoveSelectedLines(1);
971 void Editor::MoveCaretInsideView(bool ensureVisible) {
972 PRectangle rcClient = GetTextRectangle();
973 Point pt = PointMainCaret();
974 if (pt.y < rcClient.top) {
975 MovePositionTo(SPositionFromLocation(
976 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top)),
977 false, false, UserVirtualSpace()),
978 Selection::noSel, ensureVisible);
979 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
980 int yOfLastLineFullyDisplayed = static_cast<int>(rcClient.top) + (LinesOnScreen() - 1) * vs.lineHeight;
981 MovePositionTo(SPositionFromLocation(
982 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top) + yOfLastLineFullyDisplayed),
983 false, false, UserVirtualSpace()),
984 Selection::noSel, ensureVisible);
988 int Editor::DisplayFromPosition(int pos) {
989 AutoSurface surface(this);
990 return view.DisplayFromPosition(surface, *this, pos, vs);
994 * Ensure the caret is reasonably visible in context.
996 Caret policy in SciTE
998 If slop is set, we can define a slop value.
999 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1000 This zone is defined as a number of pixels near the vertical margins,
1001 and as a number of lines near the horizontal margins.
1002 By keeping the caret away from the edges, it is seen within its context,
1003 so it is likely that the identifier that the caret is on can be completely seen,
1004 and that the current line is seen with some of the lines following it which are
1005 often dependent on that line.
1007 If strict is set, the policy is enforced... strictly.
1008 The caret is centred on the display if slop is not set,
1009 and cannot go in the UZ if slop is set.
1011 If jumps is set, the display is moved more energetically
1012 so the caret can move in the same direction longer before the policy is applied again.
1013 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1015 If even is not set, instead of having symmetrical UZs,
1016 the left and bottom UZs are extended up to right and top UZs respectively.
1017 This way, we favour the displaying of useful information: the beginning of lines,
1018 where most code reside, and the lines after the caret, eg. the body of a function.
1020 | | | | |
1021 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1022 | | | | | visibility or going into the UZ) display is...
1023 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1024 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1025 0 | 0 | 0 | 1 | Yes | moved by one position
1026 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1027 0 | 0 | 1 | 1 | Yes | centred on the caret
1028 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1029 0 | 1 | - | 1 | No, caret is always centred | -
1030 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1031 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1032 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1033 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1034 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1035 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1036 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1039 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange &range, const XYScrollOptions options) {
1040 PRectangle rcClient = GetTextRectangle();
1041 Point pt = LocationFromPosition(range.caret);
1042 Point ptAnchor = LocationFromPosition(range.anchor);
1043 const Point ptOrigin = GetVisibleOriginInMain();
1044 pt.x += ptOrigin.x;
1045 pt.y += ptOrigin.y;
1046 ptAnchor.x += ptOrigin.x;
1047 ptAnchor.y += ptOrigin.y;
1048 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1050 XYScrollPosition newXY(xOffset, topLine);
1051 if (rcClient.Empty()) {
1052 return newXY;
1055 // Vertical positioning
1056 if ((options & xysVertical) && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1057 const int lineCaret = DisplayFromPosition(range.caret.Position());
1058 const int linesOnScreen = LinesOnScreen();
1059 const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1060 const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
1061 const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
1062 const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
1063 const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
1065 // It should be possible to scroll the window to show the caret,
1066 // but this fails to remove the caret on GTK+
1067 if (bSlop) { // A margin is defined
1068 int yMoveT, yMoveB;
1069 if (bStrict) {
1070 int yMarginT, yMarginB;
1071 if (!(options & xysUseMargin)) {
1072 // In drag mode, avoid moves
1073 // otherwise, a double click will select several lines.
1074 yMarginT = yMarginB = 0;
1075 } else {
1076 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1077 // a maximum of slightly less than half the heigth of the text area.
1078 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1079 if (bEven) {
1080 yMarginB = yMarginT;
1081 } else {
1082 yMarginB = linesOnScreen - yMarginT - 1;
1085 yMoveT = yMarginT;
1086 if (bEven) {
1087 if (bJump) {
1088 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1090 yMoveB = yMoveT;
1091 } else {
1092 yMoveB = linesOnScreen - yMoveT - 1;
1094 if (lineCaret < topLine + yMarginT) {
1095 // Caret goes too high
1096 newXY.topLine = lineCaret - yMoveT;
1097 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1098 // Caret goes too low
1099 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1101 } else { // Not strict
1102 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1103 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1104 if (bEven) {
1105 yMoveB = yMoveT;
1106 } else {
1107 yMoveB = linesOnScreen - yMoveT - 1;
1109 if (lineCaret < topLine) {
1110 // Caret goes too high
1111 newXY.topLine = lineCaret - yMoveT;
1112 } else if (lineCaret > topLine + linesOnScreen - 1) {
1113 // Caret goes too low
1114 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1117 } else { // No slop
1118 if (!bStrict && !bJump) {
1119 // Minimal move
1120 if (lineCaret < topLine) {
1121 // Caret goes too high
1122 newXY.topLine = lineCaret;
1123 } else if (lineCaret > topLine + linesOnScreen - 1) {
1124 // Caret goes too low
1125 if (bEven) {
1126 newXY.topLine = lineCaret - linesOnScreen + 1;
1127 } else {
1128 newXY.topLine = lineCaret;
1131 } else { // Strict or going out of display
1132 if (bEven) {
1133 // Always center caret
1134 newXY.topLine = lineCaret - halfScreen;
1135 } else {
1136 // Always put caret on top of display
1137 newXY.topLine = lineCaret;
1141 if (!(range.caret == range.anchor)) {
1142 const int lineAnchor = DisplayFromPosition(range.anchor.Position());
1143 if (lineAnchor < lineCaret) {
1144 // Shift up to show anchor or as much of range as possible
1145 newXY.topLine = std::min(newXY.topLine, lineAnchor);
1146 newXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());
1147 } else {
1148 // Shift down to show anchor or as much of range as possible
1149 newXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());
1150 newXY.topLine = std::min(newXY.topLine, lineCaret);
1153 newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
1156 // Horizontal positioning
1157 if ((options & xysHorizontal) && !Wrapping()) {
1158 const int halfScreen = Platform::Maximum(static_cast<int>(rcClient.Width()) - 4, 4) / 2;
1159 const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
1160 const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
1161 const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
1162 const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
1164 if (bSlop) { // A margin is defined
1165 int xMoveL, xMoveR;
1166 if (bStrict) {
1167 int xMarginL, xMarginR;
1168 if (!(options & xysUseMargin)) {
1169 // In drag mode, avoid moves unless very near of the margin
1170 // otherwise, a simple click will select text.
1171 xMarginL = xMarginR = 2;
1172 } else {
1173 // xMargin must equal to caretXSlop, with a minimum of 2 and
1174 // a maximum of slightly less than half the width of the text area.
1175 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1176 if (bEven) {
1177 xMarginL = xMarginR;
1178 } else {
1179 xMarginL = static_cast<int>(rcClient.Width()) - xMarginR - 4;
1182 if (bJump && bEven) {
1183 // Jump is used only in even mode
1184 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1185 } else {
1186 xMoveL = xMoveR = 0; // Not used, avoid a warning
1188 if (pt.x < rcClient.left + xMarginL) {
1189 // Caret is on the left of the display
1190 if (bJump && bEven) {
1191 newXY.xOffset -= xMoveL;
1192 } else {
1193 // Move just enough to allow to display the caret
1194 newXY.xOffset -= static_cast<int>((rcClient.left + xMarginL) - pt.x);
1196 } else if (pt.x >= rcClient.right - xMarginR) {
1197 // Caret is on the right of the display
1198 if (bJump && bEven) {
1199 newXY.xOffset += xMoveR;
1200 } else {
1201 // Move just enough to allow to display the caret
1202 newXY.xOffset += static_cast<int>(pt.x - (rcClient.right - xMarginR) + 1);
1205 } else { // Not strict
1206 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1207 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1208 if (bEven) {
1209 xMoveL = xMoveR;
1210 } else {
1211 xMoveL = static_cast<int>(rcClient.Width()) - xMoveR - 4;
1213 if (pt.x < rcClient.left) {
1214 // Caret is on the left of the display
1215 newXY.xOffset -= xMoveL;
1216 } else if (pt.x >= rcClient.right) {
1217 // Caret is on the right of the display
1218 newXY.xOffset += xMoveR;
1221 } else { // No slop
1222 if (bStrict ||
1223 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1224 // Strict or going out of display
1225 if (bEven) {
1226 // Center caret
1227 newXY.xOffset += static_cast<int>(pt.x - rcClient.left - halfScreen);
1228 } else {
1229 // Put caret on right
1230 newXY.xOffset += static_cast<int>(pt.x - rcClient.right + 1);
1232 } else {
1233 // Move just enough to allow to display the caret
1234 if (pt.x < rcClient.left) {
1235 // Caret is on the left of the display
1236 if (bEven) {
1237 newXY.xOffset -= static_cast<int>(rcClient.left - pt.x);
1238 } else {
1239 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1241 } else if (pt.x >= rcClient.right) {
1242 // Caret is on the right of the display
1243 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1247 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1248 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1249 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 2;
1250 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1251 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 2;
1252 if ((vs.caretStyle == CARETSTYLE_BLOCK) || view.imeCaretBlockOverride) {
1253 // Ensure we can see a good portion of the block caret
1254 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1257 if (!(range.caret == range.anchor)) {
1258 if (ptAnchor.x < pt.x) {
1259 // Shift to left to show anchor or as much of range as possible
1260 int maxOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.left) - 1;
1261 int minOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 1;
1262 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1263 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1264 } else {
1265 // Shift to right to show anchor or as much of range as possible
1266 int minOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.right) + 1;
1267 int maxOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 1;
1268 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1269 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1272 if (newXY.xOffset < 0) {
1273 newXY.xOffset = 0;
1277 return newXY;
1280 void Editor::SetXYScroll(XYScrollPosition newXY) {
1281 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1282 if (newXY.topLine != topLine) {
1283 SetTopLine(newXY.topLine);
1284 SetVerticalScrollPos();
1286 if (newXY.xOffset != xOffset) {
1287 xOffset = newXY.xOffset;
1288 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1289 if (newXY.xOffset > 0) {
1290 PRectangle rcText = GetTextRectangle();
1291 if (horizontalScrollBarVisible &&
1292 rcText.Width() + xOffset > scrollWidth) {
1293 scrollWidth = xOffset + static_cast<int>(rcText.Width());
1294 SetScrollBars();
1297 SetHorizontalScrollPos();
1299 Redraw();
1300 UpdateSystemCaret();
1304 void Editor::ScrollRange(SelectionRange range) {
1305 SetXYScroll(XYScrollToMakeVisible(range, xysDefault));
1308 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1309 SetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret),
1310 static_cast<XYScrollOptions>((useMargin?xysUseMargin:0)|(vert?xysVertical:0)|(horiz?xysHorizontal:0))));
1313 void Editor::ShowCaretAtCurrentPosition() {
1314 if (hasFocus) {
1315 caret.active = true;
1316 caret.on = true;
1317 if (FineTickerAvailable()) {
1318 FineTickerCancel(tickCaret);
1319 if (caret.period > 0)
1320 FineTickerStart(tickCaret, caret.period, caret.period/10);
1321 } else {
1322 SetTicking(true);
1324 } else {
1325 caret.active = false;
1326 caret.on = false;
1327 if (FineTickerAvailable()) {
1328 FineTickerCancel(tickCaret);
1331 InvalidateCaret();
1334 void Editor::DropCaret() {
1335 caret.active = false;
1336 FineTickerCancel(tickCaret);
1337 InvalidateCaret();
1340 void Editor::CaretSetPeriod(int period) {
1341 if (caret.period != period) {
1342 caret.period = period;
1343 caret.on = true;
1344 if (FineTickerAvailable()) {
1345 FineTickerCancel(tickCaret);
1346 if ((caret.active) && (caret.period > 0))
1347 FineTickerStart(tickCaret, caret.period, caret.period/10);
1349 InvalidateCaret();
1353 void Editor::InvalidateCaret() {
1354 if (posDrag.IsValid()) {
1355 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1356 } else {
1357 for (size_t r=0; r<sel.Count(); r++) {
1358 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1361 UpdateSystemCaret();
1364 void Editor::UpdateSystemCaret() {
1367 bool Editor::Wrapping() const {
1368 return vs.wrapState != eWrapNone;
1371 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1372 //Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd);
1373 if (wrapPending.AddRange(docLineStart, docLineEnd)) {
1374 view.llc.Invalidate(LineLayout::llPositions);
1376 // Wrap lines during idle.
1377 if (Wrapping() && wrapPending.NeedsWrap()) {
1378 SetIdle(true);
1382 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1383 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(lineToWrap, *this));
1384 int linesWrapped = 1;
1385 if (ll) {
1386 view.LayoutLine(*this, lineToWrap, surface, vs, ll, wrapWidth);
1387 linesWrapped = ll->lines;
1389 return cs.SetHeight(lineToWrap, linesWrapped +
1390 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1393 // Perform wrapping for a subset of the lines needing wrapping.
1394 // wsAll: wrap all lines which need wrapping in this single call
1395 // wsVisible: wrap currently visible lines
1396 // wsIdle: wrap one page + 100 lines
1397 // Return true if wrapping occurred.
1398 bool Editor::WrapLines(enum wrapScope ws) {
1399 int goodTopLine = topLine;
1400 bool wrapOccurred = false;
1401 if (!Wrapping()) {
1402 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1403 wrapWidth = LineLayout::wrapWidthInfinite;
1404 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1405 cs.SetHeight(lineDoc, 1 +
1406 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1408 wrapOccurred = true;
1410 wrapPending.Reset();
1412 } else if (wrapPending.NeedsWrap()) {
1413 wrapPending.start = std::min(wrapPending.start, pdoc->LinesTotal());
1414 if (!SetIdle(true)) {
1415 // Idle processing not supported so full wrap required.
1416 ws = wsAll;
1418 // Decide where to start wrapping
1419 int lineToWrap = wrapPending.start;
1420 int lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal());
1421 const int lineDocTop = cs.DocFromDisplay(topLine);
1422 const int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1423 if (ws == wsVisible) {
1424 lineToWrap = Platform::Clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal());
1425 // Priority wrap to just after visible area.
1426 // Since wrapping could reduce display lines, treat each
1427 // as taking only one display line.
1428 lineToWrapEnd = lineDocTop;
1429 int lines = LinesOnScreen() + 1;
1430 while ((lineToWrapEnd < cs.LinesInDoc()) && (lines>0)) {
1431 if (cs.GetVisible(lineToWrapEnd))
1432 lines--;
1433 lineToWrapEnd++;
1435 // .. and if the paint window is outside pending wraps
1436 if ((lineToWrap > wrapPending.end) || (lineToWrapEnd < wrapPending.start)) {
1437 // Currently visible text does not need wrapping
1438 return false;
1440 } else if (ws == wsIdle) {
1441 lineToWrapEnd = lineToWrap + LinesOnScreen() + 100;
1443 const int lineEndNeedWrap = std::min(wrapPending.end, pdoc->LinesTotal());
1444 lineToWrapEnd = std::min(lineToWrapEnd, lineEndNeedWrap);
1446 // Ensure all lines being wrapped are styled.
1447 pdoc->EnsureStyledTo(pdoc->LineStart(lineToWrapEnd));
1449 if (lineToWrap < lineToWrapEnd) {
1451 PRectangle rcTextArea = GetClientRectangle();
1452 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1453 rcTextArea.right -= vs.rightMarginWidth;
1454 wrapWidth = static_cast<int>(rcTextArea.Width());
1455 RefreshStyleData();
1456 AutoSurface surface(this);
1457 if (surface) {
1458 //Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
1460 while (lineToWrap < lineToWrapEnd) {
1461 if (WrapOneLine(surface, lineToWrap)) {
1462 wrapOccurred = true;
1464 wrapPending.Wrapped(lineToWrap);
1465 lineToWrap++;
1468 goodTopLine = cs.DisplayFromDoc(lineDocTop) + std::min(subLineTop, cs.GetHeight(lineDocTop)-1);
1472 // If wrapping is done, bring it to resting position
1473 if (wrapPending.start >= lineEndNeedWrap) {
1474 wrapPending.Reset();
1478 if (wrapOccurred) {
1479 SetScrollBars();
1480 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1481 SetVerticalScrollPos();
1484 return wrapOccurred;
1487 void Editor::LinesJoin() {
1488 if (!RangeContainsProtected(targetStart, targetEnd)) {
1489 UndoGroup ug(pdoc);
1490 bool prevNonWS = true;
1491 for (int pos = targetStart; pos < targetEnd; pos++) {
1492 if (pdoc->IsPositionInLineEnd(pos)) {
1493 targetEnd -= pdoc->LenChar(pos);
1494 pdoc->DelChar(pos);
1495 if (prevNonWS) {
1496 // Ensure at least one space separating previous lines
1497 const int lengthInserted = pdoc->InsertString(pos, " ", 1);
1498 targetEnd += lengthInserted;
1500 } else {
1501 prevNonWS = pdoc->CharAt(pos) != ' ';
1507 const char *Editor::StringFromEOLMode(int eolMode) {
1508 if (eolMode == SC_EOL_CRLF) {
1509 return "\r\n";
1510 } else if (eolMode == SC_EOL_CR) {
1511 return "\r";
1512 } else {
1513 return "\n";
1517 void Editor::LinesSplit(int pixelWidth) {
1518 if (!RangeContainsProtected(targetStart, targetEnd)) {
1519 if (pixelWidth == 0) {
1520 PRectangle rcText = GetTextRectangle();
1521 pixelWidth = static_cast<int>(rcText.Width());
1523 int lineStart = pdoc->LineFromPosition(targetStart);
1524 int lineEnd = pdoc->LineFromPosition(targetEnd);
1525 const char *eol = StringFromEOLMode(pdoc->eolMode);
1526 UndoGroup ug(pdoc);
1527 for (int line = lineStart; line <= lineEnd; line++) {
1528 AutoSurface surface(this);
1529 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
1530 if (surface && ll) {
1531 unsigned int posLineStart = pdoc->LineStart(line);
1532 view.LayoutLine(*this, line, surface, vs, ll, pixelWidth);
1533 int lengthInsertedTotal = 0;
1534 for (int subLine = 1; subLine < ll->lines; subLine++) {
1535 const int lengthInserted = pdoc->InsertString(
1536 static_cast<int>(posLineStart + lengthInsertedTotal +
1537 ll->LineStart(subLine)),
1538 eol, istrlen(eol));
1539 targetEnd += lengthInserted;
1540 lengthInsertedTotal += lengthInserted;
1543 lineEnd = pdoc->LineFromPosition(targetEnd);
1548 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1549 if (vs.fixedColumnWidth == 0)
1550 return;
1552 AllocateGraphics();
1553 RefreshStyleData();
1554 RefreshPixMaps(surfWindow);
1556 // On GTK+ with Ubuntu overlay scroll bars, the surface may have been finished
1557 // at this point. The Initialised call checks for this case and sets the status
1558 // to be bad which avoids crashes in following calls.
1559 if (!surfWindow->Initialised()) {
1560 return;
1563 PRectangle rcMargin = GetClientRectangle();
1564 Point ptOrigin = GetVisibleOriginInMain();
1565 rcMargin.Move(0, -ptOrigin.y);
1566 rcMargin.left = 0;
1567 rcMargin.right = static_cast<XYPOSITION>(vs.fixedColumnWidth);
1569 if (!rc.Intersects(rcMargin))
1570 return;
1572 Surface *surface;
1573 if (view.bufferedDraw) {
1574 surface = marginView.pixmapSelMargin;
1575 } else {
1576 surface = surfWindow;
1579 // Clip vertically to paint area to avoid drawing line numbers
1580 if (rcMargin.bottom > rc.bottom)
1581 rcMargin.bottom = rc.bottom;
1582 if (rcMargin.top < rc.top)
1583 rcMargin.top = rc.top;
1585 marginView.PaintMargin(surface, topLine, rc, rcMargin, *this, vs);
1587 if (view.bufferedDraw) {
1588 surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *marginView.pixmapSelMargin);
1592 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
1593 view.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1594 marginView.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1595 if (view.bufferedDraw) {
1596 PRectangle rcClient = GetClientRectangle();
1597 if (!view.pixmapLine->Initialised()) {
1599 view.pixmapLine->InitPixMap(static_cast<int>(rcClient.Width()), vs.lineHeight,
1600 surfaceWindow, wMain.GetID());
1602 if (!marginView.pixmapSelMargin->Initialised()) {
1603 marginView.pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
1604 static_cast<int>(rcClient.Height()), surfaceWindow, wMain.GetID());
1609 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
1610 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
1611 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
1612 AllocateGraphics();
1614 RefreshStyleData();
1615 if (paintState == paintAbandoned)
1616 return; // Scroll bars may have changed so need redraw
1617 RefreshPixMaps(surfaceWindow);
1619 paintAbandonedByStyling = false;
1621 StyleToPositionInView(PositionAfterArea(rcArea));
1623 PRectangle rcClient = GetClientRectangle();
1624 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
1625 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1627 if (NotifyUpdateUI()) {
1628 RefreshStyleData();
1629 RefreshPixMaps(surfaceWindow);
1632 // Wrap the visible lines if needed.
1633 if (WrapLines(wsVisible)) {
1634 // The wrapping process has changed the height of some lines so
1635 // abandon this paint for a complete repaint.
1636 if (AbandonPaint()) {
1637 return;
1639 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
1641 PLATFORM_ASSERT(marginView.pixmapSelPattern->Initialised());
1643 if (!view.bufferedDraw)
1644 surfaceWindow->SetClip(rcArea);
1646 if (paintState != paintAbandoned) {
1647 if (vs.marginInside) {
1648 PaintSelMargin(surfaceWindow, rcArea);
1649 PRectangle rcRightMargin = rcClient;
1650 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
1651 if (rcArea.Intersects(rcRightMargin)) {
1652 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
1654 } else { // Else separate view so separate paint event but leftMargin included to allow overlap
1655 PRectangle rcLeftMargin = rcArea;
1656 rcLeftMargin.left = 0;
1657 rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
1658 if (rcArea.Intersects(rcLeftMargin)) {
1659 surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[STYLE_DEFAULT].back);
1664 if (paintState == paintAbandoned) {
1665 // Either styling or NotifyUpdateUI noticed that painting is needed
1666 // outside the current painting rectangle
1667 //Platform::DebugPrintf("Abandoning paint\n");
1668 if (Wrapping()) {
1669 if (paintAbandonedByStyling) {
1670 // Styling has spilled over a line end, such as occurs by starting a multiline
1671 // comment. The width of subsequent text may have changed, so rewrap.
1672 NeedWrapping(cs.DocFromDisplay(topLine));
1675 return;
1678 view.PaintText(surfaceWindow, *this, rcArea, rcClient, vs);
1680 if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) {
1681 if (FineTickerAvailable()) {
1682 scrollWidth = view.lineWidthMaxSeen;
1683 if (!FineTickerRunning(tickWiden)) {
1684 FineTickerStart(tickWiden, 50, 5);
1689 NotifyPainted();
1692 // This is mostly copied from the Paint method but with some things omitted
1693 // such as the margin markers, line numbers, selection and caret
1694 // Should be merged back into a combined Draw method.
1695 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
1696 if (!pfr)
1697 return 0;
1699 AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
1700 if (!surface)
1701 return 0;
1702 AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
1703 if (!surfaceMeasure) {
1704 return 0;
1706 return view.FormatRange(draw, pfr, surface, surfaceMeasure, *this, vs);
1709 int Editor::TextWidth(int style, const char *text) {
1710 RefreshStyleData();
1711 AutoSurface surface(this);
1712 if (surface) {
1713 return static_cast<int>(surface->WidthText(vs.styles[style].font, text, istrlen(text)));
1714 } else {
1715 return 1;
1719 // Empty method is overridden on GTK+ to show / hide scrollbars
1720 void Editor::ReconfigureScrollBars() {}
1722 void Editor::SetScrollBars() {
1723 RefreshStyleData();
1725 int nMax = MaxScrollPos();
1726 int nPage = LinesOnScreen();
1727 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
1728 if (modified) {
1729 DwellEnd(true);
1732 // TODO: ensure always showing as many lines as possible
1733 // May not be, if, for example, window made larger
1734 if (topLine > MaxScrollPos()) {
1735 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
1736 SetVerticalScrollPos();
1737 Redraw();
1739 if (modified) {
1740 if (!AbandonPaint())
1741 Redraw();
1743 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
1746 void Editor::ChangeSize() {
1747 DropGraphics(false);
1748 SetScrollBars();
1749 if (Wrapping()) {
1750 PRectangle rcTextArea = GetClientRectangle();
1751 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1752 rcTextArea.right -= vs.rightMarginWidth;
1753 if (wrapWidth != rcTextArea.Width()) {
1754 NeedWrapping();
1755 Redraw();
1760 int Editor::InsertSpace(int position, unsigned int spaces) {
1761 if (spaces > 0) {
1762 std::string spaceText(spaces, ' ');
1763 const int lengthInserted = pdoc->InsertString(position, spaceText.c_str(), spaces);
1764 position += lengthInserted;
1766 return position;
1769 void Editor::AddChar(char ch) {
1770 char s[2];
1771 s[0] = ch;
1772 s[1] = '\0';
1773 AddCharUTF(s, 1);
1776 void Editor::FilterSelections() {
1777 if (!additionalSelectionTyping && (sel.Count() > 1)) {
1778 SelectionRange rangeOnly = sel.RangeMain();
1779 InvalidateSelection(rangeOnly, true);
1780 sel.SetSelection(rangeOnly);
1784 static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
1785 return *a < *b;
1788 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
1789 void Editor::AddCharUTF(const char *s, unsigned int len, bool treatAsDBCS) {
1790 FilterSelections();
1792 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
1794 // Vector elements point into selection in order to change selection.
1795 std::vector<SelectionRange *> selPtrs;
1796 for (size_t r = 0; r < sel.Count(); r++) {
1797 selPtrs.push_back(&sel.Range(r));
1799 // Order selections by position in document.
1800 std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
1802 // Loop in reverse to avoid disturbing positions of selections yet to be processed.
1803 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
1804 rit != selPtrs.rend(); ++rit) {
1805 SelectionRange *currentSel = *rit;
1806 if (!RangeContainsProtected(currentSel->Start().Position(),
1807 currentSel->End().Position())) {
1808 int positionInsert = currentSel->Start().Position();
1809 if (!currentSel->Empty()) {
1810 if (currentSel->Length()) {
1811 pdoc->DeleteChars(positionInsert, currentSel->Length());
1812 currentSel->ClearVirtualSpace();
1813 } else {
1814 // Range is all virtual so collapse to start of virtual space
1815 currentSel->MinimizeVirtualSpace();
1817 } else if (inOverstrike) {
1818 if (positionInsert < pdoc->Length()) {
1819 if (!pdoc->IsPositionInLineEnd(positionInsert)) {
1820 pdoc->DelChar(positionInsert);
1821 currentSel->ClearVirtualSpace();
1825 positionInsert = InsertSpace(positionInsert, currentSel->caret.VirtualSpace());
1826 const int lengthInserted = pdoc->InsertString(positionInsert, s, len);
1827 if (lengthInserted > 0) {
1828 currentSel->caret.SetPosition(positionInsert + lengthInserted);
1829 currentSel->anchor.SetPosition(positionInsert + lengthInserted);
1831 currentSel->ClearVirtualSpace();
1832 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1833 if (Wrapping()) {
1834 AutoSurface surface(this);
1835 if (surface) {
1836 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
1837 SetScrollBars();
1838 SetVerticalScrollPos();
1839 Redraw();
1846 if (Wrapping()) {
1847 SetScrollBars();
1849 ThinRectangularRange();
1850 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1851 EnsureCaretVisible();
1852 // Avoid blinking during rapid typing:
1853 ShowCaretAtCurrentPosition();
1854 if ((caretSticky == SC_CARETSTICKY_OFF) ||
1855 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
1856 SetLastXChosen();
1859 if (treatAsDBCS) {
1860 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
1861 static_cast<unsigned char>(s[1]));
1862 } else if (len > 0) {
1863 int byte = static_cast<unsigned char>(s[0]);
1864 if ((byte < 0xC0) || (1 == len)) {
1865 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
1866 // characters when not in UTF-8 mode.
1867 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
1868 // characters representing themselves.
1869 } else {
1870 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
1871 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
1872 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
1873 if (byte < 0xE0) {
1874 int byte2 = static_cast<unsigned char>(s[1]);
1875 if ((byte2 & 0xC0) == 0x80) {
1876 // Two-byte-character lead-byte followed by a trail-byte.
1877 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
1879 // A two-byte-character lead-byte not followed by trail-byte
1880 // represents itself.
1881 } else if (byte < 0xF0) {
1882 int byte2 = static_cast<unsigned char>(s[1]);
1883 int byte3 = static_cast<unsigned char>(s[2]);
1884 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
1885 // Three-byte-character lead byte followed by two trail bytes.
1886 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
1887 (byte3 & 0x3F));
1889 // A three-byte-character lead-byte not followed by two trail-bytes
1890 // represents itself.
1893 NotifyChar(byte);
1896 if (recordingMacro) {
1897 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
1901 void Editor::InsertPaste(const char *text, int len) {
1902 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
1903 SelectionPosition selStart = sel.Start();
1904 selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
1905 const int lengthInserted = pdoc->InsertString(selStart.Position(), text, len);
1906 if (lengthInserted > 0) {
1907 SetEmptySelection(selStart.Position() + lengthInserted);
1909 } else {
1910 // SC_MULTIPASTE_EACH
1911 for (size_t r=0; r<sel.Count(); r++) {
1912 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
1913 sel.Range(r).End().Position())) {
1914 int positionInsert = sel.Range(r).Start().Position();
1915 if (!sel.Range(r).Empty()) {
1916 if (sel.Range(r).Length()) {
1917 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
1918 sel.Range(r).ClearVirtualSpace();
1919 } else {
1920 // Range is all virtual so collapse to start of virtual space
1921 sel.Range(r).MinimizeVirtualSpace();
1924 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
1925 const int lengthInserted = pdoc->InsertString(positionInsert, text, len);
1926 if (lengthInserted > 0) {
1927 sel.Range(r).caret.SetPosition(positionInsert + lengthInserted);
1928 sel.Range(r).anchor.SetPosition(positionInsert + lengthInserted);
1930 sel.Range(r).ClearVirtualSpace();
1936 void Editor::InsertPasteShape(const char *text, int len, PasteShape shape) {
1937 std::string convertedText;
1938 if (convertPastes) {
1939 // Convert line endings of the paste into our local line-endings mode
1940 convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
1941 len = static_cast<int>(convertedText.length());
1942 text = convertedText.c_str();
1944 if (shape == pasteRectangular) {
1945 PasteRectangular(sel.Start(), text, len);
1946 } else {
1947 if (shape == pasteLine) {
1948 int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
1949 int lengthInserted = pdoc->InsertString(insertPos, text, len);
1950 // add the newline if necessary
1951 if ((len > 0) && (text[len - 1] != '\n' && text[len - 1] != '\r')) {
1952 const char *endline = StringFromEOLMode(pdoc->eolMode);
1953 int length = static_cast<int>(strlen(endline));
1954 lengthInserted += pdoc->InsertString(insertPos + lengthInserted, endline, length);
1956 if (sel.MainCaret() == insertPos) {
1957 SetEmptySelection(sel.MainCaret() + lengthInserted);
1959 } else {
1960 InsertPaste(text, len);
1965 void Editor::ClearSelection(bool retainMultipleSelections) {
1966 if (!sel.IsRectangular() && !retainMultipleSelections)
1967 FilterSelections();
1968 UndoGroup ug(pdoc);
1969 for (size_t r=0; r<sel.Count(); r++) {
1970 if (!sel.Range(r).Empty()) {
1971 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
1972 sel.Range(r).End().Position())) {
1973 pdoc->DeleteChars(sel.Range(r).Start().Position(),
1974 sel.Range(r).Length());
1975 sel.Range(r) = SelectionRange(sel.Range(r).Start());
1979 ThinRectangularRange();
1980 sel.RemoveDuplicates();
1981 ClaimSelection();
1984 void Editor::ClearAll() {
1986 UndoGroup ug(pdoc);
1987 if (0 != pdoc->Length()) {
1988 pdoc->DeleteChars(0, pdoc->Length());
1990 if (!pdoc->IsReadOnly()) {
1991 cs.Clear();
1992 pdoc->AnnotationClearAll();
1993 pdoc->MarginClearAll();
1997 view.ClearAllTabstops();
1999 sel.Clear();
2000 SetTopLine(0);
2001 SetVerticalScrollPos();
2002 InvalidateStyleRedraw();
2005 void Editor::ClearDocumentStyle() {
2006 Decoration *deco = pdoc->decorations.root;
2007 while (deco) {
2008 // Save next in case deco deleted
2009 Decoration *decoNext = deco->next;
2010 if (deco->indicator < INDIC_CONTAINER) {
2011 pdoc->decorations.SetCurrentIndicator(deco->indicator);
2012 pdoc->DecorationFillRange(0, 0, pdoc->Length());
2014 deco = decoNext;
2016 pdoc->StartStyling(0, '\377');
2017 pdoc->SetStyleFor(pdoc->Length(), 0);
2018 cs.ShowAll();
2019 SetAnnotationHeights(0, pdoc->LinesTotal());
2020 pdoc->ClearLevels();
2023 void Editor::CopyAllowLine() {
2024 SelectionText selectedText;
2025 CopySelectionRange(&selectedText, true);
2026 CopyToClipboard(selectedText);
2029 void Editor::Cut() {
2030 pdoc->CheckReadOnly();
2031 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
2032 Copy();
2033 ClearSelection();
2037 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
2038 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
2039 return;
2041 sel.Clear();
2042 sel.RangeMain() = SelectionRange(pos);
2043 int line = pdoc->LineFromPosition(sel.MainCaret());
2044 UndoGroup ug(pdoc);
2045 sel.RangeMain().caret = SelectionPosition(
2046 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
2047 int xInsert = XFromPosition(sel.RangeMain().caret);
2048 bool prevCr = false;
2049 while ((len > 0) && IsEOLChar(ptr[len-1]))
2050 len--;
2051 for (int i = 0; i < len; i++) {
2052 if (IsEOLChar(ptr[i])) {
2053 if ((ptr[i] == '\r') || (!prevCr))
2054 line++;
2055 if (line >= pdoc->LinesTotal()) {
2056 if (pdoc->eolMode != SC_EOL_LF)
2057 pdoc->InsertString(pdoc->Length(), "\r", 1);
2058 if (pdoc->eolMode != SC_EOL_CR)
2059 pdoc->InsertString(pdoc->Length(), "\n", 1);
2061 // Pad the end of lines with spaces if required
2062 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
2063 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
2064 while (XFromPosition(sel.MainCaret()) < xInsert) {
2065 const int lengthInserted = pdoc->InsertString(sel.MainCaret(), " ", 1);
2066 sel.RangeMain().caret.Add(lengthInserted);
2069 prevCr = ptr[i] == '\r';
2070 } else {
2071 const int lengthInserted = pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
2072 sel.RangeMain().caret.Add(lengthInserted);
2073 prevCr = false;
2076 SetEmptySelection(pos);
2079 bool Editor::CanPaste() {
2080 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
2083 void Editor::Clear() {
2084 // If multiple selections, don't delete EOLS
2085 if (sel.Empty()) {
2086 bool singleVirtual = false;
2087 if ((sel.Count() == 1) &&
2088 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
2089 sel.RangeMain().Start().VirtualSpace()) {
2090 singleVirtual = true;
2092 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
2093 for (size_t r=0; r<sel.Count(); r++) {
2094 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
2095 if (sel.Range(r).Start().VirtualSpace()) {
2096 if (sel.Range(r).anchor < sel.Range(r).caret)
2097 sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
2098 else
2099 sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
2101 if ((sel.Count() == 1) || !pdoc->IsPositionInLineEnd(sel.Range(r).caret.Position())) {
2102 pdoc->DelChar(sel.Range(r).caret.Position());
2103 sel.Range(r).ClearVirtualSpace();
2104 } // else multiple selection so don't eat line ends
2105 } else {
2106 sel.Range(r).ClearVirtualSpace();
2109 } else {
2110 ClearSelection();
2112 sel.RemoveDuplicates();
2113 ShowCaretAtCurrentPosition(); // Avoid blinking
2116 void Editor::SelectAll() {
2117 sel.Clear();
2118 SetSelection(0, pdoc->Length());
2119 Redraw();
2122 void Editor::Undo() {
2123 if (pdoc->CanUndo()) {
2124 InvalidateCaret();
2125 int newPos = pdoc->Undo();
2126 if (newPos >= 0)
2127 SetEmptySelection(newPos);
2128 EnsureCaretVisible();
2132 void Editor::Redo() {
2133 if (pdoc->CanRedo()) {
2134 int newPos = pdoc->Redo();
2135 if (newPos >= 0)
2136 SetEmptySelection(newPos);
2137 EnsureCaretVisible();
2141 void Editor::DelCharBack(bool allowLineStartDeletion) {
2142 RefreshStyleData();
2143 if (!sel.IsRectangular())
2144 FilterSelections();
2145 if (sel.IsRectangular())
2146 allowLineStartDeletion = false;
2147 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
2148 if (sel.Empty()) {
2149 for (size_t r=0; r<sel.Count(); r++) {
2150 if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
2151 if (sel.Range(r).caret.VirtualSpace()) {
2152 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
2153 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
2154 } else {
2155 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
2156 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
2157 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
2158 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
2159 UndoGroup ugInner(pdoc, !ug.Needed());
2160 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
2161 int indentationStep = pdoc->IndentSize();
2162 int indentationChange = indentation % indentationStep;
2163 if (indentationChange == 0)
2164 indentationChange = indentationStep;
2165 const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationChange);
2166 // SetEmptySelection
2167 sel.Range(r) = SelectionRange(posSelect);
2168 } else {
2169 pdoc->DelCharBack(sel.Range(r).caret.Position());
2173 } else {
2174 sel.Range(r).ClearVirtualSpace();
2177 ThinRectangularRange();
2178 } else {
2179 ClearSelection();
2181 sel.RemoveDuplicates();
2182 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
2183 // Avoid blinking during rapid typing:
2184 ShowCaretAtCurrentPosition();
2187 int Editor::ModifierFlags(bool shift, bool ctrl, bool alt, bool meta) {
2188 return
2189 (shift ? SCI_SHIFT : 0) |
2190 (ctrl ? SCI_CTRL : 0) |
2191 (alt ? SCI_ALT : 0) |
2192 (meta ? SCI_META : 0);
2195 void Editor::NotifyFocus(bool focus) {
2196 SCNotification scn = {};
2197 scn.nmhdr.code = focus ? SCN_FOCUSIN : SCN_FOCUSOUT;
2198 NotifyParent(scn);
2201 void Editor::SetCtrlID(int identifier) {
2202 ctrlID = identifier;
2205 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
2206 SCNotification scn = {};
2207 scn.nmhdr.code = SCN_STYLENEEDED;
2208 scn.position = endStyleNeeded;
2209 NotifyParent(scn);
2212 void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
2213 NotifyStyleToNeeded(endStyleNeeded);
2216 void Editor::NotifyLexerChanged(Document *, void *) {
2219 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
2220 errorStatus = status;
2223 void Editor::NotifyChar(int ch) {
2224 SCNotification scn = {};
2225 scn.nmhdr.code = SCN_CHARADDED;
2226 scn.ch = ch;
2227 NotifyParent(scn);
2230 void Editor::NotifySavePoint(bool isSavePoint) {
2231 SCNotification scn = {};
2232 if (isSavePoint) {
2233 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
2234 } else {
2235 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
2237 NotifyParent(scn);
2240 void Editor::NotifyModifyAttempt() {
2241 SCNotification scn = {};
2242 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
2243 NotifyParent(scn);
2246 void Editor::NotifyDoubleClick(Point pt, int modifiers) {
2247 SCNotification scn = {};
2248 scn.nmhdr.code = SCN_DOUBLECLICK;
2249 scn.line = LineFromLocation(pt);
2250 scn.position = PositionFromLocation(pt, true);
2251 scn.modifiers = modifiers;
2252 NotifyParent(scn);
2255 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
2256 NotifyDoubleClick(pt, ModifierFlags(shift, ctrl, alt));
2259 void Editor::NotifyHotSpotDoubleClicked(int position, int modifiers) {
2260 SCNotification scn = {};
2261 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
2262 scn.position = position;
2263 scn.modifiers = modifiers;
2264 NotifyParent(scn);
2267 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
2268 NotifyHotSpotDoubleClicked(position, ModifierFlags(shift, ctrl, alt));
2271 void Editor::NotifyHotSpotClicked(int position, int modifiers) {
2272 SCNotification scn = {};
2273 scn.nmhdr.code = SCN_HOTSPOTCLICK;
2274 scn.position = position;
2275 scn.modifiers = modifiers;
2276 NotifyParent(scn);
2279 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
2280 NotifyHotSpotClicked(position, ModifierFlags(shift, ctrl, alt));
2283 void Editor::NotifyHotSpotReleaseClick(int position, int modifiers) {
2284 SCNotification scn = {};
2285 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
2286 scn.position = position;
2287 scn.modifiers = modifiers;
2288 NotifyParent(scn);
2291 void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
2292 NotifyHotSpotReleaseClick(position, ModifierFlags(shift, ctrl, alt));
2295 bool Editor::NotifyUpdateUI() {
2296 if (needUpdateUI) {
2297 SCNotification scn = {};
2298 scn.nmhdr.code = SCN_UPDATEUI;
2299 scn.updated = needUpdateUI;
2300 NotifyParent(scn);
2301 needUpdateUI = 0;
2302 return true;
2304 return false;
2307 void Editor::NotifyPainted() {
2308 SCNotification scn = {};
2309 scn.nmhdr.code = SCN_PAINTED;
2310 NotifyParent(scn);
2313 void Editor::NotifyIndicatorClick(bool click, int position, int modifiers) {
2314 int mask = pdoc->decorations.AllOnFor(position);
2315 if ((click && mask) || pdoc->decorations.clickNotified) {
2316 SCNotification scn = {};
2317 pdoc->decorations.clickNotified = click;
2318 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
2319 scn.modifiers = modifiers;
2320 scn.position = position;
2321 NotifyParent(scn);
2325 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
2326 NotifyIndicatorClick(click, position, ModifierFlags(shift, ctrl, alt));
2329 bool Editor::NotifyMarginClick(Point pt, int modifiers) {
2330 int marginClicked = -1;
2331 int x = vs.textStart - vs.fixedColumnWidth;
2332 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
2333 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
2334 marginClicked = margin;
2335 x += vs.ms[margin].width;
2337 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
2338 int position = pdoc->LineStart(LineFromLocation(pt));
2339 if ((vs.ms[marginClicked].mask & SC_MASK_FOLDERS) && (foldAutomatic & SC_AUTOMATICFOLD_CLICK)) {
2340 const bool ctrl = (modifiers & SCI_CTRL) != 0;
2341 const bool shift = (modifiers & SCI_SHIFT) != 0;
2342 int lineClick = pdoc->LineFromPosition(position);
2343 if (shift && ctrl) {
2344 FoldAll(SC_FOLDACTION_TOGGLE);
2345 } else {
2346 int levelClick = pdoc->GetLevel(lineClick);
2347 if (levelClick & SC_FOLDLEVELHEADERFLAG) {
2348 if (shift) {
2349 // Ensure all children visible
2350 FoldExpand(lineClick, SC_FOLDACTION_EXPAND, levelClick);
2351 } else if (ctrl) {
2352 FoldExpand(lineClick, SC_FOLDACTION_TOGGLE, levelClick);
2353 } else {
2354 // Toggle this line
2355 FoldLine(lineClick, SC_FOLDACTION_TOGGLE);
2359 return true;
2361 SCNotification scn = {};
2362 scn.nmhdr.code = SCN_MARGINCLICK;
2363 scn.modifiers = modifiers;
2364 scn.position = position;
2365 scn.margin = marginClicked;
2366 NotifyParent(scn);
2367 return true;
2368 } else {
2369 return false;
2373 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
2374 return NotifyMarginClick(pt, ModifierFlags(shift, ctrl, alt));
2377 void Editor::NotifyNeedShown(int pos, int len) {
2378 SCNotification scn = {};
2379 scn.nmhdr.code = SCN_NEEDSHOWN;
2380 scn.position = pos;
2381 scn.length = len;
2382 NotifyParent(scn);
2385 void Editor::NotifyDwelling(Point pt, bool state) {
2386 SCNotification scn = {};
2387 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
2388 scn.position = PositionFromLocation(pt, true);
2389 scn.x = static_cast<int>(pt.x + vs.ExternalMarginWidth());
2390 scn.y = static_cast<int>(pt.y);
2391 NotifyParent(scn);
2394 void Editor::NotifyZoom() {
2395 SCNotification scn = {};
2396 scn.nmhdr.code = SCN_ZOOM;
2397 NotifyParent(scn);
2400 // Notifications from document
2401 void Editor::NotifyModifyAttempt(Document *, void *) {
2402 //Platform::DebugPrintf("** Modify Attempt\n");
2403 NotifyModifyAttempt();
2406 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
2407 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
2408 NotifySavePoint(atSavePoint);
2411 void Editor::CheckModificationForWrap(DocModification mh) {
2412 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
2413 view.llc.Invalidate(LineLayout::llCheckTextAndStyle);
2414 int lineDoc = pdoc->LineFromPosition(mh.position);
2415 int lines = Platform::Maximum(0, mh.linesAdded);
2416 if (Wrapping()) {
2417 NeedWrapping(lineDoc, lineDoc + lines + 1);
2419 RefreshStyleData();
2420 // Fix up annotation heights
2421 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
2425 // Move a position so it is still after the same character as before the insertion.
2426 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
2427 if (position > startInsertion) {
2428 return position + length;
2430 return position;
2433 // Move a position so it is still after the same character as before the deletion if that
2434 // character is still present else after the previous surviving character.
2435 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
2436 if (position > startDeletion) {
2437 int endDeletion = startDeletion + length;
2438 if (position > endDeletion) {
2439 return position - length;
2440 } else {
2441 return startDeletion;
2443 } else {
2444 return position;
2448 void Editor::NotifyModified(Document *, DocModification mh, void *) {
2449 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
2450 if (paintState == painting) {
2451 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
2453 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
2454 if (paintState == painting) {
2455 CheckForChangeOutsidePaint(
2456 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
2457 } else {
2458 // Could check that change is before last visible line.
2459 Redraw();
2462 if (mh.modificationType & SC_MOD_CHANGETABSTOPS) {
2463 Redraw();
2465 if (mh.modificationType & SC_MOD_LEXERSTATE) {
2466 if (paintState == painting) {
2467 CheckForChangeOutsidePaint(
2468 Range(mh.position, mh.position + mh.length));
2469 } else {
2470 Redraw();
2473 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
2474 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
2475 pdoc->IncrementStyleClock();
2477 if (paintState == notPainting) {
2478 if (mh.position < pdoc->LineStart(topLine)) {
2479 // Styling performed before this view
2480 Redraw();
2481 } else {
2482 InvalidateRange(mh.position, mh.position + mh.length);
2485 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
2486 view.llc.Invalidate(LineLayout::llCheckTextAndStyle);
2488 } else {
2489 // Move selection and brace highlights
2490 if (mh.modificationType & SC_MOD_INSERTTEXT) {
2491 sel.MovePositions(true, mh.position, mh.length);
2492 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
2493 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
2494 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
2495 sel.MovePositions(false, mh.position, mh.length);
2496 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
2497 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
2499 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && cs.HiddenLines()) {
2500 // Some lines are hidden so may need shown.
2501 // TODO: check if the modified area is hidden.
2502 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
2503 int lineOfPos = pdoc->LineFromPosition(mh.position);
2504 bool insertingNewLine = false;
2505 for (int i=0; i < mh.length; i++) {
2506 if ((mh.text[i] == '\n') || (mh.text[i] == '\r'))
2507 insertingNewLine = true;
2509 if (insertingNewLine && (mh.position != pdoc->LineStart(lineOfPos)))
2510 NeedShown(mh.position, pdoc->LineStart(lineOfPos+1) - mh.position);
2511 else
2512 NeedShown(mh.position, 0);
2513 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
2514 NeedShown(mh.position, mh.length);
2517 if (mh.linesAdded != 0) {
2518 // Update contraction state for inserted and removed lines
2519 // lineOfPos should be calculated in context of state before modification, shouldn't it
2520 int lineOfPos = pdoc->LineFromPosition(mh.position);
2521 if (mh.position > pdoc->LineStart(lineOfPos))
2522 lineOfPos++; // Affecting subsequent lines
2523 if (mh.linesAdded > 0) {
2524 cs.InsertLines(lineOfPos, mh.linesAdded);
2525 } else {
2526 cs.DeleteLines(lineOfPos, -mh.linesAdded);
2528 view.LinesAddedOrRemoved(lineOfPos, mh.linesAdded);
2530 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
2531 int lineDoc = pdoc->LineFromPosition(mh.position);
2532 if (vs.annotationVisible) {
2533 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
2534 Redraw();
2537 CheckModificationForWrap(mh);
2538 if (mh.linesAdded != 0) {
2539 // Avoid scrolling of display if change before current display
2540 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
2541 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
2542 if (newTop != topLine) {
2543 SetTopLine(newTop);
2544 SetVerticalScrollPos();
2548 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
2549 QueueIdleWork(WorkNeeded::workStyle, pdoc->Length());
2550 Redraw();
2552 } else {
2553 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
2554 QueueIdleWork(WorkNeeded::workStyle, mh.position + mh.length);
2555 InvalidateRange(mh.position, mh.position + mh.length);
2560 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
2561 SetScrollBars();
2564 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
2565 if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) {
2566 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
2567 // Fold changes can affect the drawing of following lines so redraw whole margin
2568 RedrawSelMargin(marginView.highlightDelimiter.isEnabled ? -1 : mh.line - 1, true);
2569 } else {
2570 RedrawSelMargin(mh.line);
2574 if ((mh.modificationType & SC_MOD_CHANGEFOLD) && (foldAutomatic & SC_AUTOMATICFOLD_CHANGE)) {
2575 FoldChanged(mh.line, mh.foldLevelNow, mh.foldLevelPrev);
2578 // NOW pay the piper WRT "deferred" visual updates
2579 if (IsLastStep(mh)) {
2580 SetScrollBars();
2581 Redraw();
2584 // If client wants to see this modification
2585 if (mh.modificationType & modEventMask) {
2586 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
2587 // Real modification made to text of document.
2588 NotifyChange(); // Send EN_CHANGE
2591 SCNotification scn = {};
2592 scn.nmhdr.code = SCN_MODIFIED;
2593 scn.position = mh.position;
2594 scn.modificationType = mh.modificationType;
2595 scn.text = mh.text;
2596 scn.length = mh.length;
2597 scn.linesAdded = mh.linesAdded;
2598 scn.line = mh.line;
2599 scn.foldLevelNow = mh.foldLevelNow;
2600 scn.foldLevelPrev = mh.foldLevelPrev;
2601 scn.token = mh.token;
2602 scn.annotationLinesAdded = mh.annotationLinesAdded;
2603 NotifyParent(scn);
2607 void Editor::NotifyDeleted(Document *, void *) {
2608 /* Do nothing */
2611 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2613 // Enumerates all macroable messages
2614 switch (iMessage) {
2615 case SCI_CUT:
2616 case SCI_COPY:
2617 case SCI_PASTE:
2618 case SCI_CLEAR:
2619 case SCI_REPLACESEL:
2620 case SCI_ADDTEXT:
2621 case SCI_INSERTTEXT:
2622 case SCI_APPENDTEXT:
2623 case SCI_CLEARALL:
2624 case SCI_SELECTALL:
2625 case SCI_GOTOLINE:
2626 case SCI_GOTOPOS:
2627 case SCI_SEARCHANCHOR:
2628 case SCI_SEARCHNEXT:
2629 case SCI_SEARCHPREV:
2630 case SCI_LINEDOWN:
2631 case SCI_LINEDOWNEXTEND:
2632 case SCI_PARADOWN:
2633 case SCI_PARADOWNEXTEND:
2634 case SCI_LINEUP:
2635 case SCI_LINEUPEXTEND:
2636 case SCI_PARAUP:
2637 case SCI_PARAUPEXTEND:
2638 case SCI_CHARLEFT:
2639 case SCI_CHARLEFTEXTEND:
2640 case SCI_CHARRIGHT:
2641 case SCI_CHARRIGHTEXTEND:
2642 case SCI_WORDLEFT:
2643 case SCI_WORDLEFTEXTEND:
2644 case SCI_WORDRIGHT:
2645 case SCI_WORDRIGHTEXTEND:
2646 case SCI_WORDPARTLEFT:
2647 case SCI_WORDPARTLEFTEXTEND:
2648 case SCI_WORDPARTRIGHT:
2649 case SCI_WORDPARTRIGHTEXTEND:
2650 case SCI_WORDLEFTEND:
2651 case SCI_WORDLEFTENDEXTEND:
2652 case SCI_WORDRIGHTEND:
2653 case SCI_WORDRIGHTENDEXTEND:
2654 case SCI_HOME:
2655 case SCI_HOMEEXTEND:
2656 case SCI_LINEEND:
2657 case SCI_LINEENDEXTEND:
2658 case SCI_HOMEWRAP:
2659 case SCI_HOMEWRAPEXTEND:
2660 case SCI_LINEENDWRAP:
2661 case SCI_LINEENDWRAPEXTEND:
2662 case SCI_DOCUMENTSTART:
2663 case SCI_DOCUMENTSTARTEXTEND:
2664 case SCI_DOCUMENTEND:
2665 case SCI_DOCUMENTENDEXTEND:
2666 case SCI_STUTTEREDPAGEUP:
2667 case SCI_STUTTEREDPAGEUPEXTEND:
2668 case SCI_STUTTEREDPAGEDOWN:
2669 case SCI_STUTTEREDPAGEDOWNEXTEND:
2670 case SCI_PAGEUP:
2671 case SCI_PAGEUPEXTEND:
2672 case SCI_PAGEDOWN:
2673 case SCI_PAGEDOWNEXTEND:
2674 case SCI_EDITTOGGLEOVERTYPE:
2675 case SCI_CANCEL:
2676 case SCI_DELETEBACK:
2677 case SCI_TAB:
2678 case SCI_BACKTAB:
2679 case SCI_FORMFEED:
2680 case SCI_VCHOME:
2681 case SCI_VCHOMEEXTEND:
2682 case SCI_VCHOMEWRAP:
2683 case SCI_VCHOMEWRAPEXTEND:
2684 case SCI_VCHOMEDISPLAY:
2685 case SCI_VCHOMEDISPLAYEXTEND:
2686 case SCI_DELWORDLEFT:
2687 case SCI_DELWORDRIGHT:
2688 case SCI_DELWORDRIGHTEND:
2689 case SCI_DELLINELEFT:
2690 case SCI_DELLINERIGHT:
2691 case SCI_LINECOPY:
2692 case SCI_LINECUT:
2693 case SCI_LINEDELETE:
2694 case SCI_LINETRANSPOSE:
2695 case SCI_LINEDUPLICATE:
2696 case SCI_LOWERCASE:
2697 case SCI_UPPERCASE:
2698 case SCI_LINESCROLLDOWN:
2699 case SCI_LINESCROLLUP:
2700 case SCI_DELETEBACKNOTLINE:
2701 case SCI_HOMEDISPLAY:
2702 case SCI_HOMEDISPLAYEXTEND:
2703 case SCI_LINEENDDISPLAY:
2704 case SCI_LINEENDDISPLAYEXTEND:
2705 case SCI_SETSELECTIONMODE:
2706 case SCI_LINEDOWNRECTEXTEND:
2707 case SCI_LINEUPRECTEXTEND:
2708 case SCI_CHARLEFTRECTEXTEND:
2709 case SCI_CHARRIGHTRECTEXTEND:
2710 case SCI_HOMERECTEXTEND:
2711 case SCI_VCHOMERECTEXTEND:
2712 case SCI_LINEENDRECTEXTEND:
2713 case SCI_PAGEUPRECTEXTEND:
2714 case SCI_PAGEDOWNRECTEXTEND:
2715 case SCI_SELECTIONDUPLICATE:
2716 case SCI_COPYALLOWLINE:
2717 case SCI_VERTICALCENTRECARET:
2718 case SCI_MOVESELECTEDLINESUP:
2719 case SCI_MOVESELECTEDLINESDOWN:
2720 case SCI_SCROLLTOSTART:
2721 case SCI_SCROLLTOEND:
2722 break;
2724 // Filter out all others like display changes. Also, newlines are redundant
2725 // with char insert messages.
2726 case SCI_NEWLINE:
2727 default:
2728 // printf("Filtered out %ld of macro recording\n", iMessage);
2729 return;
2732 // Send notification
2733 SCNotification scn = {};
2734 scn.nmhdr.code = SCN_MACRORECORD;
2735 scn.message = iMessage;
2736 scn.wParam = wParam;
2737 scn.lParam = lParam;
2738 NotifyParent(scn);
2741 // Something has changed that the container should know about
2742 void Editor::ContainerNeedsUpdate(int flags) {
2743 needUpdateUI |= flags;
2747 * Force scroll and keep position relative to top of window.
2749 * If stuttered = true and not already at first/last row, move to first/last row of window.
2750 * If stuttered = true and already at first/last row, scroll as normal.
2752 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
2753 int topLineNew;
2754 SelectionPosition newPos;
2756 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
2757 int topStutterLine = topLine + caretYSlop;
2758 int bottomStutterLine =
2759 pdoc->LineFromPosition(PositionFromLocation(
2760 Point::FromInts(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
2761 - caretYSlop - 1;
2763 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
2764 topLineNew = topLine;
2765 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
2766 false, false, UserVirtualSpace());
2768 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
2769 topLineNew = topLine;
2770 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
2771 false, false, UserVirtualSpace());
2773 } else {
2774 Point pt = LocationFromPosition(sel.MainCaret());
2776 topLineNew = Platform::Clamp(
2777 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
2778 newPos = SPositionFromLocation(
2779 Point::FromInts(lastXChosen - xOffset, static_cast<int>(pt.y) + direction * (vs.lineHeight * LinesToScroll())),
2780 false, false, UserVirtualSpace());
2783 if (topLineNew != topLine) {
2784 SetTopLine(topLineNew);
2785 MovePositionTo(newPos, selt);
2786 Redraw();
2787 SetVerticalScrollPos();
2788 } else {
2789 MovePositionTo(newPos, selt);
2793 void Editor::ChangeCaseOfSelection(int caseMapping) {
2794 UndoGroup ug(pdoc);
2795 for (size_t r=0; r<sel.Count(); r++) {
2796 SelectionRange current = sel.Range(r);
2797 SelectionRange currentNoVS = current;
2798 currentNoVS.ClearVirtualSpace();
2799 size_t rangeBytes = currentNoVS.Length();
2800 if (rangeBytes > 0) {
2801 std::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position());
2803 std::string sMapped = CaseMapString(sText, caseMapping);
2805 if (sMapped != sText) {
2806 size_t firstDifference = 0;
2807 while (sMapped[firstDifference] == sText[firstDifference])
2808 firstDifference++;
2809 size_t lastDifferenceText = sText.size() - 1;
2810 size_t lastDifferenceMapped = sMapped.size() - 1;
2811 while (sMapped[lastDifferenceMapped] == sText[lastDifferenceText]) {
2812 lastDifferenceText--;
2813 lastDifferenceMapped--;
2815 size_t endDifferenceText = sText.size() - 1 - lastDifferenceText;
2816 pdoc->DeleteChars(
2817 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
2818 static_cast<int>(rangeBytes - firstDifference - endDifferenceText));
2819 const int lengthChange = static_cast<int>(lastDifferenceMapped - firstDifference + 1);
2820 const int lengthInserted = pdoc->InsertString(
2821 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
2822 sMapped.c_str() + firstDifference,
2823 lengthChange);
2824 // Automatic movement changes selection so reset to exactly the same as it was.
2825 int diffSizes = static_cast<int>(sMapped.size() - sText.size()) + lengthInserted - lengthChange;
2826 if (diffSizes != 0) {
2827 if (current.anchor > current.caret)
2828 current.anchor.Add(diffSizes);
2829 else
2830 current.caret.Add(diffSizes);
2832 sel.Range(r) = current;
2838 void Editor::LineTranspose() {
2839 int line = pdoc->LineFromPosition(sel.MainCaret());
2840 if (line > 0) {
2841 UndoGroup ug(pdoc);
2843 const int startPrevious = pdoc->LineStart(line - 1);
2844 const std::string linePrevious = RangeText(startPrevious, pdoc->LineEnd(line - 1));
2846 int startCurrent = pdoc->LineStart(line);
2847 const std::string lineCurrent = RangeText(startCurrent, pdoc->LineEnd(line));
2849 pdoc->DeleteChars(startCurrent, static_cast<int>(lineCurrent.length()));
2850 pdoc->DeleteChars(startPrevious, static_cast<int>(linePrevious.length()));
2851 startCurrent -= static_cast<int>(linePrevious.length());
2853 startCurrent += pdoc->InsertString(startPrevious, lineCurrent.c_str(),
2854 static_cast<int>(lineCurrent.length()));
2855 pdoc->InsertString(startCurrent, linePrevious.c_str(),
2856 static_cast<int>(linePrevious.length()));
2857 // Move caret to start of current line
2858 MovePositionTo(SelectionPosition(startCurrent));
2862 void Editor::Duplicate(bool forLine) {
2863 if (sel.Empty()) {
2864 forLine = true;
2866 UndoGroup ug(pdoc);
2867 const char *eol = "";
2868 int eolLen = 0;
2869 if (forLine) {
2870 eol = StringFromEOLMode(pdoc->eolMode);
2871 eolLen = istrlen(eol);
2873 for (size_t r=0; r<sel.Count(); r++) {
2874 SelectionPosition start = sel.Range(r).Start();
2875 SelectionPosition end = sel.Range(r).End();
2876 if (forLine) {
2877 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
2878 start = SelectionPosition(pdoc->LineStart(line));
2879 end = SelectionPosition(pdoc->LineEnd(line));
2881 std::string text = RangeText(start.Position(), end.Position());
2882 int lengthInserted = eolLen;
2883 if (forLine)
2884 lengthInserted = pdoc->InsertString(end.Position(), eol, eolLen);
2885 pdoc->InsertString(end.Position() + lengthInserted, text.c_str(), static_cast<int>(text.length()));
2887 if (sel.Count() && sel.IsRectangular()) {
2888 SelectionPosition last = sel.Last();
2889 if (forLine) {
2890 int line = pdoc->LineFromPosition(last.Position());
2891 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
2893 if (sel.Rectangular().anchor > sel.Rectangular().caret)
2894 sel.Rectangular().anchor = last;
2895 else
2896 sel.Rectangular().caret = last;
2897 SetRectangularRange();
2901 void Editor::CancelModes() {
2902 sel.SetMoveExtends(false);
2905 void Editor::NewLine() {
2906 // Remove non-main ranges
2907 InvalidateSelection(sel.RangeMain(), true);
2908 sel.SetSelection(sel.RangeMain());
2909 sel.RangeMain().ClearVirtualSpace();
2911 // Clear main range and insert line end
2912 bool needGroupUndo = !sel.Empty();
2913 if (needGroupUndo)
2914 pdoc->BeginUndoAction();
2916 if (!sel.Empty())
2917 ClearSelection();
2918 const char *eol = "\n";
2919 if (pdoc->eolMode == SC_EOL_CRLF) {
2920 eol = "\r\n";
2921 } else if (pdoc->eolMode == SC_EOL_CR) {
2922 eol = "\r";
2923 } // else SC_EOL_LF -> "\n" already set
2924 const int insertLength = pdoc->InsertString(sel.MainCaret(), eol, istrlen(eol));
2925 // Want to end undo group before NotifyChar as applications often modify text here
2926 if (needGroupUndo)
2927 pdoc->EndUndoAction();
2928 if (insertLength > 0) {
2929 SetEmptySelection(sel.MainCaret() + insertLength);
2930 while (*eol) {
2931 NotifyChar(*eol);
2932 if (recordingMacro) {
2933 char txt[2];
2934 txt[0] = *eol;
2935 txt[1] = '\0';
2936 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
2938 eol++;
2941 SetLastXChosen();
2942 SetScrollBars();
2943 EnsureCaretVisible();
2944 // Avoid blinking during rapid typing:
2945 ShowCaretAtCurrentPosition();
2948 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
2949 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
2950 if (sel.IsRectangular()) {
2951 if (selt == Selection::noSel) {
2952 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
2953 } else {
2954 caretToUse = sel.Rectangular().caret;
2958 Point pt = LocationFromPosition(caretToUse);
2959 int skipLines = 0;
2961 if (vs.annotationVisible) {
2962 int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
2963 Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
2964 int subLine = static_cast<int>(pt.y - ptStartLine.y) / vs.lineHeight;
2966 if (direction < 0 && subLine == 0) {
2967 int lineDisplay = cs.DisplayFromDoc(lineDoc);
2968 if (lineDisplay > 0) {
2969 skipLines = pdoc->AnnotationLines(cs.DocFromDisplay(lineDisplay - 1));
2971 } else if (direction > 0 && subLine >= (cs.GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
2972 skipLines = pdoc->AnnotationLines(lineDoc);
2976 int newY = static_cast<int>(pt.y) + (1 + skipLines) * direction * vs.lineHeight;
2977 SelectionPosition posNew = SPositionFromLocation(
2978 Point::FromInts(lastXChosen - xOffset, newY), false, false, UserVirtualSpace());
2980 if (direction < 0) {
2981 // Line wrapping may lead to a location on the same line, so
2982 // seek back if that is the case.
2983 Point ptNew = LocationFromPosition(posNew.Position());
2984 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
2985 posNew.Add(-1);
2986 posNew.SetVirtualSpace(0);
2987 ptNew = LocationFromPosition(posNew.Position());
2989 } else if (direction > 0 && posNew.Position() != pdoc->Length()) {
2990 // There is an equivalent case when moving down which skips
2991 // over a line.
2992 Point ptNew = LocationFromPosition(posNew.Position());
2993 while ((posNew.Position() > caretToUse.Position()) && (ptNew.y > newY)) {
2994 posNew.Add(-1);
2995 posNew.SetVirtualSpace(0);
2996 ptNew = LocationFromPosition(posNew.Position());
3000 MovePositionTo(MovePositionSoVisible(posNew, direction), selt);
3003 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
3004 int lineDoc, savedPos = sel.MainCaret();
3005 do {
3006 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
3007 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
3008 if (direction > 0) {
3009 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
3010 if (selt == Selection::noSel) {
3011 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
3013 break;
3016 } while (!cs.GetVisible(lineDoc));
3019 int Editor::StartEndDisplayLine(int pos, bool start) {
3020 RefreshStyleData();
3021 AutoSurface surface(this);
3022 int posRet = view.StartEndDisplayLine(surface, *this, pos, start, vs);
3023 if (posRet == INVALID_POSITION) {
3024 return pos;
3025 } else {
3026 return posRet;
3030 int Editor::KeyCommand(unsigned int iMessage) {
3031 switch (iMessage) {
3032 case SCI_LINEDOWN:
3033 CursorUpOrDown(1);
3034 break;
3035 case SCI_LINEDOWNEXTEND:
3036 CursorUpOrDown(1, Selection::selStream);
3037 break;
3038 case SCI_LINEDOWNRECTEXTEND:
3039 CursorUpOrDown(1, Selection::selRectangle);
3040 break;
3041 case SCI_PARADOWN:
3042 ParaUpOrDown(1);
3043 break;
3044 case SCI_PARADOWNEXTEND:
3045 ParaUpOrDown(1, Selection::selStream);
3046 break;
3047 case SCI_LINESCROLLDOWN:
3048 ScrollTo(topLine + 1);
3049 MoveCaretInsideView(false);
3050 break;
3051 case SCI_LINEUP:
3052 CursorUpOrDown(-1);
3053 break;
3054 case SCI_LINEUPEXTEND:
3055 CursorUpOrDown(-1, Selection::selStream);
3056 break;
3057 case SCI_LINEUPRECTEXTEND:
3058 CursorUpOrDown(-1, Selection::selRectangle);
3059 break;
3060 case SCI_PARAUP:
3061 ParaUpOrDown(-1);
3062 break;
3063 case SCI_PARAUPEXTEND:
3064 ParaUpOrDown(-1, Selection::selStream);
3065 break;
3066 case SCI_LINESCROLLUP:
3067 ScrollTo(topLine - 1);
3068 MoveCaretInsideView(false);
3069 break;
3070 case SCI_CHARLEFT:
3071 if (SelectionEmpty() || sel.MoveExtends()) {
3072 if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
3073 SelectionPosition spCaret = sel.RangeMain().caret;
3074 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3075 MovePositionTo(spCaret);
3076 } else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
3077 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1));
3078 } else {
3079 MovePositionTo(MovePositionSoVisible(
3080 SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
3082 } else {
3083 MovePositionTo(sel.LimitsForRectangularElseMain().start);
3085 SetLastXChosen();
3086 break;
3087 case SCI_CHARLEFTEXTEND:
3088 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
3089 SelectionPosition spCaret = sel.RangeMain().caret;
3090 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3091 MovePositionTo(spCaret, Selection::selStream);
3092 } else {
3093 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
3095 SetLastXChosen();
3096 break;
3097 case SCI_CHARLEFTRECTEXTEND:
3098 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
3099 SelectionPosition spCaret = sel.RangeMain().caret;
3100 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3101 MovePositionTo(spCaret, Selection::selRectangle);
3102 } else {
3103 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
3105 SetLastXChosen();
3106 break;
3107 case SCI_CHARRIGHT:
3108 if (SelectionEmpty() || sel.MoveExtends()) {
3109 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
3110 SelectionPosition spCaret = sel.RangeMain().caret;
3111 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3112 MovePositionTo(spCaret);
3113 } else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
3114 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1));
3115 } else {
3116 MovePositionTo(MovePositionSoVisible(
3117 SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
3119 } else {
3120 MovePositionTo(sel.LimitsForRectangularElseMain().end);
3122 SetLastXChosen();
3123 break;
3124 case SCI_CHARRIGHTEXTEND:
3125 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
3126 SelectionPosition spCaret = sel.RangeMain().caret;
3127 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3128 MovePositionTo(spCaret, Selection::selStream);
3129 } else {
3130 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
3132 SetLastXChosen();
3133 break;
3134 case SCI_CHARRIGHTRECTEXTEND:
3135 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
3136 SelectionPosition spCaret = sel.RangeMain().caret;
3137 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3138 MovePositionTo(spCaret, Selection::selRectangle);
3139 } else {
3140 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
3142 SetLastXChosen();
3143 break;
3144 case SCI_WORDLEFT:
3145 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
3146 SetLastXChosen();
3147 break;
3148 case SCI_WORDLEFTEXTEND:
3149 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
3150 SetLastXChosen();
3151 break;
3152 case SCI_WORDRIGHT:
3153 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
3154 SetLastXChosen();
3155 break;
3156 case SCI_WORDRIGHTEXTEND:
3157 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
3158 SetLastXChosen();
3159 break;
3161 case SCI_WORDLEFTEND:
3162 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
3163 SetLastXChosen();
3164 break;
3165 case SCI_WORDLEFTENDEXTEND:
3166 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
3167 SetLastXChosen();
3168 break;
3169 case SCI_WORDRIGHTEND:
3170 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
3171 SetLastXChosen();
3172 break;
3173 case SCI_WORDRIGHTENDEXTEND:
3174 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
3175 SetLastXChosen();
3176 break;
3178 case SCI_HOME:
3179 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
3180 SetLastXChosen();
3181 break;
3182 case SCI_HOMEEXTEND:
3183 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
3184 SetLastXChosen();
3185 break;
3186 case SCI_HOMERECTEXTEND:
3187 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
3188 SetLastXChosen();
3189 break;
3190 case SCI_LINEEND:
3191 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
3192 SetLastXChosen();
3193 break;
3194 case SCI_LINEENDEXTEND:
3195 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
3196 SetLastXChosen();
3197 break;
3198 case SCI_LINEENDRECTEXTEND:
3199 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
3200 SetLastXChosen();
3201 break;
3202 case SCI_HOMEWRAP: {
3203 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
3204 if (sel.RangeMain().caret <= homePos)
3205 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
3206 MovePositionTo(homePos);
3207 SetLastXChosen();
3209 break;
3210 case SCI_HOMEWRAPEXTEND: {
3211 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
3212 if (sel.RangeMain().caret <= homePos)
3213 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
3214 MovePositionTo(homePos, Selection::selStream);
3215 SetLastXChosen();
3217 break;
3218 case SCI_LINEENDWRAP: {
3219 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
3220 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
3221 if (endPos > realEndPos // if moved past visible EOLs
3222 || sel.RangeMain().caret >= endPos) // if at end of display line already
3223 endPos = realEndPos;
3224 MovePositionTo(endPos);
3225 SetLastXChosen();
3227 break;
3228 case SCI_LINEENDWRAPEXTEND: {
3229 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
3230 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
3231 if (endPos > realEndPos // if moved past visible EOLs
3232 || sel.RangeMain().caret >= endPos) // if at end of display line already
3233 endPos = realEndPos;
3234 MovePositionTo(endPos, Selection::selStream);
3235 SetLastXChosen();
3237 break;
3238 case SCI_DOCUMENTSTART:
3239 MovePositionTo(0);
3240 SetLastXChosen();
3241 break;
3242 case SCI_DOCUMENTSTARTEXTEND:
3243 MovePositionTo(0, Selection::selStream);
3244 SetLastXChosen();
3245 break;
3246 case SCI_DOCUMENTEND:
3247 MovePositionTo(pdoc->Length());
3248 SetLastXChosen();
3249 break;
3250 case SCI_DOCUMENTENDEXTEND:
3251 MovePositionTo(pdoc->Length(), Selection::selStream);
3252 SetLastXChosen();
3253 break;
3254 case SCI_STUTTEREDPAGEUP:
3255 PageMove(-1, Selection::noSel, true);
3256 break;
3257 case SCI_STUTTEREDPAGEUPEXTEND:
3258 PageMove(-1, Selection::selStream, true);
3259 break;
3260 case SCI_STUTTEREDPAGEDOWN:
3261 PageMove(1, Selection::noSel, true);
3262 break;
3263 case SCI_STUTTEREDPAGEDOWNEXTEND:
3264 PageMove(1, Selection::selStream, true);
3265 break;
3266 case SCI_PAGEUP:
3267 PageMove(-1);
3268 break;
3269 case SCI_PAGEUPEXTEND:
3270 PageMove(-1, Selection::selStream);
3271 break;
3272 case SCI_PAGEUPRECTEXTEND:
3273 PageMove(-1, Selection::selRectangle);
3274 break;
3275 case SCI_PAGEDOWN:
3276 PageMove(1);
3277 break;
3278 case SCI_PAGEDOWNEXTEND:
3279 PageMove(1, Selection::selStream);
3280 break;
3281 case SCI_PAGEDOWNRECTEXTEND:
3282 PageMove(1, Selection::selRectangle);
3283 break;
3284 case SCI_EDITTOGGLEOVERTYPE:
3285 inOverstrike = !inOverstrike;
3286 DropCaret();
3287 ShowCaretAtCurrentPosition();
3288 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
3289 NotifyUpdateUI();
3290 break;
3291 case SCI_CANCEL: // Cancel any modes - handled in subclass
3292 // Also unselect text
3293 CancelModes();
3294 break;
3295 case SCI_DELETEBACK:
3296 DelCharBack(true);
3297 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3298 SetLastXChosen();
3300 EnsureCaretVisible();
3301 break;
3302 case SCI_DELETEBACKNOTLINE:
3303 DelCharBack(false);
3304 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3305 SetLastXChosen();
3307 EnsureCaretVisible();
3308 break;
3309 case SCI_TAB:
3310 Indent(true);
3311 if (caretSticky == SC_CARETSTICKY_OFF) {
3312 SetLastXChosen();
3314 EnsureCaretVisible();
3315 ShowCaretAtCurrentPosition(); // Avoid blinking
3316 break;
3317 case SCI_BACKTAB:
3318 Indent(false);
3319 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3320 SetLastXChosen();
3322 EnsureCaretVisible();
3323 ShowCaretAtCurrentPosition(); // Avoid blinking
3324 break;
3325 case SCI_NEWLINE:
3326 NewLine();
3327 break;
3328 case SCI_FORMFEED:
3329 AddChar('\f');
3330 break;
3331 case SCI_VCHOME:
3332 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
3333 SetLastXChosen();
3334 break;
3335 case SCI_VCHOMEEXTEND:
3336 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
3337 SetLastXChosen();
3338 break;
3339 case SCI_VCHOMERECTEXTEND:
3340 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
3341 SetLastXChosen();
3342 break;
3343 case SCI_VCHOMEWRAP: {
3344 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
3345 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
3346 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
3347 homePos = viewLineStart;
3349 MovePositionTo(homePos);
3350 SetLastXChosen();
3352 break;
3353 case SCI_VCHOMEWRAPEXTEND: {
3354 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
3355 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
3356 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
3357 homePos = viewLineStart;
3359 MovePositionTo(homePos, Selection::selStream);
3360 SetLastXChosen();
3362 break;
3363 case SCI_ZOOMIN:
3364 if (vs.zoomLevel < 20) {
3365 vs.zoomLevel++;
3366 InvalidateStyleRedraw();
3367 NotifyZoom();
3369 break;
3370 case SCI_ZOOMOUT:
3371 if (vs.zoomLevel > -10) {
3372 vs.zoomLevel--;
3373 InvalidateStyleRedraw();
3374 NotifyZoom();
3376 break;
3377 case SCI_DELWORDLEFT: {
3378 int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
3379 pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
3380 sel.RangeMain().ClearVirtualSpace();
3381 SetLastXChosen();
3383 break;
3384 case SCI_DELWORDRIGHT: {
3385 UndoGroup ug(pdoc);
3386 InvalidateSelection(sel.RangeMain(), true);
3387 sel.RangeMain().caret = SelectionPosition(
3388 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
3389 sel.RangeMain().anchor = sel.RangeMain().caret;
3390 int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
3391 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
3393 break;
3394 case SCI_DELWORDRIGHTEND: {
3395 UndoGroup ug(pdoc);
3396 InvalidateSelection(sel.RangeMain(), true);
3397 sel.RangeMain().caret = SelectionPosition(
3398 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
3399 int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
3400 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
3402 break;
3403 case SCI_DELLINELEFT: {
3404 int line = pdoc->LineFromPosition(sel.MainCaret());
3405 int start = pdoc->LineStart(line);
3406 pdoc->DeleteChars(start, sel.MainCaret() - start);
3407 sel.RangeMain().ClearVirtualSpace();
3408 SetLastXChosen();
3410 break;
3411 case SCI_DELLINERIGHT: {
3412 int line = pdoc->LineFromPosition(sel.MainCaret());
3413 int end = pdoc->LineEnd(line);
3414 pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
3416 break;
3417 case SCI_LINECOPY: {
3418 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
3419 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
3420 CopyRangeToClipboard(pdoc->LineStart(lineStart),
3421 pdoc->LineStart(lineEnd + 1));
3423 break;
3424 case SCI_LINECUT: {
3425 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
3426 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
3427 int start = pdoc->LineStart(lineStart);
3428 int end = pdoc->LineStart(lineEnd + 1);
3429 SetSelection(start, end);
3430 Cut();
3431 SetLastXChosen();
3433 break;
3434 case SCI_LINEDELETE: {
3435 int line = pdoc->LineFromPosition(sel.MainCaret());
3436 int start = pdoc->LineStart(line);
3437 int end = pdoc->LineStart(line + 1);
3438 pdoc->DeleteChars(start, end - start);
3440 break;
3441 case SCI_LINETRANSPOSE:
3442 LineTranspose();
3443 break;
3444 case SCI_LINEDUPLICATE:
3445 Duplicate(true);
3446 break;
3447 case SCI_SELECTIONDUPLICATE:
3448 Duplicate(false);
3449 break;
3450 case SCI_LOWERCASE:
3451 ChangeCaseOfSelection(cmLower);
3452 break;
3453 case SCI_UPPERCASE:
3454 ChangeCaseOfSelection(cmUpper);
3455 break;
3456 case SCI_WORDPARTLEFT:
3457 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
3458 SetLastXChosen();
3459 break;
3460 case SCI_WORDPARTLEFTEXTEND:
3461 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
3462 SetLastXChosen();
3463 break;
3464 case SCI_WORDPARTRIGHT:
3465 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
3466 SetLastXChosen();
3467 break;
3468 case SCI_WORDPARTRIGHTEXTEND:
3469 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
3470 SetLastXChosen();
3471 break;
3472 case SCI_HOMEDISPLAY:
3473 MovePositionTo(MovePositionSoVisible(
3474 StartEndDisplayLine(sel.MainCaret(), true), -1));
3475 SetLastXChosen();
3476 break;
3477 case SCI_VCHOMEDISPLAY: {
3478 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
3479 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
3480 if (viewLineStart > homePos)
3481 homePos = viewLineStart;
3483 MovePositionTo(homePos);
3484 SetLastXChosen();
3486 break;
3487 case SCI_HOMEDISPLAYEXTEND:
3488 MovePositionTo(MovePositionSoVisible(
3489 StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
3490 SetLastXChosen();
3491 break;
3492 case SCI_VCHOMEDISPLAYEXTEND: {
3493 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
3494 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
3495 if (viewLineStart > homePos)
3496 homePos = viewLineStart;
3498 MovePositionTo(homePos, Selection::selStream);
3499 SetLastXChosen();
3501 break;
3502 case SCI_LINEENDDISPLAY:
3503 MovePositionTo(MovePositionSoVisible(
3504 StartEndDisplayLine(sel.MainCaret(), false), 1));
3505 SetLastXChosen();
3506 break;
3507 case SCI_LINEENDDISPLAYEXTEND:
3508 MovePositionTo(MovePositionSoVisible(
3509 StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
3510 SetLastXChosen();
3511 break;
3512 case SCI_SCROLLTOSTART:
3513 ScrollTo(0);
3514 break;
3515 case SCI_SCROLLTOEND:
3516 ScrollTo(MaxScrollPos());
3517 break;
3519 return 0;
3522 int Editor::KeyDefault(int, int) {
3523 return 0;
3526 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
3527 DwellEnd(false);
3528 int msg = kmap.Find(key, modifiers);
3529 if (msg) {
3530 if (consumed)
3531 *consumed = true;
3532 return static_cast<int>(WndProc(msg, 0, 0));
3533 } else {
3534 if (consumed)
3535 *consumed = false;
3536 return KeyDefault(key, modifiers);
3540 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
3541 return KeyDownWithModifiers(key, ModifierFlags(shift, ctrl, alt), consumed);
3544 void Editor::Indent(bool forwards) {
3545 UndoGroup ug(pdoc);
3546 for (size_t r=0; r<sel.Count(); r++) {
3547 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
3548 int caretPosition = sel.Range(r).caret.Position();
3549 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
3550 if (lineOfAnchor == lineCurrentPos) {
3551 if (forwards) {
3552 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
3553 caretPosition = sel.Range(r).caret.Position();
3554 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
3555 pdoc->tabIndents) {
3556 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3557 int indentationStep = pdoc->IndentSize();
3558 const int posSelect = pdoc->SetLineIndentation(
3559 lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
3560 sel.Range(r) = SelectionRange(posSelect);
3561 } else {
3562 if (pdoc->useTabs) {
3563 const int lengthInserted = pdoc->InsertString(caretPosition, "\t", 1);
3564 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
3565 } else {
3566 int numSpaces = (pdoc->tabInChars) -
3567 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
3568 if (numSpaces < 1)
3569 numSpaces = pdoc->tabInChars;
3570 const std::string spaceText(numSpaces, ' ');
3571 const int lengthInserted = pdoc->InsertString(caretPosition, spaceText.c_str(),
3572 static_cast<int>(spaceText.length()));
3573 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
3576 } else {
3577 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
3578 pdoc->tabIndents) {
3579 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3580 int indentationStep = pdoc->IndentSize();
3581 const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
3582 sel.Range(r) = SelectionRange(posSelect);
3583 } else {
3584 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
3585 pdoc->tabInChars;
3586 if (newColumn < 0)
3587 newColumn = 0;
3588 int newPos = caretPosition;
3589 while (pdoc->GetColumn(newPos) > newColumn)
3590 newPos--;
3591 sel.Range(r) = SelectionRange(newPos);
3594 } else { // Multiline
3595 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
3596 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
3597 // Multiple lines selected so indent / dedent
3598 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
3599 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
3600 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
3601 lineBottomSel--; // If not selecting any characters on a line, do not indent
3602 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
3603 if (lineOfAnchor < lineCurrentPos) {
3604 if (currentPosPosOnLine == 0)
3605 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
3606 else
3607 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
3608 } else {
3609 if (anchorPosOnLine == 0)
3610 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
3611 else
3612 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
3616 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
3619 class CaseFolderASCII : public CaseFolderTable {
3620 public:
3621 CaseFolderASCII() {
3622 StandardASCII();
3624 ~CaseFolderASCII() {
3629 CaseFolder *Editor::CaseFolderForEncoding() {
3630 // Simple default that only maps ASCII upper case to lower case.
3631 return new CaseFolderASCII();
3635 * Search of a text in the document, in the given range.
3636 * @return The position of the found text, -1 if not found.
3638 long Editor::FindText(
3639 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
3640 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
3641 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
3643 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
3644 int lengthFound = istrlen(ft->lpstrText);
3645 if (!pdoc->HasCaseFolder())
3646 pdoc->SetCaseFolder(CaseFolderForEncoding());
3647 try {
3648 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
3649 (wParam & SCFIND_MATCHCASE) != 0,
3650 (wParam & SCFIND_WHOLEWORD) != 0,
3651 (wParam & SCFIND_WORDSTART) != 0,
3652 (wParam & SCFIND_REGEXP) != 0,
3653 static_cast<int>(wParam),
3654 &lengthFound);
3655 if (pos != -1) {
3656 ft->chrgText.cpMin = pos;
3657 ft->chrgText.cpMax = pos + lengthFound;
3659 return pos;
3660 } catch (RegexError &) {
3661 errorStatus = SC_STATUS_WARN_REGEX;
3662 return -1;
3667 * Relocatable search support : Searches relative to current selection
3668 * point and sets the selection to the found text range with
3669 * each search.
3672 * Anchor following searches at current selection start: This allows
3673 * multiple incremental interactive searches to be macro recorded
3674 * while still setting the selection to found text so the find/select
3675 * operation is self-contained.
3677 void Editor::SearchAnchor() {
3678 searchAnchor = SelectionStart().Position();
3682 * Find text from current search anchor: Must call @c SearchAnchor first.
3683 * Used for next text and previous text requests.
3684 * @return The position of the found text, -1 if not found.
3686 long Editor::SearchText(
3687 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
3688 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
3689 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
3690 sptr_t lParam) { ///< The text to search for.
3692 const char *txt = reinterpret_cast<char *>(lParam);
3693 int pos;
3694 int lengthFound = istrlen(txt);
3695 if (!pdoc->HasCaseFolder())
3696 pdoc->SetCaseFolder(CaseFolderForEncoding());
3697 try {
3698 if (iMessage == SCI_SEARCHNEXT) {
3699 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
3700 (wParam & SCFIND_MATCHCASE) != 0,
3701 (wParam & SCFIND_WHOLEWORD) != 0,
3702 (wParam & SCFIND_WORDSTART) != 0,
3703 (wParam & SCFIND_REGEXP) != 0,
3704 static_cast<int>(wParam),
3705 &lengthFound);
3706 } else {
3707 pos = pdoc->FindText(searchAnchor, 0, txt,
3708 (wParam & SCFIND_MATCHCASE) != 0,
3709 (wParam & SCFIND_WHOLEWORD) != 0,
3710 (wParam & SCFIND_WORDSTART) != 0,
3711 (wParam & SCFIND_REGEXP) != 0,
3712 static_cast<int>(wParam),
3713 &lengthFound);
3715 } catch (RegexError &) {
3716 errorStatus = SC_STATUS_WARN_REGEX;
3717 return -1;
3719 if (pos != -1) {
3720 SetSelection(pos, pos + lengthFound);
3723 return pos;
3726 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
3727 std::string ret(s);
3728 for (size_t i=0; i<ret.size(); i++) {
3729 switch (caseMapping) {
3730 case cmUpper:
3731 if (ret[i] >= 'a' && ret[i] <= 'z')
3732 ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
3733 break;
3734 case cmLower:
3735 if (ret[i] >= 'A' && ret[i] <= 'Z')
3736 ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
3737 break;
3740 return ret;
3744 * Search for text in the target range of the document.
3745 * @return The position of the found text, -1 if not found.
3747 long Editor::SearchInTarget(const char *text, int length) {
3748 int lengthFound = length;
3750 if (!pdoc->HasCaseFolder())
3751 pdoc->SetCaseFolder(CaseFolderForEncoding());
3752 try {
3753 int pos = pdoc->FindText(targetStart, targetEnd, text,
3754 (searchFlags & SCFIND_MATCHCASE) != 0,
3755 (searchFlags & SCFIND_WHOLEWORD) != 0,
3756 (searchFlags & SCFIND_WORDSTART) != 0,
3757 (searchFlags & SCFIND_REGEXP) != 0,
3758 searchFlags,
3759 &lengthFound);
3760 if (pos != -1) {
3761 targetStart = pos;
3762 targetEnd = pos + lengthFound;
3764 return pos;
3765 } catch (RegexError &) {
3766 errorStatus = SC_STATUS_WARN_REGEX;
3767 return -1;
3771 void Editor::GoToLine(int lineNo) {
3772 if (lineNo > pdoc->LinesTotal())
3773 lineNo = pdoc->LinesTotal();
3774 if (lineNo < 0)
3775 lineNo = 0;
3776 SetEmptySelection(pdoc->LineStart(lineNo));
3777 ShowCaretAtCurrentPosition();
3778 EnsureCaretVisible();
3781 static bool Close(Point pt1, Point pt2, Point threshold) {
3782 if (abs(pt1.x - pt2.x) > threshold.x)
3783 return false;
3784 if (abs(pt1.y - pt2.y) > threshold.y)
3785 return false;
3786 return true;
3789 std::string Editor::RangeText(int start, int end) const {
3790 if (start < end) {
3791 int len = end - start;
3792 std::string ret(len, '\0');
3793 for (int i = 0; i < len; i++) {
3794 ret[i] = pdoc->CharAt(start + i);
3796 return ret;
3798 return std::string();
3801 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
3802 if (sel.Empty()) {
3803 if (allowLineCopy) {
3804 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
3805 int start = pdoc->LineStart(currentLine);
3806 int end = pdoc->LineEnd(currentLine);
3808 std::string text = RangeText(start, end);
3809 if (pdoc->eolMode != SC_EOL_LF)
3810 text.push_back('\r');
3811 if (pdoc->eolMode != SC_EOL_CR)
3812 text.push_back('\n');
3813 ss->Copy(text, pdoc->dbcsCodePage,
3814 vs.styles[STYLE_DEFAULT].characterSet, false, true);
3816 } else {
3817 std::string text;
3818 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
3819 if (sel.selType == Selection::selRectangle)
3820 std::sort(rangesInOrder.begin(), rangesInOrder.end());
3821 for (size_t r=0; r<rangesInOrder.size(); r++) {
3822 SelectionRange current = rangesInOrder[r];
3823 text.append(RangeText(current.Start().Position(), current.End().Position()));
3824 if (sel.selType == Selection::selRectangle) {
3825 if (pdoc->eolMode != SC_EOL_LF)
3826 text.push_back('\r');
3827 if (pdoc->eolMode != SC_EOL_CR)
3828 text.push_back('\n');
3831 ss->Copy(text, pdoc->dbcsCodePage,
3832 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
3836 void Editor::CopyRangeToClipboard(int start, int end) {
3837 start = pdoc->ClampPositionIntoDocument(start);
3838 end = pdoc->ClampPositionIntoDocument(end);
3839 SelectionText selectedText;
3840 std::string text = RangeText(start, end);
3841 selectedText.Copy(text,
3842 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
3843 CopyToClipboard(selectedText);
3846 void Editor::CopyText(int length, const char *text) {
3847 SelectionText selectedText;
3848 selectedText.Copy(std::string(text, length),
3849 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
3850 CopyToClipboard(selectedText);
3853 void Editor::SetDragPosition(SelectionPosition newPos) {
3854 if (newPos.Position() >= 0) {
3855 newPos = MovePositionOutsideChar(newPos, 1);
3856 posDrop = newPos;
3858 if (!(posDrag == newPos)) {
3859 caret.on = true;
3860 if (FineTickerAvailable()) {
3861 FineTickerCancel(tickCaret);
3862 if ((caret.active) && (caret.period > 0) && (newPos.Position() < 0))
3863 FineTickerStart(tickCaret, caret.period, caret.period/10);
3864 } else {
3865 SetTicking(true);
3867 InvalidateCaret();
3868 posDrag = newPos;
3869 InvalidateCaret();
3873 void Editor::DisplayCursor(Window::Cursor c) {
3874 if (cursorMode == SC_CURSORNORMAL)
3875 wMain.SetCursor(c);
3876 else
3877 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
3880 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
3881 int xMove = static_cast<int>(ptStart.x - ptNow.x);
3882 int yMove = static_cast<int>(ptStart.y - ptNow.y);
3883 int distanceSquared = xMove * xMove + yMove * yMove;
3884 return distanceSquared > 16;
3887 void Editor::StartDrag() {
3888 // Always handled by subclasses
3889 //SetMouseCapture(true);
3890 //DisplayCursor(Window::cursorArrow);
3893 void Editor::DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular) {
3894 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
3895 if (inDragDrop == ddDragging)
3896 dropWentOutside = false;
3898 bool positionWasInSelection = PositionInSelection(position.Position());
3900 bool positionOnEdgeOfSelection =
3901 (position == SelectionStart()) || (position == SelectionEnd());
3903 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
3904 (positionOnEdgeOfSelection && !moving)) {
3906 SelectionPosition selStart = SelectionStart();
3907 SelectionPosition selEnd = SelectionEnd();
3909 UndoGroup ug(pdoc);
3911 SelectionPosition positionAfterDeletion = position;
3912 if ((inDragDrop == ddDragging) && moving) {
3913 // Remove dragged out text
3914 if (rectangular || sel.selType == Selection::selLines) {
3915 for (size_t r=0; r<sel.Count(); r++) {
3916 if (position >= sel.Range(r).Start()) {
3917 if (position > sel.Range(r).End()) {
3918 positionAfterDeletion.Add(-sel.Range(r).Length());
3919 } else {
3920 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
3924 } else {
3925 if (position > selStart) {
3926 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
3929 ClearSelection();
3931 position = positionAfterDeletion;
3933 std::string convertedText = Document::TransformLineEnds(value, lengthValue, pdoc->eolMode);
3935 if (rectangular) {
3936 PasteRectangular(position, convertedText.c_str(), static_cast<int>(convertedText.length()));
3937 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
3938 SetEmptySelection(position);
3939 } else {
3940 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
3941 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
3942 const int lengthInserted = pdoc->InsertString(
3943 position.Position(), convertedText.c_str(), static_cast<int>(convertedText.length()));
3944 if (lengthInserted > 0) {
3945 SelectionPosition posAfterInsertion = position;
3946 posAfterInsertion.Add(lengthInserted);
3947 SetSelection(posAfterInsertion, position);
3950 } else if (inDragDrop == ddDragging) {
3951 SetEmptySelection(position);
3955 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
3956 DropAt(position, value, strlen(value), moving, rectangular);
3960 * @return true if given position is inside the selection,
3962 bool Editor::PositionInSelection(int pos) {
3963 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
3964 for (size_t r=0; r<sel.Count(); r++) {
3965 if (sel.Range(r).Contains(pos))
3966 return true;
3968 return false;
3971 bool Editor::PointInSelection(Point pt) {
3972 SelectionPosition pos = SPositionFromLocation(pt, false, true);
3973 Point ptPos = LocationFromPosition(pos);
3974 for (size_t r=0; r<sel.Count(); r++) {
3975 SelectionRange range = sel.Range(r);
3976 if (range.Contains(pos)) {
3977 bool hit = true;
3978 if (pos == range.Start()) {
3979 // see if just before selection
3980 if (pt.x < ptPos.x) {
3981 hit = false;
3984 if (pos == range.End()) {
3985 // see if just after selection
3986 if (pt.x > ptPos.x) {
3987 hit = false;
3990 if (hit)
3991 return true;
3994 return false;
3997 bool Editor::PointInSelMargin(Point pt) const {
3998 // Really means: "Point in a margin"
3999 if (vs.fixedColumnWidth > 0) { // There is a margin
4000 PRectangle rcSelMargin = GetClientRectangle();
4001 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart - vs.leftMarginWidth);
4002 rcSelMargin.left = static_cast<XYPOSITION>(vs.textStart - vs.fixedColumnWidth);
4003 return rcSelMargin.Contains(pt);
4004 } else {
4005 return false;
4009 Window::Cursor Editor::GetMarginCursor(Point pt) const {
4010 int x = 0;
4011 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
4012 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
4013 return static_cast<Window::Cursor>(vs.ms[margin].cursor);
4014 x += vs.ms[margin].width;
4016 return Window::cursorReverseArrow;
4019 void Editor::TrimAndSetSelection(int currentPos_, int anchor_) {
4020 sel.TrimSelection(SelectionRange(currentPos_, anchor_));
4021 SetSelection(currentPos_, anchor_);
4024 void Editor::LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine) {
4025 int selCurrentPos, selAnchorPos;
4026 if (wholeLine) {
4027 int lineCurrent_ = pdoc->LineFromPosition(lineCurrentPos_);
4028 int lineAnchor_ = pdoc->LineFromPosition(lineAnchorPos_);
4029 if (lineAnchorPos_ < lineCurrentPos_) {
4030 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
4031 selAnchorPos = pdoc->LineStart(lineAnchor_);
4032 } else if (lineAnchorPos_ > lineCurrentPos_) {
4033 selCurrentPos = pdoc->LineStart(lineCurrent_);
4034 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
4035 } else { // Same line, select it
4036 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
4037 selAnchorPos = pdoc->LineStart(lineAnchor_);
4039 } else {
4040 if (lineAnchorPos_ < lineCurrentPos_) {
4041 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
4042 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4043 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4044 } else if (lineAnchorPos_ > lineCurrentPos_) {
4045 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
4046 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4047 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
4048 } else { // Same line, select it
4049 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4050 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4051 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4054 TrimAndSetSelection(selCurrentPos, selAnchorPos);
4057 void Editor::WordSelection(int pos) {
4058 if (pos < wordSelectAnchorStartPos) {
4059 // Extend backward to the word containing pos.
4060 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
4061 // This ensures that a series of empty lines isn't counted as a single "word".
4062 if (!pdoc->IsLineEndPosition(pos))
4063 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
4064 TrimAndSetSelection(pos, wordSelectAnchorEndPos);
4065 } else if (pos > wordSelectAnchorEndPos) {
4066 // Extend forward to the word containing the character to the left of pos.
4067 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
4068 // This ensures that a series of empty lines isn't counted as a single "word".
4069 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
4070 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
4071 TrimAndSetSelection(pos, wordSelectAnchorStartPos);
4072 } else {
4073 // Select only the anchored word
4074 if (pos >= originalAnchorPos)
4075 TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
4076 else
4077 TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
4081 void Editor::DwellEnd(bool mouseMoved) {
4082 if (mouseMoved)
4083 ticksToDwell = dwellDelay;
4084 else
4085 ticksToDwell = SC_TIME_FOREVER;
4086 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
4087 dwelling = false;
4088 NotifyDwelling(ptMouseLast, dwelling);
4090 if (FineTickerAvailable()) {
4091 FineTickerCancel(tickDwell);
4092 if (mouseMoved && (dwellDelay < SC_TIME_FOREVER)) {
4093 //FineTickerStart(tickDwell, dwellDelay, dwellDelay/10);
4098 void Editor::MouseLeave() {
4099 SetHotSpotRange(NULL);
4100 if (!HaveMouseCapture()) {
4101 ptMouseLast = Point(-1,-1);
4102 DwellEnd(true);
4106 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
4107 return (!rectangular && ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0))
4108 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
4111 void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) {
4112 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
4113 ptMouseLast = pt;
4114 const bool ctrl = (modifiers & SCI_CTRL) != 0;
4115 const bool shift = (modifiers & SCI_SHIFT) != 0;
4116 const bool alt = (modifiers & SCI_ALT) != 0;
4117 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
4118 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4119 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4120 newCharPos = MovePositionOutsideChar(newCharPos, -1);
4121 inDragDrop = ddNone;
4122 sel.SetMoveExtends(false);
4124 if (NotifyMarginClick(pt, modifiers))
4125 return;
4127 NotifyIndicatorClick(true, newPos.Position(), modifiers);
4129 bool inSelMargin = PointInSelMargin(pt);
4130 // In margin ctrl+(double)click should always select everything
4131 if (ctrl && inSelMargin) {
4132 SelectAll();
4133 lastClickTime = curTime;
4134 lastClick = pt;
4135 return;
4137 if (shift && !inSelMargin) {
4138 SetSelection(newPos);
4140 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick, doubleClickCloseThreshold)) {
4141 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
4142 SetMouseCapture(true);
4143 if (FineTickerAvailable()) {
4144 FineTickerStart(tickScroll, 100, 10);
4146 if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))
4147 SetEmptySelection(newPos.Position());
4148 bool doubleClick = false;
4149 // Stop mouse button bounce changing selection type
4150 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
4151 if (inSelMargin) {
4152 // Inside margin selection type should be either selSubLine or selWholeLine.
4153 if (selectionType == selSubLine) {
4154 // If it is selSubLine, we're inside a *double* click and word wrap is enabled,
4155 // so we switch to selWholeLine in order to select whole line.
4156 selectionType = selWholeLine;
4157 } else if (selectionType != selSubLine && selectionType != selWholeLine) {
4158 // If it is neither, reset selection type to line selection.
4159 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4161 } else {
4162 if (selectionType == selChar) {
4163 selectionType = selWord;
4164 doubleClick = true;
4165 } else if (selectionType == selWord) {
4166 // Since we ended up here, we're inside a *triple* click, which should always select
4167 // whole line regardless of word wrap being enabled or not.
4168 selectionType = selWholeLine;
4169 } else {
4170 selectionType = selChar;
4171 originalAnchorPos = sel.MainCaret();
4176 if (selectionType == selWord) {
4177 int charPos = originalAnchorPos;
4178 if (sel.MainCaret() == originalAnchorPos) {
4179 charPos = PositionFromLocation(pt, false, true);
4180 charPos = MovePositionOutsideChar(charPos, -1);
4183 int startWord, endWord;
4184 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
4185 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
4186 endWord = pdoc->ExtendWordSelect(charPos, 1);
4187 } else {
4188 // Selecting backwards, or anchor beyond last character on line. In these cases,
4189 // we select the word containing the character to the *left* of the anchor.
4190 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
4191 startWord = pdoc->ExtendWordSelect(charPos, -1);
4192 endWord = pdoc->ExtendWordSelect(startWord, 1);
4193 } else {
4194 // Anchor at start of line; select nothing to begin with.
4195 startWord = charPos;
4196 endWord = charPos;
4200 wordSelectAnchorStartPos = startWord;
4201 wordSelectAnchorEndPos = endWord;
4202 wordSelectInitialCaretPos = sel.MainCaret();
4203 WordSelection(wordSelectInitialCaretPos);
4204 } else if (selectionType == selSubLine || selectionType == selWholeLine) {
4205 lineAnchorPos = newPos.Position();
4206 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
4207 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
4208 } else {
4209 SetEmptySelection(sel.MainCaret());
4211 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
4212 if (doubleClick) {
4213 NotifyDoubleClick(pt, modifiers);
4214 if (PositionIsHotspot(newCharPos.Position()))
4215 NotifyHotSpotDoubleClicked(newCharPos.Position(), modifiers);
4217 } else { // Single click
4218 if (inSelMargin) {
4219 sel.selType = Selection::selStream;
4220 if (!shift) {
4221 // Single click in margin: select whole line or only subline if word wrap is enabled
4222 lineAnchorPos = newPos.Position();
4223 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4224 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
4225 } else {
4226 // Single shift+click in margin: select from line anchor to clicked line
4227 if (sel.MainAnchor() > sel.MainCaret())
4228 lineAnchorPos = sel.MainAnchor() - 1;
4229 else
4230 lineAnchorPos = sel.MainAnchor();
4231 // Reset selection type if there is an empty selection.
4232 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
4233 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
4234 // This ensures that we continue selecting in the same selection mode.
4235 if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
4236 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4237 LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
4240 SetDragPosition(SelectionPosition(invalidPosition));
4241 SetMouseCapture(true);
4242 if (FineTickerAvailable()) {
4243 FineTickerStart(tickScroll, 100, 10);
4245 } else {
4246 if (PointIsHotspot(pt)) {
4247 NotifyHotSpotClicked(newCharPos.Position(), modifiers);
4248 hotSpotClickPos = newCharPos.Position();
4250 if (!shift) {
4251 if (PointInSelection(pt) && !SelectionEmpty())
4252 inDragDrop = ddInitial;
4253 else
4254 inDragDrop = ddNone;
4256 SetMouseCapture(true);
4257 if (FineTickerAvailable()) {
4258 FineTickerStart(tickScroll, 100, 10);
4260 if (inDragDrop != ddInitial) {
4261 SetDragPosition(SelectionPosition(invalidPosition));
4262 if (!shift) {
4263 if (ctrl && multipleSelection) {
4264 SelectionRange range(newPos);
4265 sel.TentativeSelection(range);
4266 InvalidateSelection(range, true);
4267 } else {
4268 InvalidateSelection(SelectionRange(newPos), true);
4269 if (sel.Count() > 1)
4270 Redraw();
4271 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
4272 sel.Clear();
4273 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
4274 SetSelection(newPos, newPos);
4277 SelectionPosition anchorCurrent = newPos;
4278 if (shift)
4279 anchorCurrent = sel.IsRectangular() ?
4280 sel.Rectangular().anchor : sel.RangeMain().anchor;
4281 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
4282 selectionType = selChar;
4283 originalAnchorPos = sel.MainCaret();
4284 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
4285 SetRectangularRange();
4289 lastClickTime = curTime;
4290 lastClick = pt;
4291 lastXChosen = static_cast<int>(pt.x) + xOffset;
4292 ShowCaretAtCurrentPosition();
4295 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
4296 return ButtonDownWithModifiers(pt, curTime, ModifierFlags(shift, ctrl, alt));
4299 bool Editor::PositionIsHotspot(int position) const {
4300 return vs.styles[static_cast<unsigned char>(pdoc->StyleAt(position))].hotspot;
4303 bool Editor::PointIsHotspot(Point pt) {
4304 int pos = PositionFromLocation(pt, true, true);
4305 if (pos == INVALID_POSITION)
4306 return false;
4307 return PositionIsHotspot(pos);
4310 void Editor::SetHotSpotRange(Point *pt) {
4311 if (pt) {
4312 int pos = PositionFromLocation(*pt, false, true);
4314 // If we don't limit this to word characters then the
4315 // range can encompass more than the run range and then
4316 // the underline will not be drawn properly.
4317 Range hsNew;
4318 hsNew.start = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
4319 hsNew.end = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
4321 // Only invalidate the range if the hotspot range has changed...
4322 if (!(hsNew == hotspot)) {
4323 if (hotspot.Valid()) {
4324 InvalidateRange(hotspot.start, hotspot.end);
4326 hotspot = hsNew;
4327 InvalidateRange(hotspot.start, hotspot.end);
4329 } else {
4330 if (hotspot.Valid()) {
4331 InvalidateRange(hotspot.start, hotspot.end);
4333 hotspot = Range(invalidPosition);
4337 Range Editor::GetHotSpotRange() const {
4338 return hotspot;
4341 void Editor::ButtonMoveWithModifiers(Point pt, int modifiers) {
4342 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
4343 DwellEnd(true);
4346 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
4347 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4348 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
4350 if (inDragDrop == ddInitial) {
4351 if (DragThreshold(ptMouseLast, pt)) {
4352 SetMouseCapture(false);
4353 if (FineTickerAvailable()) {
4354 FineTickerCancel(tickScroll);
4356 SetDragPosition(movePos);
4357 CopySelectionRange(&drag);
4358 StartDrag();
4360 return;
4363 ptMouseLast = pt;
4364 PRectangle rcClient = GetClientRectangle();
4365 Point ptOrigin = GetVisibleOriginInMain();
4366 rcClient.Move(0, -ptOrigin.y);
4367 if (FineTickerAvailable() && (dwellDelay < SC_TIME_FOREVER) && rcClient.Contains(pt)) {
4368 FineTickerStart(tickDwell, dwellDelay, dwellDelay/10);
4370 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
4371 if (HaveMouseCapture()) {
4373 // Slow down autoscrolling/selection
4374 autoScrollTimer.ticksToWait -= timer.tickSize;
4375 if (autoScrollTimer.ticksToWait > 0)
4376 return;
4377 autoScrollTimer.ticksToWait = autoScrollDelay;
4379 // Adjust selection
4380 if (posDrag.IsValid()) {
4381 SetDragPosition(movePos);
4382 } else {
4383 if (selectionType == selChar) {
4384 if (sel.selType == Selection::selStream && (modifiers & SCI_ALT) && mouseSelectionRectangularSwitch) {
4385 sel.selType = Selection::selRectangle;
4387 if (sel.IsRectangular()) {
4388 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
4389 SetSelection(movePos, sel.RangeMain().anchor);
4390 } else if (sel.Count() > 1) {
4391 InvalidateSelection(sel.RangeMain(), false);
4392 SelectionRange range(movePos, sel.RangeMain().anchor);
4393 sel.TentativeSelection(range);
4394 InvalidateSelection(range, true);
4395 } else {
4396 SetSelection(movePos, sel.RangeMain().anchor);
4398 } else if (selectionType == selWord) {
4399 // Continue selecting by word
4400 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
4401 // No need to do anything. Previously this case was lumped
4402 // in with "Moved forward", but that can be harmful in this
4403 // case: a handler for the NotifyDoubleClick re-adjusts
4404 // the selection for a fancier definition of "word" (for
4405 // example, in Perl it is useful to include the leading
4406 // '$', '%' or '@' on variables for word selection). In this
4407 // the ButtonMove() called via Tick() for auto-scrolling
4408 // could result in the fancier word selection adjustment
4409 // being unmade.
4410 } else {
4411 wordSelectInitialCaretPos = -1;
4412 WordSelection(movePos.Position());
4414 } else {
4415 // Continue selecting by line
4416 LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
4420 // Autoscroll
4421 int lineMove = DisplayFromPosition(movePos.Position());
4422 if (pt.y > rcClient.bottom) {
4423 ScrollTo(lineMove - LinesOnScreen() + 1);
4424 Redraw();
4425 } else if (pt.y < rcClient.top) {
4426 ScrollTo(lineMove);
4427 Redraw();
4429 EnsureCaretVisible(false, false, true);
4431 if (hotspot.Valid() && !PointIsHotspot(pt))
4432 SetHotSpotRange(NULL);
4434 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,true) != hotSpotClickPos) {
4435 if (inDragDrop == ddNone) {
4436 DisplayCursor(Window::cursorText);
4438 hotSpotClickPos = INVALID_POSITION;
4441 } else {
4442 if (vs.fixedColumnWidth > 0) { // There is a margin
4443 if (PointInSelMargin(pt)) {
4444 DisplayCursor(GetMarginCursor(pt));
4445 SetHotSpotRange(NULL);
4446 return; // No need to test for selection
4449 // Display regular (drag) cursor over selection
4450 if (PointInSelection(pt) && !SelectionEmpty()) {
4451 DisplayCursor(Window::cursorArrow);
4452 } else if (PointIsHotspot(pt)) {
4453 DisplayCursor(Window::cursorHand);
4454 SetHotSpotRange(&pt);
4455 } else {
4456 DisplayCursor(Window::cursorText);
4457 SetHotSpotRange(NULL);
4462 void Editor::ButtonMove(Point pt) {
4463 ButtonMoveWithModifiers(pt, 0);
4466 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
4467 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
4468 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
4469 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4470 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4471 if (inDragDrop == ddInitial) {
4472 inDragDrop = ddNone;
4473 SetEmptySelection(newPos);
4474 selectionType = selChar;
4475 originalAnchorPos = sel.MainCaret();
4477 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
4478 hotSpotClickPos = INVALID_POSITION;
4479 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4480 newCharPos = MovePositionOutsideChar(newCharPos, -1);
4481 NotifyHotSpotReleaseClick(newCharPos.Position(), ctrl ? SCI_CTRL : 0);
4483 if (HaveMouseCapture()) {
4484 if (PointInSelMargin(pt)) {
4485 DisplayCursor(GetMarginCursor(pt));
4486 } else {
4487 DisplayCursor(Window::cursorText);
4488 SetHotSpotRange(NULL);
4490 ptMouseLast = pt;
4491 SetMouseCapture(false);
4492 if (FineTickerAvailable()) {
4493 FineTickerCancel(tickScroll);
4495 NotifyIndicatorClick(false, newPos.Position(), 0);
4496 if (inDragDrop == ddDragging) {
4497 SelectionPosition selStart = SelectionStart();
4498 SelectionPosition selEnd = SelectionEnd();
4499 if (selStart < selEnd) {
4500 if (drag.Length()) {
4501 const int length = static_cast<int>(drag.Length());
4502 if (ctrl) {
4503 const int lengthInserted = pdoc->InsertString(
4504 newPos.Position(), drag.Data(), length);
4505 if (lengthInserted > 0) {
4506 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4508 } else if (newPos < selStart) {
4509 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
4510 const int lengthInserted = pdoc->InsertString(
4511 newPos.Position(), drag.Data(), length);
4512 if (lengthInserted > 0) {
4513 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4515 } else if (newPos > selEnd) {
4516 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
4517 newPos.Add(-static_cast<int>(drag.Length()));
4518 const int lengthInserted = pdoc->InsertString(
4519 newPos.Position(), drag.Data(), length);
4520 if (lengthInserted > 0) {
4521 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4523 } else {
4524 SetEmptySelection(newPos.Position());
4526 drag.Clear();
4528 selectionType = selChar;
4530 } else {
4531 if (selectionType == selChar) {
4532 if (sel.Count() > 1) {
4533 sel.RangeMain() =
4534 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
4535 InvalidateSelection(sel.RangeMain(), true);
4536 } else {
4537 SetSelection(newPos, sel.RangeMain().anchor);
4540 sel.CommitTentative();
4542 SetRectangularRange();
4543 lastClickTime = curTime;
4544 lastClick = pt;
4545 lastXChosen = static_cast<int>(pt.x) + xOffset;
4546 if (sel.selType == Selection::selStream) {
4547 SetLastXChosen();
4549 inDragDrop = ddNone;
4550 EnsureCaretVisible(false);
4554 // Called frequently to perform background UI including
4555 // caret blinking and automatic scrolling.
4556 void Editor::Tick() {
4557 if (HaveMouseCapture()) {
4558 // Auto scroll
4559 ButtonMove(ptMouseLast);
4561 if (caret.period > 0) {
4562 timer.ticksToWait -= timer.tickSize;
4563 if (timer.ticksToWait <= 0) {
4564 caret.on = !caret.on;
4565 timer.ticksToWait = caret.period;
4566 if (caret.active) {
4567 InvalidateCaret();
4571 if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) {
4572 scrollWidth = view.lineWidthMaxSeen;
4573 SetScrollBars();
4575 if ((dwellDelay < SC_TIME_FOREVER) &&
4576 (ticksToDwell > 0) &&
4577 (!HaveMouseCapture()) &&
4578 (ptMouseLast.y >= 0)) {
4579 ticksToDwell -= timer.tickSize;
4580 if (ticksToDwell <= 0) {
4581 dwelling = true;
4582 NotifyDwelling(ptMouseLast, dwelling);
4587 bool Editor::Idle() {
4589 bool idleDone;
4591 bool wrappingDone = !Wrapping();
4593 if (!wrappingDone) {
4594 // Wrap lines during idle.
4595 WrapLines(wsIdle);
4596 // No more wrapping
4597 if (!wrapPending.NeedsWrap())
4598 wrappingDone = true;
4601 // Add more idle things to do here, but make sure idleDone is
4602 // set correctly before the function returns. returning
4603 // false will stop calling this idle function until SetIdle() is
4604 // called again.
4606 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
4608 return !idleDone;
4611 void Editor::SetTicking(bool) {
4612 // SetTicking is deprecated. In the past it was pure virtual and was overridden in each
4613 // derived platform class but fine grained timers should now be implemented.
4614 // Either way, execution should not arrive here so assert failure.
4615 assert(false);
4618 void Editor::TickFor(TickReason reason) {
4619 switch (reason) {
4620 case tickCaret:
4621 caret.on = !caret.on;
4622 if (caret.active) {
4623 InvalidateCaret();
4625 break;
4626 case tickScroll:
4627 // Auto scroll
4628 ButtonMove(ptMouseLast);
4629 break;
4630 case tickWiden:
4631 SetScrollBars();
4632 FineTickerCancel(tickWiden);
4633 break;
4634 case tickDwell:
4635 if ((!HaveMouseCapture()) &&
4636 (ptMouseLast.y >= 0)) {
4637 dwelling = true;
4638 NotifyDwelling(ptMouseLast, dwelling);
4640 FineTickerCancel(tickDwell);
4641 break;
4642 default:
4643 // tickPlatform handled by subclass
4644 break;
4648 bool Editor::FineTickerAvailable() {
4649 return false;
4652 // FineTickerStart is be overridden by subclasses that support fine ticking so
4653 // this method should never be called.
4654 bool Editor::FineTickerRunning(TickReason) {
4655 assert(false);
4656 return false;
4659 // FineTickerStart is be overridden by subclasses that support fine ticking so
4660 // this method should never be called.
4661 void Editor::FineTickerStart(TickReason, int, int) {
4662 assert(false);
4665 // FineTickerCancel is be overridden by subclasses that support fine ticking so
4666 // this method should never be called.
4667 void Editor::FineTickerCancel(TickReason) {
4668 assert(false);
4671 void Editor::SetFocusState(bool focusState) {
4672 hasFocus = focusState;
4673 NotifyFocus(hasFocus);
4674 if (hasFocus) {
4675 ShowCaretAtCurrentPosition();
4676 } else {
4677 CancelModes();
4678 DropCaret();
4682 int Editor::PositionAfterArea(PRectangle rcArea) const {
4683 // The start of the document line after the display line after the area
4684 // This often means that the line after a modification is restyled which helps
4685 // detect multiline comment additions and heals single line comments
4686 int lineAfter = TopLineOfMain() + static_cast<int>(rcArea.bottom - 1) / vs.lineHeight + 1;
4687 if (lineAfter < cs.LinesDisplayed())
4688 return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
4689 else
4690 return pdoc->Length();
4693 // Style to a position within the view. If this causes a change at end of last line then
4694 // affects later lines so style all the viewed text.
4695 void Editor::StyleToPositionInView(Position pos) {
4696 int endWindow = PositionAfterArea(GetClientDrawingRectangle());
4697 if (pos > endWindow)
4698 pos = endWindow;
4699 int styleAtEnd = pdoc->StyleAt(pos-1);
4700 pdoc->EnsureStyledTo(pos);
4701 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleAt(pos-1))) {
4702 // Style at end of line changed so is multi-line change like starting a comment
4703 // so require rest of window to be styled.
4704 DiscardOverdraw(); // Prepared bitmaps may be invalid
4705 // DiscardOverdraw may have truncated client drawing area so recalculate endWindow
4706 endWindow = PositionAfterArea(GetClientDrawingRectangle());
4707 pdoc->EnsureStyledTo(endWindow);
4711 void Editor::IdleWork() {
4712 // Style the line after the modification as this allows modifications that change just the
4713 // line of the modification to heal instead of propagating to the rest of the window.
4714 if (workNeeded.items & WorkNeeded::workStyle)
4715 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2));
4717 NotifyUpdateUI();
4718 workNeeded.Reset();
4721 void Editor::QueueIdleWork(WorkNeeded::workItems items, int upTo) {
4722 workNeeded.Need(items, upTo);
4725 bool Editor::PaintContains(PRectangle rc) {
4726 if (rc.Empty()) {
4727 return true;
4728 } else {
4729 return rcPaint.Contains(rc);
4733 bool Editor::PaintContainsMargin() {
4734 if (wMargin.GetID()) {
4735 // With separate margin view, paint of text view
4736 // never contains margin.
4737 return false;
4739 PRectangle rcSelMargin = GetClientRectangle();
4740 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart);
4741 return PaintContains(rcSelMargin);
4744 void Editor::CheckForChangeOutsidePaint(Range r) {
4745 if (paintState == painting && !paintingAllText) {
4746 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
4747 if (!r.Valid())
4748 return;
4750 PRectangle rcRange = RectangleFromRange(r, 0);
4751 PRectangle rcText = GetTextRectangle();
4752 if (rcRange.top < rcText.top) {
4753 rcRange.top = rcText.top;
4755 if (rcRange.bottom > rcText.bottom) {
4756 rcRange.bottom = rcText.bottom;
4759 if (!PaintContains(rcRange)) {
4760 AbandonPaint();
4761 paintAbandonedByStyling = true;
4766 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
4767 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
4768 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
4769 CheckForChangeOutsidePaint(Range(braces[0]));
4770 CheckForChangeOutsidePaint(Range(pos0));
4771 braces[0] = pos0;
4773 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
4774 CheckForChangeOutsidePaint(Range(braces[1]));
4775 CheckForChangeOutsidePaint(Range(pos1));
4776 braces[1] = pos1;
4778 bracesMatchStyle = matchStyle;
4779 if (paintState == notPainting) {
4780 Redraw();
4785 void Editor::SetAnnotationHeights(int start, int end) {
4786 if (vs.annotationVisible) {
4787 bool changedHeight = false;
4788 for (int line=start; line<end && line<pdoc->LinesTotal(); line++) {
4789 int linesWrapped = 1;
4790 if (Wrapping()) {
4791 AutoSurface surface(this);
4792 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
4793 if (surface && ll) {
4794 view.LayoutLine(*this, line, surface, vs, ll, wrapWidth);
4795 linesWrapped = ll->lines;
4798 if (cs.SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
4799 changedHeight = true;
4801 if (changedHeight) {
4802 Redraw();
4807 void Editor::SetDocPointer(Document *document) {
4808 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
4809 pdoc->RemoveWatcher(this, 0);
4810 pdoc->Release();
4811 if (document == NULL) {
4812 pdoc = new Document();
4813 } else {
4814 pdoc = document;
4816 pdoc->AddRef();
4818 // Ensure all positions within document
4819 sel.Clear();
4820 targetStart = 0;
4821 targetEnd = 0;
4823 braces[0] = invalidPosition;
4824 braces[1] = invalidPosition;
4826 vs.ReleaseAllExtendedStyles();
4828 SetRepresentations();
4830 // Reset the contraction state to fully shown.
4831 cs.Clear();
4832 cs.InsertLines(0, pdoc->LinesTotal() - 1);
4833 SetAnnotationHeights(0, pdoc->LinesTotal());
4834 view.llc.Deallocate();
4835 NeedWrapping();
4837 view.ClearAllTabstops();
4839 pdoc->AddWatcher(this, 0);
4840 SetScrollBars();
4841 Redraw();
4844 void Editor::SetAnnotationVisible(int visible) {
4845 if (vs.annotationVisible != visible) {
4846 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
4847 vs.annotationVisible = visible;
4848 if (changedFromOrToHidden) {
4849 int dir = vs.annotationVisible ? 1 : -1;
4850 for (int line=0; line<pdoc->LinesTotal(); line++) {
4851 int annotationLines = pdoc->AnnotationLines(line);
4852 if (annotationLines > 0) {
4853 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
4857 Redraw();
4862 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
4864 int Editor::ExpandLine(int line) {
4865 int lineMaxSubord = pdoc->GetLastChild(line);
4866 line++;
4867 while (line <= lineMaxSubord) {
4868 cs.SetVisible(line, line, true);
4869 int level = pdoc->GetLevel(line);
4870 if (level & SC_FOLDLEVELHEADERFLAG) {
4871 if (cs.GetExpanded(line)) {
4872 line = ExpandLine(line);
4873 } else {
4874 line = pdoc->GetLastChild(line);
4877 line++;
4879 return lineMaxSubord;
4882 void Editor::SetFoldExpanded(int lineDoc, bool expanded) {
4883 if (cs.SetExpanded(lineDoc, expanded)) {
4884 RedrawSelMargin();
4888 void Editor::FoldLine(int line, int action) {
4889 if (line >= 0) {
4890 if (action == SC_FOLDACTION_TOGGLE) {
4891 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
4892 line = pdoc->GetFoldParent(line);
4893 if (line < 0)
4894 return;
4896 action = (cs.GetExpanded(line)) ? SC_FOLDACTION_CONTRACT : SC_FOLDACTION_EXPAND;
4899 if (action == SC_FOLDACTION_CONTRACT) {
4900 int lineMaxSubord = pdoc->GetLastChild(line);
4901 if (lineMaxSubord > line) {
4902 cs.SetExpanded(line, 0);
4903 cs.SetVisible(line + 1, lineMaxSubord, false);
4905 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
4906 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
4907 // This does not re-expand the fold
4908 EnsureCaretVisible();
4912 } else {
4913 if (!(cs.GetVisible(line))) {
4914 EnsureLineVisible(line, false);
4915 GoToLine(line);
4917 cs.SetExpanded(line, 1);
4918 ExpandLine(line);
4921 SetScrollBars();
4922 Redraw();
4926 void Editor::FoldExpand(int line, int action, int level) {
4927 bool expanding = action == SC_FOLDACTION_EXPAND;
4928 if (action == SC_FOLDACTION_TOGGLE) {
4929 expanding = !cs.GetExpanded(line);
4931 SetFoldExpanded(line, expanding);
4932 if (expanding && (cs.HiddenLines() == 0))
4933 // Nothing to do
4934 return;
4935 int lineMaxSubord = pdoc->GetLastChild(line, level & SC_FOLDLEVELNUMBERMASK);
4936 line++;
4937 cs.SetVisible(line, lineMaxSubord, expanding);
4938 while (line <= lineMaxSubord) {
4939 int levelLine = pdoc->GetLevel(line);
4940 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
4941 SetFoldExpanded(line, expanding);
4943 line++;
4945 SetScrollBars();
4946 Redraw();
4949 int Editor::ContractedFoldNext(int lineStart) const {
4950 for (int line = lineStart; line<pdoc->LinesTotal();) {
4951 if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
4952 return line;
4953 line = cs.ContractedNext(line+1);
4954 if (line < 0)
4955 return -1;
4958 return -1;
4962 * Recurse up from this line to find any folds that prevent this line from being visible
4963 * and unfold them all.
4965 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
4967 // In case in need of wrapping to ensure DisplayFromDoc works.
4968 if (lineDoc >= wrapPending.start)
4969 WrapLines(wsAll);
4971 if (!cs.GetVisible(lineDoc)) {
4972 // Back up to find a non-blank line
4973 int lookLine = lineDoc;
4974 int lookLineLevel = pdoc->GetLevel(lookLine);
4975 while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
4976 lookLineLevel = pdoc->GetLevel(--lookLine);
4978 int lineParent = pdoc->GetFoldParent(lookLine);
4979 if (lineParent < 0) {
4980 // Backed up to a top level line, so try to find parent of initial line
4981 lineParent = pdoc->GetFoldParent(lineDoc);
4983 if (lineParent >= 0) {
4984 if (lineDoc != lineParent)
4985 EnsureLineVisible(lineParent, enforcePolicy);
4986 if (!cs.GetExpanded(lineParent)) {
4987 cs.SetExpanded(lineParent, 1);
4988 ExpandLine(lineParent);
4991 SetScrollBars();
4992 Redraw();
4994 if (enforcePolicy) {
4995 int lineDisplay = cs.DisplayFromDoc(lineDoc);
4996 if (visiblePolicy & VISIBLE_SLOP) {
4997 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
4998 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
4999 SetVerticalScrollPos();
5000 Redraw();
5001 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
5002 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
5003 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
5004 SetVerticalScrollPos();
5005 Redraw();
5007 } else {
5008 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
5009 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
5010 SetVerticalScrollPos();
5011 Redraw();
5017 void Editor::FoldAll(int action) {
5018 pdoc->EnsureStyledTo(pdoc->Length());
5019 int maxLine = pdoc->LinesTotal();
5020 bool expanding = action == SC_FOLDACTION_EXPAND;
5021 if (action == SC_FOLDACTION_TOGGLE) {
5022 // Discover current state
5023 for (int lineSeek = 0; lineSeek < maxLine; lineSeek++) {
5024 if (pdoc->GetLevel(lineSeek) & SC_FOLDLEVELHEADERFLAG) {
5025 expanding = !cs.GetExpanded(lineSeek);
5026 break;
5030 if (expanding) {
5031 cs.SetVisible(0, maxLine-1, true);
5032 for (int line = 0; line < maxLine; line++) {
5033 int levelLine = pdoc->GetLevel(line);
5034 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
5035 SetFoldExpanded(line, true);
5038 } else {
5039 for (int line = 0; line < maxLine; line++) {
5040 int level = pdoc->GetLevel(line);
5041 if ((level & SC_FOLDLEVELHEADERFLAG) &&
5042 (SC_FOLDLEVELBASE == (level & SC_FOLDLEVELNUMBERMASK))) {
5043 SetFoldExpanded(line, false);
5044 int lineMaxSubord = pdoc->GetLastChild(line, -1);
5045 if (lineMaxSubord > line) {
5046 cs.SetVisible(line + 1, lineMaxSubord, false);
5051 SetScrollBars();
5052 Redraw();
5055 void Editor::FoldChanged(int line, int levelNow, int levelPrev) {
5056 if (levelNow & SC_FOLDLEVELHEADERFLAG) {
5057 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
5058 // Adding a fold point.
5059 if (cs.SetExpanded(line, true)) {
5060 RedrawSelMargin();
5062 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
5064 } else if (levelPrev & SC_FOLDLEVELHEADERFLAG) {
5065 if (!cs.GetExpanded(line)) {
5066 // Removing the fold from one that has been contracted so should expand
5067 // otherwise lines are left invisible with no way to make them visible
5068 if (cs.SetExpanded(line, true)) {
5069 RedrawSelMargin();
5071 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
5074 if (!(levelNow & SC_FOLDLEVELWHITEFLAG) &&
5075 ((levelPrev & SC_FOLDLEVELNUMBERMASK) > (levelNow & SC_FOLDLEVELNUMBERMASK))) {
5076 if (cs.HiddenLines()) {
5077 // See if should still be hidden
5078 int parentLine = pdoc->GetFoldParent(line);
5079 if ((parentLine < 0) || (cs.GetExpanded(parentLine) && cs.GetVisible(parentLine))) {
5080 cs.SetVisible(line, line, true);
5081 SetScrollBars();
5082 Redraw();
5088 void Editor::NeedShown(int pos, int len) {
5089 if (foldAutomatic & SC_AUTOMATICFOLD_SHOW) {
5090 int lineStart = pdoc->LineFromPosition(pos);
5091 int lineEnd = pdoc->LineFromPosition(pos+len);
5092 for (int line = lineStart; line <= lineEnd; line++) {
5093 EnsureLineVisible(line, false);
5095 } else {
5096 NotifyNeedShown(pos, len);
5100 int Editor::GetTag(char *tagValue, int tagNumber) {
5101 const char *text = 0;
5102 int length = 0;
5103 if ((tagNumber >= 1) && (tagNumber <= 9)) {
5104 char name[3] = "\\?";
5105 name[1] = static_cast<char>(tagNumber + '0');
5106 length = 2;
5107 text = pdoc->SubstituteByPosition(name, &length);
5109 if (tagValue) {
5110 if (text)
5111 memcpy(tagValue, text, length + 1);
5112 else
5113 *tagValue = '\0';
5115 return length;
5118 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
5119 UndoGroup ug(pdoc);
5120 if (length == -1)
5121 length = istrlen(text);
5122 if (replacePatterns) {
5123 text = pdoc->SubstituteByPosition(text, &length);
5124 if (!text) {
5125 return 0;
5128 if (targetStart != targetEnd)
5129 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
5130 targetEnd = targetStart;
5131 const int lengthInserted = pdoc->InsertString(targetStart, text, length);
5132 targetEnd = targetStart + lengthInserted;
5133 return length;
5136 bool Editor::IsUnicodeMode() const {
5137 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
5140 int Editor::CodePage() const {
5141 if (pdoc)
5142 return pdoc->dbcsCodePage;
5143 else
5144 return 0;
5147 int Editor::WrapCount(int line) {
5148 AutoSurface surface(this);
5149 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
5151 if (surface && ll) {
5152 view.LayoutLine(*this, line, surface, vs, ll, wrapWidth);
5153 return ll->lines;
5154 } else {
5155 return 1;
5159 void Editor::AddStyledText(char *buffer, int appendLength) {
5160 // The buffer consists of alternating character bytes and style bytes
5161 int textLength = appendLength / 2;
5162 std::string text(textLength, '\0');
5163 int i;
5164 for (i = 0; i < textLength; i++) {
5165 text[i] = buffer[i*2];
5167 const int lengthInserted = pdoc->InsertString(CurrentPosition(), text.c_str(), textLength);
5168 for (i = 0; i < textLength; i++) {
5169 text[i] = buffer[i*2+1];
5171 pdoc->StartStyling(CurrentPosition(), static_cast<unsigned char>(0xff));
5172 pdoc->SetStyles(textLength, text.c_str());
5173 SetEmptySelection(sel.MainCaret() + lengthInserted);
5176 static bool ValidMargin(uptr_t wParam) {
5177 return wParam <= SC_MAX_MARGIN;
5180 static char *CharPtrFromSPtr(sptr_t lParam) {
5181 return reinterpret_cast<char *>(lParam);
5184 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5185 vs.EnsureStyle(wParam);
5186 switch (iMessage) {
5187 case SCI_STYLESETFORE:
5188 vs.styles[wParam].fore = ColourDesired(static_cast<long>(lParam));
5189 break;
5190 case SCI_STYLESETBACK:
5191 vs.styles[wParam].back = ColourDesired(static_cast<long>(lParam));
5192 break;
5193 case SCI_STYLESETBOLD:
5194 vs.styles[wParam].weight = lParam != 0 ? SC_WEIGHT_BOLD : SC_WEIGHT_NORMAL;
5195 break;
5196 case SCI_STYLESETWEIGHT:
5197 vs.styles[wParam].weight = static_cast<int>(lParam);
5198 break;
5199 case SCI_STYLESETITALIC:
5200 vs.styles[wParam].italic = lParam != 0;
5201 break;
5202 case SCI_STYLESETEOLFILLED:
5203 vs.styles[wParam].eolFilled = lParam != 0;
5204 break;
5205 case SCI_STYLESETSIZE:
5206 vs.styles[wParam].size = static_cast<int>(lParam * SC_FONT_SIZE_MULTIPLIER);
5207 break;
5208 case SCI_STYLESETSIZEFRACTIONAL:
5209 vs.styles[wParam].size = static_cast<int>(lParam);
5210 break;
5211 case SCI_STYLESETFONT:
5212 if (lParam != 0) {
5213 vs.SetStyleFontName(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
5215 break;
5216 case SCI_STYLESETUNDERLINE:
5217 vs.styles[wParam].underline = lParam != 0;
5218 break;
5219 case SCI_STYLESETCASE:
5220 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
5221 break;
5222 case SCI_STYLESETCHARACTERSET:
5223 vs.styles[wParam].characterSet = static_cast<int>(lParam);
5224 pdoc->SetCaseFolder(NULL);
5225 break;
5226 case SCI_STYLESETVISIBLE:
5227 vs.styles[wParam].visible = lParam != 0;
5228 break;
5229 case SCI_STYLESETCHANGEABLE:
5230 vs.styles[wParam].changeable = lParam != 0;
5231 break;
5232 case SCI_STYLESETHOTSPOT:
5233 vs.styles[wParam].hotspot = lParam != 0;
5234 break;
5236 InvalidateStyleRedraw();
5239 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5240 vs.EnsureStyle(wParam);
5241 switch (iMessage) {
5242 case SCI_STYLEGETFORE:
5243 return vs.styles[wParam].fore.AsLong();
5244 case SCI_STYLEGETBACK:
5245 return vs.styles[wParam].back.AsLong();
5246 case SCI_STYLEGETBOLD:
5247 return vs.styles[wParam].weight > SC_WEIGHT_NORMAL;
5248 case SCI_STYLEGETWEIGHT:
5249 return vs.styles[wParam].weight;
5250 case SCI_STYLEGETITALIC:
5251 return vs.styles[wParam].italic ? 1 : 0;
5252 case SCI_STYLEGETEOLFILLED:
5253 return vs.styles[wParam].eolFilled ? 1 : 0;
5254 case SCI_STYLEGETSIZE:
5255 return vs.styles[wParam].size / SC_FONT_SIZE_MULTIPLIER;
5256 case SCI_STYLEGETSIZEFRACTIONAL:
5257 return vs.styles[wParam].size;
5258 case SCI_STYLEGETFONT:
5259 return StringResult(lParam, vs.styles[wParam].fontName);
5260 case SCI_STYLEGETUNDERLINE:
5261 return vs.styles[wParam].underline ? 1 : 0;
5262 case SCI_STYLEGETCASE:
5263 return static_cast<int>(vs.styles[wParam].caseForce);
5264 case SCI_STYLEGETCHARACTERSET:
5265 return vs.styles[wParam].characterSet;
5266 case SCI_STYLEGETVISIBLE:
5267 return vs.styles[wParam].visible ? 1 : 0;
5268 case SCI_STYLEGETCHANGEABLE:
5269 return vs.styles[wParam].changeable ? 1 : 0;
5270 case SCI_STYLEGETHOTSPOT:
5271 return vs.styles[wParam].hotspot ? 1 : 0;
5273 return 0;
5276 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
5277 const size_t len = val ? strlen(val) : 0;
5278 if (lParam) {
5279 char *ptr = CharPtrFromSPtr(lParam);
5280 if (val)
5281 memcpy(ptr, val, len+1);
5282 else
5283 *ptr = 0;
5285 return len; // Not including NUL
5288 sptr_t Editor::BytesResult(sptr_t lParam, const unsigned char *val, size_t len) {
5289 // No NUL termination: len is number of valid/displayed bytes
5290 if (lParam) {
5291 char *ptr = CharPtrFromSPtr(lParam);
5292 if (val)
5293 memcpy(ptr, val, len);
5294 else
5295 *ptr = 0;
5297 return val ? len : 0;
5300 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5301 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
5303 // Optional macro recording hook
5304 if (recordingMacro)
5305 NotifyMacroRecord(iMessage, wParam, lParam);
5307 switch (iMessage) {
5309 case SCI_GETTEXT: {
5310 if (lParam == 0)
5311 return pdoc->Length() + 1;
5312 if (wParam == 0)
5313 return 0;
5314 char *ptr = CharPtrFromSPtr(lParam);
5315 unsigned int iChar = 0;
5316 for (; iChar < wParam - 1; iChar++)
5317 ptr[iChar] = pdoc->CharAt(iChar);
5318 ptr[iChar] = '\0';
5319 return iChar;
5322 case SCI_SETTEXT: {
5323 if (lParam == 0)
5324 return 0;
5325 UndoGroup ug(pdoc);
5326 pdoc->DeleteChars(0, pdoc->Length());
5327 SetEmptySelection(0);
5328 const char *text = CharPtrFromSPtr(lParam);
5329 pdoc->InsertString(0, text, istrlen(text));
5330 return 1;
5333 case SCI_GETTEXTLENGTH:
5334 return pdoc->Length();
5336 case SCI_CUT:
5337 Cut();
5338 SetLastXChosen();
5339 break;
5341 case SCI_COPY:
5342 Copy();
5343 break;
5345 case SCI_COPYALLOWLINE:
5346 CopyAllowLine();
5347 break;
5349 case SCI_VERTICALCENTRECARET:
5350 VerticalCentreCaret();
5351 break;
5353 case SCI_MOVESELECTEDLINESUP:
5354 MoveSelectedLinesUp();
5355 break;
5357 case SCI_MOVESELECTEDLINESDOWN:
5358 MoveSelectedLinesDown();
5359 break;
5361 case SCI_COPYRANGE:
5362 CopyRangeToClipboard(static_cast<int>(wParam), static_cast<int>(lParam));
5363 break;
5365 case SCI_COPYTEXT:
5366 CopyText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
5367 break;
5369 case SCI_PASTE:
5370 Paste();
5371 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5372 SetLastXChosen();
5374 EnsureCaretVisible();
5375 break;
5377 case SCI_CLEAR:
5378 Clear();
5379 SetLastXChosen();
5380 EnsureCaretVisible();
5381 break;
5383 case SCI_UNDO:
5384 Undo();
5385 SetLastXChosen();
5386 break;
5388 case SCI_CANUNDO:
5389 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
5391 case SCI_EMPTYUNDOBUFFER:
5392 pdoc->DeleteUndoHistory();
5393 return 0;
5395 case SCI_GETFIRSTVISIBLELINE:
5396 return topLine;
5398 case SCI_SETFIRSTVISIBLELINE:
5399 ScrollTo(static_cast<int>(wParam));
5400 break;
5402 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
5403 int lineStart = pdoc->LineStart(static_cast<int>(wParam));
5404 int lineEnd = pdoc->LineStart(static_cast<int>(wParam + 1));
5405 if (lParam == 0) {
5406 return lineEnd - lineStart;
5408 char *ptr = CharPtrFromSPtr(lParam);
5409 int iPlace = 0;
5410 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
5411 ptr[iPlace++] = pdoc->CharAt(iChar);
5413 return iPlace;
5416 case SCI_GETLINECOUNT:
5417 if (pdoc->LinesTotal() == 0)
5418 return 1;
5419 else
5420 return pdoc->LinesTotal();
5422 case SCI_GETMODIFY:
5423 return !pdoc->IsSavePoint();
5425 case SCI_SETSEL: {
5426 int nStart = static_cast<int>(wParam);
5427 int nEnd = static_cast<int>(lParam);
5428 if (nEnd < 0)
5429 nEnd = pdoc->Length();
5430 if (nStart < 0)
5431 nStart = nEnd; // Remove selection
5432 InvalidateSelection(SelectionRange(nStart, nEnd));
5433 sel.Clear();
5434 sel.selType = Selection::selStream;
5435 SetSelection(nEnd, nStart);
5436 EnsureCaretVisible();
5438 break;
5440 case SCI_GETSELTEXT: {
5441 SelectionText selectedText;
5442 CopySelectionRange(&selectedText);
5443 if (lParam == 0) {
5444 return selectedText.LengthWithTerminator();
5445 } else {
5446 char *ptr = CharPtrFromSPtr(lParam);
5447 unsigned int iChar = 0;
5448 if (selectedText.Length()) {
5449 for (; iChar < selectedText.LengthWithTerminator(); iChar++)
5450 ptr[iChar] = selectedText.Data()[iChar];
5451 } else {
5452 ptr[0] = '\0';
5454 return iChar;
5458 case SCI_LINEFROMPOSITION:
5459 if (static_cast<int>(wParam) < 0)
5460 return 0;
5461 return pdoc->LineFromPosition(static_cast<int>(wParam));
5463 case SCI_POSITIONFROMLINE:
5464 if (static_cast<int>(wParam) < 0)
5465 wParam = pdoc->LineFromPosition(SelectionStart().Position());
5466 if (wParam == 0)
5467 return 0; // Even if there is no text, there is a first line that starts at 0
5468 if (static_cast<int>(wParam) > pdoc->LinesTotal())
5469 return -1;
5470 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
5471 // return -1;
5472 return pdoc->LineStart(static_cast<int>(wParam));
5474 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
5475 case SCI_LINELENGTH:
5476 if ((static_cast<int>(wParam) < 0) ||
5477 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
5478 return 0;
5479 return pdoc->LineStart(static_cast<int>(wParam) + 1) - pdoc->LineStart(static_cast<int>(wParam));
5481 case SCI_REPLACESEL: {
5482 if (lParam == 0)
5483 return 0;
5484 UndoGroup ug(pdoc);
5485 ClearSelection();
5486 char *replacement = CharPtrFromSPtr(lParam);
5487 const int lengthInserted = pdoc->InsertString(
5488 sel.MainCaret(), replacement, istrlen(replacement));
5489 SetEmptySelection(sel.MainCaret() + lengthInserted);
5490 EnsureCaretVisible();
5492 break;
5494 case SCI_SETTARGETSTART:
5495 targetStart = static_cast<int>(wParam);
5496 break;
5498 case SCI_GETTARGETSTART:
5499 return targetStart;
5501 case SCI_SETTARGETEND:
5502 targetEnd = static_cast<int>(wParam);
5503 break;
5505 case SCI_GETTARGETEND:
5506 return targetEnd;
5508 case SCI_TARGETFROMSELECTION:
5509 if (sel.MainCaret() < sel.MainAnchor()) {
5510 targetStart = sel.MainCaret();
5511 targetEnd = sel.MainAnchor();
5512 } else {
5513 targetStart = sel.MainAnchor();
5514 targetEnd = sel.MainCaret();
5516 break;
5518 case SCI_REPLACETARGET:
5519 PLATFORM_ASSERT(lParam);
5520 return ReplaceTarget(false, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5522 case SCI_REPLACETARGETRE:
5523 PLATFORM_ASSERT(lParam);
5524 return ReplaceTarget(true, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5526 case SCI_SEARCHINTARGET:
5527 PLATFORM_ASSERT(lParam);
5528 return SearchInTarget(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5530 case SCI_SETSEARCHFLAGS:
5531 searchFlags = static_cast<int>(wParam);
5532 break;
5534 case SCI_GETSEARCHFLAGS:
5535 return searchFlags;
5537 case SCI_GETTAG:
5538 return GetTag(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5540 case SCI_POSITIONBEFORE:
5541 return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) - 1, -1, true);
5543 case SCI_POSITIONAFTER:
5544 return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) + 1, 1, true);
5546 case SCI_POSITIONRELATIVE:
5547 return Platform::Clamp(pdoc->GetRelativePosition(static_cast<int>(wParam), static_cast<int>(lParam)), 0, pdoc->Length());
5549 case SCI_LINESCROLL:
5550 ScrollTo(topLine + static_cast<int>(lParam));
5551 HorizontalScrollTo(xOffset + static_cast<int>(wParam)* static_cast<int>(vs.spaceWidth));
5552 return 1;
5554 case SCI_SETXOFFSET:
5555 xOffset = static_cast<int>(wParam);
5556 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
5557 SetHorizontalScrollPos();
5558 Redraw();
5559 break;
5561 case SCI_GETXOFFSET:
5562 return xOffset;
5564 case SCI_CHOOSECARETX:
5565 SetLastXChosen();
5566 break;
5568 case SCI_SCROLLCARET:
5569 EnsureCaretVisible();
5570 break;
5572 case SCI_SETREADONLY:
5573 pdoc->SetReadOnly(wParam != 0);
5574 return 1;
5576 case SCI_GETREADONLY:
5577 return pdoc->IsReadOnly();
5579 case SCI_CANPASTE:
5580 return CanPaste();
5582 case SCI_POINTXFROMPOSITION:
5583 if (lParam < 0) {
5584 return 0;
5585 } else {
5586 Point pt = LocationFromPosition(static_cast<int>(lParam));
5587 // Convert to view-relative
5588 return static_cast<int>(pt.x) - vs.textStart + vs.fixedColumnWidth;
5591 case SCI_POINTYFROMPOSITION:
5592 if (lParam < 0) {
5593 return 0;
5594 } else {
5595 Point pt = LocationFromPosition(static_cast<int>(lParam));
5596 return static_cast<int>(pt.y);
5599 case SCI_FINDTEXT:
5600 return FindText(wParam, lParam);
5602 case SCI_GETTEXTRANGE: {
5603 if (lParam == 0)
5604 return 0;
5605 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
5606 int cpMax = tr->chrg.cpMax;
5607 if (cpMax == -1)
5608 cpMax = pdoc->Length();
5609 PLATFORM_ASSERT(cpMax <= pdoc->Length());
5610 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
5611 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
5612 // Spec says copied text is terminated with a NUL
5613 tr->lpstrText[len] = '\0';
5614 return len; // Not including NUL
5617 case SCI_HIDESELECTION:
5618 view.hideSelection = wParam != 0;
5619 Redraw();
5620 break;
5622 case SCI_FORMATRANGE:
5623 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
5625 case SCI_GETMARGINLEFT:
5626 return vs.leftMarginWidth;
5628 case SCI_GETMARGINRIGHT:
5629 return vs.rightMarginWidth;
5631 case SCI_SETMARGINLEFT:
5632 lastXChosen += static_cast<int>(lParam) - vs.leftMarginWidth;
5633 vs.leftMarginWidth = static_cast<int>(lParam);
5634 InvalidateStyleRedraw();
5635 break;
5637 case SCI_SETMARGINRIGHT:
5638 vs.rightMarginWidth = static_cast<int>(lParam);
5639 InvalidateStyleRedraw();
5640 break;
5642 // Control specific mesages
5644 case SCI_ADDTEXT: {
5645 if (lParam == 0)
5646 return 0;
5647 const int lengthInserted = pdoc->InsertString(
5648 CurrentPosition(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5649 SetEmptySelection(sel.MainCaret() + lengthInserted);
5650 return 0;
5653 case SCI_ADDSTYLEDTEXT:
5654 if (lParam)
5655 AddStyledText(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5656 return 0;
5658 case SCI_INSERTTEXT: {
5659 if (lParam == 0)
5660 return 0;
5661 int insertPos = static_cast<int>(wParam);
5662 if (static_cast<int>(wParam) == -1)
5663 insertPos = CurrentPosition();
5664 int newCurrent = CurrentPosition();
5665 char *sz = CharPtrFromSPtr(lParam);
5666 const int lengthInserted = pdoc->InsertString(insertPos, sz, istrlen(sz));
5667 if (newCurrent > insertPos)
5668 newCurrent += lengthInserted;
5669 SetEmptySelection(newCurrent);
5670 return 0;
5673 case SCI_CHANGEINSERTION:
5674 PLATFORM_ASSERT(lParam);
5675 pdoc->ChangeInsertion(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5676 return 0;
5678 case SCI_APPENDTEXT:
5679 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
5680 return 0;
5682 case SCI_CLEARALL:
5683 ClearAll();
5684 return 0;
5686 case SCI_DELETERANGE:
5687 pdoc->DeleteChars(static_cast<int>(wParam), static_cast<int>(lParam));
5688 return 0;
5690 case SCI_CLEARDOCUMENTSTYLE:
5691 ClearDocumentStyle();
5692 return 0;
5694 case SCI_SETUNDOCOLLECTION:
5695 pdoc->SetUndoCollection(wParam != 0);
5696 return 0;
5698 case SCI_GETUNDOCOLLECTION:
5699 return pdoc->IsCollectingUndo();
5701 case SCI_BEGINUNDOACTION:
5702 pdoc->BeginUndoAction();
5703 return 0;
5705 case SCI_ENDUNDOACTION:
5706 pdoc->EndUndoAction();
5707 return 0;
5709 case SCI_GETCARETPERIOD:
5710 return caret.period;
5712 case SCI_SETCARETPERIOD:
5713 CaretSetPeriod(static_cast<int>(wParam));
5714 break;
5716 case SCI_GETWORDCHARS:
5717 return pdoc->GetCharsOfClass(CharClassify::ccWord, reinterpret_cast<unsigned char *>(lParam));
5719 case SCI_SETWORDCHARS: {
5720 pdoc->SetDefaultCharClasses(false);
5721 if (lParam == 0)
5722 return 0;
5723 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
5725 break;
5727 case SCI_GETWHITESPACECHARS:
5728 return pdoc->GetCharsOfClass(CharClassify::ccSpace, reinterpret_cast<unsigned char *>(lParam));
5730 case SCI_SETWHITESPACECHARS: {
5731 if (lParam == 0)
5732 return 0;
5733 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
5735 break;
5737 case SCI_GETPUNCTUATIONCHARS:
5738 return pdoc->GetCharsOfClass(CharClassify::ccPunctuation, reinterpret_cast<unsigned char *>(lParam));
5740 case SCI_SETPUNCTUATIONCHARS: {
5741 if (lParam == 0)
5742 return 0;
5743 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccPunctuation);
5745 break;
5747 case SCI_SETCHARSDEFAULT:
5748 pdoc->SetDefaultCharClasses(true);
5749 break;
5751 case SCI_GETLENGTH:
5752 return pdoc->Length();
5754 case SCI_ALLOCATE:
5755 pdoc->Allocate(static_cast<int>(wParam));
5756 break;
5758 case SCI_GETCHARAT:
5759 return pdoc->CharAt(static_cast<int>(wParam));
5761 case SCI_SETCURRENTPOS:
5762 if (sel.IsRectangular()) {
5763 sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
5764 SetRectangularRange();
5765 Redraw();
5766 } else {
5767 SetSelection(static_cast<int>(wParam), sel.MainAnchor());
5769 break;
5771 case SCI_GETCURRENTPOS:
5772 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
5774 case SCI_SETANCHOR:
5775 if (sel.IsRectangular()) {
5776 sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
5777 SetRectangularRange();
5778 Redraw();
5779 } else {
5780 SetSelection(sel.MainCaret(), static_cast<int>(wParam));
5782 break;
5784 case SCI_GETANCHOR:
5785 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
5787 case SCI_SETSELECTIONSTART:
5788 SetSelection(Platform::Maximum(sel.MainCaret(), static_cast<int>(wParam)), static_cast<int>(wParam));
5789 break;
5791 case SCI_GETSELECTIONSTART:
5792 return sel.LimitsForRectangularElseMain().start.Position();
5794 case SCI_SETSELECTIONEND:
5795 SetSelection(static_cast<int>(wParam), Platform::Minimum(sel.MainAnchor(), static_cast<int>(wParam)));
5796 break;
5798 case SCI_GETSELECTIONEND:
5799 return sel.LimitsForRectangularElseMain().end.Position();
5801 case SCI_SETEMPTYSELECTION:
5802 SetEmptySelection(static_cast<int>(wParam));
5803 break;
5805 case SCI_SETPRINTMAGNIFICATION:
5806 view.printParameters.magnification = static_cast<int>(wParam);
5807 break;
5809 case SCI_GETPRINTMAGNIFICATION:
5810 return view.printParameters.magnification;
5812 case SCI_SETPRINTCOLOURMODE:
5813 view.printParameters.colourMode = static_cast<int>(wParam);
5814 break;
5816 case SCI_GETPRINTCOLOURMODE:
5817 return view.printParameters.colourMode;
5819 case SCI_SETPRINTWRAPMODE:
5820 view.printParameters.wrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
5821 break;
5823 case SCI_GETPRINTWRAPMODE:
5824 return view.printParameters.wrapState;
5826 case SCI_GETSTYLEAT:
5827 if (static_cast<int>(wParam) >= pdoc->Length())
5828 return 0;
5829 else
5830 return pdoc->StyleAt(static_cast<int>(wParam));
5832 case SCI_REDO:
5833 Redo();
5834 break;
5836 case SCI_SELECTALL:
5837 SelectAll();
5838 break;
5840 case SCI_SETSAVEPOINT:
5841 pdoc->SetSavePoint();
5842 break;
5844 case SCI_GETSTYLEDTEXT: {
5845 if (lParam == 0)
5846 return 0;
5847 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
5848 int iPlace = 0;
5849 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
5850 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
5851 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
5853 tr->lpstrText[iPlace] = '\0';
5854 tr->lpstrText[iPlace + 1] = '\0';
5855 return iPlace;
5858 case SCI_CANREDO:
5859 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
5861 case SCI_MARKERLINEFROMHANDLE:
5862 return pdoc->LineFromHandle(static_cast<int>(wParam));
5864 case SCI_MARKERDELETEHANDLE:
5865 pdoc->DeleteMarkFromHandle(static_cast<int>(wParam));
5866 break;
5868 case SCI_GETVIEWWS:
5869 return vs.viewWhitespace;
5871 case SCI_SETVIEWWS:
5872 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
5873 Redraw();
5874 break;
5876 case SCI_GETWHITESPACESIZE:
5877 return vs.whitespaceSize;
5879 case SCI_SETWHITESPACESIZE:
5880 vs.whitespaceSize = static_cast<int>(wParam);
5881 Redraw();
5882 break;
5884 case SCI_POSITIONFROMPOINT:
5885 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
5886 false, false);
5888 case SCI_POSITIONFROMPOINTCLOSE:
5889 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
5890 true, false);
5892 case SCI_CHARPOSITIONFROMPOINT:
5893 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
5894 false, true);
5896 case SCI_CHARPOSITIONFROMPOINTCLOSE:
5897 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
5898 true, true);
5900 case SCI_GOTOLINE:
5901 GoToLine(static_cast<int>(wParam));
5902 break;
5904 case SCI_GOTOPOS:
5905 SetEmptySelection(static_cast<int>(wParam));
5906 EnsureCaretVisible();
5907 break;
5909 case SCI_GETCURLINE: {
5910 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
5911 int lineStart = pdoc->LineStart(lineCurrentPos);
5912 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
5913 if (lParam == 0) {
5914 return 1 + lineEnd - lineStart;
5916 PLATFORM_ASSERT(wParam > 0);
5917 char *ptr = CharPtrFromSPtr(lParam);
5918 unsigned int iPlace = 0;
5919 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
5920 ptr[iPlace++] = pdoc->CharAt(iChar);
5922 ptr[iPlace] = '\0';
5923 return sel.MainCaret() - lineStart;
5926 case SCI_GETENDSTYLED:
5927 return pdoc->GetEndStyled();
5929 case SCI_GETEOLMODE:
5930 return pdoc->eolMode;
5932 case SCI_SETEOLMODE:
5933 pdoc->eolMode = static_cast<int>(wParam);
5934 break;
5936 case SCI_SETLINEENDTYPESALLOWED:
5937 if (pdoc->SetLineEndTypesAllowed(static_cast<int>(wParam))) {
5938 cs.Clear();
5939 cs.InsertLines(0, pdoc->LinesTotal() - 1);
5940 SetAnnotationHeights(0, pdoc->LinesTotal());
5941 InvalidateStyleRedraw();
5943 break;
5945 case SCI_GETLINEENDTYPESALLOWED:
5946 return pdoc->GetLineEndTypesAllowed();
5948 case SCI_GETLINEENDTYPESACTIVE:
5949 return pdoc->GetLineEndTypesActive();
5951 case SCI_STARTSTYLING:
5952 pdoc->StartStyling(static_cast<int>(wParam), static_cast<char>(lParam));
5953 break;
5955 case SCI_SETSTYLING:
5956 pdoc->SetStyleFor(static_cast<int>(wParam), static_cast<char>(lParam));
5957 break;
5959 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
5960 if (lParam == 0)
5961 return 0;
5962 pdoc->SetStyles(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
5963 break;
5965 case SCI_SETBUFFEREDDRAW:
5966 view.bufferedDraw = wParam != 0;
5967 break;
5969 case SCI_GETBUFFEREDDRAW:
5970 return view.bufferedDraw;
5972 case SCI_GETTWOPHASEDRAW:
5973 return view.phasesDraw == EditView::phasesTwo;
5975 case SCI_SETTWOPHASEDRAW:
5976 if (view.SetTwoPhaseDraw(wParam != 0))
5977 InvalidateStyleRedraw();
5978 break;
5980 case SCI_GETPHASESDRAW:
5981 return view.phasesDraw;
5983 case SCI_SETPHASESDRAW:
5984 if (view.SetPhasesDraw(static_cast<int>(wParam)))
5985 InvalidateStyleRedraw();
5986 break;
5988 case SCI_SETFONTQUALITY:
5989 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
5990 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
5991 InvalidateStyleRedraw();
5992 break;
5994 case SCI_GETFONTQUALITY:
5995 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
5997 case SCI_SETTABWIDTH:
5998 if (wParam > 0) {
5999 pdoc->tabInChars = static_cast<int>(wParam);
6000 if (pdoc->indentInChars == 0)
6001 pdoc->actualIndentInChars = pdoc->tabInChars;
6003 InvalidateStyleRedraw();
6004 break;
6006 case SCI_GETTABWIDTH:
6007 return pdoc->tabInChars;
6009 case SCI_CLEARTABSTOPS:
6010 if (view.ClearTabstops(static_cast<int>(wParam))) {
6011 DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, 0, static_cast<int>(wParam));
6012 NotifyModified(pdoc, mh, NULL);
6014 break;
6016 case SCI_ADDTABSTOP:
6017 if (view.AddTabstop(static_cast<int>(wParam), static_cast<int>(lParam))) {
6018 DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, 0, static_cast<int>(wParam));
6019 NotifyModified(pdoc, mh, NULL);
6021 break;
6023 case SCI_GETNEXTTABSTOP:
6024 return view.GetNextTabstop(static_cast<int>(wParam), static_cast<int>(lParam));
6026 case SCI_SETINDENT:
6027 pdoc->indentInChars = static_cast<int>(wParam);
6028 if (pdoc->indentInChars != 0)
6029 pdoc->actualIndentInChars = pdoc->indentInChars;
6030 else
6031 pdoc->actualIndentInChars = pdoc->tabInChars;
6032 InvalidateStyleRedraw();
6033 break;
6035 case SCI_GETINDENT:
6036 return pdoc->indentInChars;
6038 case SCI_SETUSETABS:
6039 pdoc->useTabs = wParam != 0;
6040 InvalidateStyleRedraw();
6041 break;
6043 case SCI_GETUSETABS:
6044 return pdoc->useTabs;
6046 case SCI_SETLINEINDENTATION:
6047 pdoc->SetLineIndentation(static_cast<int>(wParam), static_cast<int>(lParam));
6048 break;
6050 case SCI_GETLINEINDENTATION:
6051 return pdoc->GetLineIndentation(static_cast<int>(wParam));
6053 case SCI_GETLINEINDENTPOSITION:
6054 return pdoc->GetLineIndentPosition(static_cast<int>(wParam));
6056 case SCI_SETTABINDENTS:
6057 pdoc->tabIndents = wParam != 0;
6058 break;
6060 case SCI_GETTABINDENTS:
6061 return pdoc->tabIndents;
6063 case SCI_SETBACKSPACEUNINDENTS:
6064 pdoc->backspaceUnindents = wParam != 0;
6065 break;
6067 case SCI_GETBACKSPACEUNINDENTS:
6068 return pdoc->backspaceUnindents;
6070 case SCI_SETMOUSEDWELLTIME:
6071 dwellDelay = static_cast<int>(wParam);
6072 ticksToDwell = dwellDelay;
6073 break;
6075 case SCI_GETMOUSEDWELLTIME:
6076 return dwellDelay;
6078 case SCI_WORDSTARTPOSITION:
6079 return pdoc->ExtendWordSelect(static_cast<int>(wParam), -1, lParam != 0);
6081 case SCI_WORDENDPOSITION:
6082 return pdoc->ExtendWordSelect(static_cast<int>(wParam), 1, lParam != 0);
6084 case SCI_SETWRAPMODE:
6085 if (vs.SetWrapState(static_cast<int>(wParam))) {
6086 xOffset = 0;
6087 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
6088 InvalidateStyleRedraw();
6089 ReconfigureScrollBars();
6091 break;
6093 case SCI_GETWRAPMODE:
6094 return vs.wrapState;
6096 case SCI_SETWRAPVISUALFLAGS:
6097 if (vs.SetWrapVisualFlags(static_cast<int>(wParam))) {
6098 InvalidateStyleRedraw();
6099 ReconfigureScrollBars();
6101 break;
6103 case SCI_GETWRAPVISUALFLAGS:
6104 return vs.wrapVisualFlags;
6106 case SCI_SETWRAPVISUALFLAGSLOCATION:
6107 if (vs.SetWrapVisualFlagsLocation(static_cast<int>(wParam))) {
6108 InvalidateStyleRedraw();
6110 break;
6112 case SCI_GETWRAPVISUALFLAGSLOCATION:
6113 return vs.wrapVisualFlagsLocation;
6115 case SCI_SETWRAPSTARTINDENT:
6116 if (vs.SetWrapVisualStartIndent(static_cast<int>(wParam))) {
6117 InvalidateStyleRedraw();
6118 ReconfigureScrollBars();
6120 break;
6122 case SCI_GETWRAPSTARTINDENT:
6123 return vs.wrapVisualStartIndent;
6125 case SCI_SETWRAPINDENTMODE:
6126 if (vs.SetWrapIndentMode(static_cast<int>(wParam))) {
6127 InvalidateStyleRedraw();
6128 ReconfigureScrollBars();
6130 break;
6132 case SCI_GETWRAPINDENTMODE:
6133 return vs.wrapIndentMode;
6135 case SCI_SETLAYOUTCACHE:
6136 view.llc.SetLevel(static_cast<int>(wParam));
6137 break;
6139 case SCI_GETLAYOUTCACHE:
6140 return view.llc.GetLevel();
6142 case SCI_SETPOSITIONCACHE:
6143 view.posCache.SetSize(wParam);
6144 break;
6146 case SCI_GETPOSITIONCACHE:
6147 return view.posCache.GetSize();
6149 case SCI_SETSCROLLWIDTH:
6150 PLATFORM_ASSERT(wParam > 0);
6151 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
6152 view.lineWidthMaxSeen = 0;
6153 scrollWidth = static_cast<int>(wParam);
6154 SetScrollBars();
6156 break;
6158 case SCI_GETSCROLLWIDTH:
6159 return scrollWidth;
6161 case SCI_SETSCROLLWIDTHTRACKING:
6162 trackLineWidth = wParam != 0;
6163 break;
6165 case SCI_GETSCROLLWIDTHTRACKING:
6166 return trackLineWidth;
6168 case SCI_LINESJOIN:
6169 LinesJoin();
6170 break;
6172 case SCI_LINESSPLIT:
6173 LinesSplit(static_cast<int>(wParam));
6174 break;
6176 case SCI_TEXTWIDTH:
6177 PLATFORM_ASSERT(wParam < vs.styles.size());
6178 PLATFORM_ASSERT(lParam);
6179 return TextWidth(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
6181 case SCI_TEXTHEIGHT:
6182 return vs.lineHeight;
6184 case SCI_SETENDATLASTLINE:
6185 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
6186 if (endAtLastLine != (wParam != 0)) {
6187 endAtLastLine = wParam != 0;
6188 SetScrollBars();
6190 break;
6192 case SCI_GETENDATLASTLINE:
6193 return endAtLastLine;
6195 case SCI_SETCARETSTICKY:
6196 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
6197 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
6198 caretSticky = static_cast<int>(wParam);
6200 break;
6202 case SCI_GETCARETSTICKY:
6203 return caretSticky;
6205 case SCI_TOGGLECARETSTICKY:
6206 caretSticky = !caretSticky;
6207 break;
6209 case SCI_GETCOLUMN:
6210 return pdoc->GetColumn(static_cast<int>(wParam));
6212 case SCI_FINDCOLUMN:
6213 return pdoc->FindColumn(static_cast<int>(wParam), static_cast<int>(lParam));
6215 case SCI_SETHSCROLLBAR :
6216 if (horizontalScrollBarVisible != (wParam != 0)) {
6217 horizontalScrollBarVisible = wParam != 0;
6218 SetScrollBars();
6219 ReconfigureScrollBars();
6221 break;
6223 case SCI_GETHSCROLLBAR:
6224 return horizontalScrollBarVisible;
6226 case SCI_SETVSCROLLBAR:
6227 if (verticalScrollBarVisible != (wParam != 0)) {
6228 verticalScrollBarVisible = wParam != 0;
6229 SetScrollBars();
6230 ReconfigureScrollBars();
6231 if (verticalScrollBarVisible)
6232 SetVerticalScrollPos();
6234 break;
6236 case SCI_GETVSCROLLBAR:
6237 return verticalScrollBarVisible;
6239 case SCI_SETINDENTATIONGUIDES:
6240 vs.viewIndentationGuides = IndentView(wParam);
6241 Redraw();
6242 break;
6244 case SCI_GETINDENTATIONGUIDES:
6245 return vs.viewIndentationGuides;
6247 case SCI_SETHIGHLIGHTGUIDE:
6248 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
6249 highlightGuideColumn = static_cast<int>(wParam);
6250 Redraw();
6252 break;
6254 case SCI_GETHIGHLIGHTGUIDE:
6255 return highlightGuideColumn;
6257 case SCI_GETLINEENDPOSITION:
6258 return pdoc->LineEnd(static_cast<int>(wParam));
6260 case SCI_SETCODEPAGE:
6261 if (ValidCodePage(static_cast<int>(wParam))) {
6262 if (pdoc->SetDBCSCodePage(static_cast<int>(wParam))) {
6263 cs.Clear();
6264 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6265 SetAnnotationHeights(0, pdoc->LinesTotal());
6266 InvalidateStyleRedraw();
6267 SetRepresentations();
6270 break;
6272 case SCI_GETCODEPAGE:
6273 return pdoc->dbcsCodePage;
6275 case SCI_SETIMEINTERACTION:
6276 imeInteraction = static_cast<EditModel::IMEInteraction>(wParam);
6277 break;
6279 case SCI_GETIMEINTERACTION:
6280 return imeInteraction;
6282 #ifdef INCLUDE_DEPRECATED_FEATURES
6283 case SCI_SETUSEPALETTE:
6284 InvalidateStyleRedraw();
6285 break;
6287 case SCI_GETUSEPALETTE:
6288 return 0;
6289 #endif
6291 // Marker definition and setting
6292 case SCI_MARKERDEFINE:
6293 if (wParam <= MARKER_MAX) {
6294 vs.markers[wParam].markType = static_cast<int>(lParam);
6295 vs.CalcLargestMarkerHeight();
6297 InvalidateStyleData();
6298 RedrawSelMargin();
6299 break;
6301 case SCI_MARKERSYMBOLDEFINED:
6302 if (wParam <= MARKER_MAX)
6303 return vs.markers[wParam].markType;
6304 else
6305 return 0;
6307 case SCI_MARKERSETFORE:
6308 if (wParam <= MARKER_MAX)
6309 vs.markers[wParam].fore = ColourDesired(static_cast<long>(lParam));
6310 InvalidateStyleData();
6311 RedrawSelMargin();
6312 break;
6313 case SCI_MARKERSETBACKSELECTED:
6314 if (wParam <= MARKER_MAX)
6315 vs.markers[wParam].backSelected = ColourDesired(static_cast<long>(lParam));
6316 InvalidateStyleData();
6317 RedrawSelMargin();
6318 break;
6319 case SCI_MARKERENABLEHIGHLIGHT:
6320 marginView.highlightDelimiter.isEnabled = wParam == 1;
6321 RedrawSelMargin();
6322 break;
6323 case SCI_MARKERSETBACK:
6324 if (wParam <= MARKER_MAX)
6325 vs.markers[wParam].back = ColourDesired(static_cast<long>(lParam));
6326 InvalidateStyleData();
6327 RedrawSelMargin();
6328 break;
6329 case SCI_MARKERSETALPHA:
6330 if (wParam <= MARKER_MAX)
6331 vs.markers[wParam].alpha = static_cast<int>(lParam);
6332 InvalidateStyleRedraw();
6333 break;
6334 case SCI_MARKERADD: {
6335 int markerID = pdoc->AddMark(static_cast<int>(wParam), static_cast<int>(lParam));
6336 return markerID;
6338 case SCI_MARKERADDSET:
6339 if (lParam != 0)
6340 pdoc->AddMarkSet(static_cast<int>(wParam), static_cast<int>(lParam));
6341 break;
6343 case SCI_MARKERDELETE:
6344 pdoc->DeleteMark(static_cast<int>(wParam), static_cast<int>(lParam));
6345 break;
6347 case SCI_MARKERDELETEALL:
6348 pdoc->DeleteAllMarks(static_cast<int>(wParam));
6349 break;
6351 case SCI_MARKERGET:
6352 return pdoc->GetMark(static_cast<int>(wParam));
6354 case SCI_MARKERNEXT:
6355 return pdoc->MarkerNext(static_cast<int>(wParam), static_cast<int>(lParam));
6357 case SCI_MARKERPREVIOUS: {
6358 for (int iLine = static_cast<int>(wParam); iLine >= 0; iLine--) {
6359 if ((pdoc->GetMark(iLine) & lParam) != 0)
6360 return iLine;
6363 return -1;
6365 case SCI_MARKERDEFINEPIXMAP:
6366 if (wParam <= MARKER_MAX) {
6367 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
6368 vs.CalcLargestMarkerHeight();
6370 InvalidateStyleData();
6371 RedrawSelMargin();
6372 break;
6374 case SCI_RGBAIMAGESETWIDTH:
6375 sizeRGBAImage.x = static_cast<XYPOSITION>(wParam);
6376 break;
6378 case SCI_RGBAIMAGESETHEIGHT:
6379 sizeRGBAImage.y = static_cast<XYPOSITION>(wParam);
6380 break;
6382 case SCI_RGBAIMAGESETSCALE:
6383 scaleRGBAImage = static_cast<float>(wParam);
6384 break;
6386 case SCI_MARKERDEFINERGBAIMAGE:
6387 if (wParam <= MARKER_MAX) {
6388 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0f, reinterpret_cast<unsigned char *>(lParam));
6389 vs.CalcLargestMarkerHeight();
6391 InvalidateStyleData();
6392 RedrawSelMargin();
6393 break;
6395 case SCI_SETMARGINTYPEN:
6396 if (ValidMargin(wParam)) {
6397 vs.ms[wParam].style = static_cast<int>(lParam);
6398 InvalidateStyleRedraw();
6400 break;
6402 case SCI_GETMARGINTYPEN:
6403 if (ValidMargin(wParam))
6404 return vs.ms[wParam].style;
6405 else
6406 return 0;
6408 case SCI_SETMARGINWIDTHN:
6409 if (ValidMargin(wParam)) {
6410 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
6411 if (vs.ms[wParam].width != lParam) {
6412 lastXChosen += static_cast<int>(lParam) - vs.ms[wParam].width;
6413 vs.ms[wParam].width = static_cast<int>(lParam);
6414 InvalidateStyleRedraw();
6417 break;
6419 case SCI_GETMARGINWIDTHN:
6420 if (ValidMargin(wParam))
6421 return vs.ms[wParam].width;
6422 else
6423 return 0;
6425 case SCI_SETMARGINMASKN:
6426 if (ValidMargin(wParam)) {
6427 vs.ms[wParam].mask = static_cast<int>(lParam);
6428 InvalidateStyleRedraw();
6430 break;
6432 case SCI_GETMARGINMASKN:
6433 if (ValidMargin(wParam))
6434 return vs.ms[wParam].mask;
6435 else
6436 return 0;
6438 case SCI_SETMARGINSENSITIVEN:
6439 if (ValidMargin(wParam)) {
6440 vs.ms[wParam].sensitive = lParam != 0;
6441 InvalidateStyleRedraw();
6443 break;
6445 case SCI_GETMARGINSENSITIVEN:
6446 if (ValidMargin(wParam))
6447 return vs.ms[wParam].sensitive ? 1 : 0;
6448 else
6449 return 0;
6451 case SCI_SETMARGINCURSORN:
6452 if (ValidMargin(wParam))
6453 vs.ms[wParam].cursor = static_cast<int>(lParam);
6454 break;
6456 case SCI_GETMARGINCURSORN:
6457 if (ValidMargin(wParam))
6458 return vs.ms[wParam].cursor;
6459 else
6460 return 0;
6462 case SCI_STYLECLEARALL:
6463 vs.ClearStyles();
6464 InvalidateStyleRedraw();
6465 break;
6467 case SCI_STYLESETFORE:
6468 case SCI_STYLESETBACK:
6469 case SCI_STYLESETBOLD:
6470 case SCI_STYLESETWEIGHT:
6471 case SCI_STYLESETITALIC:
6472 case SCI_STYLESETEOLFILLED:
6473 case SCI_STYLESETSIZE:
6474 case SCI_STYLESETSIZEFRACTIONAL:
6475 case SCI_STYLESETFONT:
6476 case SCI_STYLESETUNDERLINE:
6477 case SCI_STYLESETCASE:
6478 case SCI_STYLESETCHARACTERSET:
6479 case SCI_STYLESETVISIBLE:
6480 case SCI_STYLESETCHANGEABLE:
6481 case SCI_STYLESETHOTSPOT:
6482 StyleSetMessage(iMessage, wParam, lParam);
6483 break;
6485 case SCI_STYLEGETFORE:
6486 case SCI_STYLEGETBACK:
6487 case SCI_STYLEGETBOLD:
6488 case SCI_STYLEGETWEIGHT:
6489 case SCI_STYLEGETITALIC:
6490 case SCI_STYLEGETEOLFILLED:
6491 case SCI_STYLEGETSIZE:
6492 case SCI_STYLEGETSIZEFRACTIONAL:
6493 case SCI_STYLEGETFONT:
6494 case SCI_STYLEGETUNDERLINE:
6495 case SCI_STYLEGETCASE:
6496 case SCI_STYLEGETCHARACTERSET:
6497 case SCI_STYLEGETVISIBLE:
6498 case SCI_STYLEGETCHANGEABLE:
6499 case SCI_STYLEGETHOTSPOT:
6500 return StyleGetMessage(iMessage, wParam, lParam);
6502 case SCI_STYLERESETDEFAULT:
6503 vs.ResetDefaultStyle();
6504 InvalidateStyleRedraw();
6505 break;
6506 case SCI_SETSTYLEBITS:
6507 vs.EnsureStyle(0xff);
6508 break;
6510 case SCI_GETSTYLEBITS:
6511 return 8;
6513 case SCI_SETLINESTATE:
6514 return pdoc->SetLineState(static_cast<int>(wParam), static_cast<int>(lParam));
6516 case SCI_GETLINESTATE:
6517 return pdoc->GetLineState(static_cast<int>(wParam));
6519 case SCI_GETMAXLINESTATE:
6520 return pdoc->GetMaxLineState();
6522 case SCI_GETCARETLINEVISIBLE:
6523 return vs.showCaretLineBackground;
6524 case SCI_SETCARETLINEVISIBLE:
6525 vs.showCaretLineBackground = wParam != 0;
6526 InvalidateStyleRedraw();
6527 break;
6528 case SCI_GETCARETLINEVISIBLEALWAYS:
6529 return vs.alwaysShowCaretLineBackground;
6530 case SCI_SETCARETLINEVISIBLEALWAYS:
6531 vs.alwaysShowCaretLineBackground = wParam != 0;
6532 InvalidateStyleRedraw();
6533 break;
6535 case SCI_GETCARETLINEBACK:
6536 return vs.caretLineBackground.AsLong();
6537 case SCI_SETCARETLINEBACK:
6538 vs.caretLineBackground = static_cast<int>(wParam);
6539 InvalidateStyleRedraw();
6540 break;
6541 case SCI_GETCARETLINEBACKALPHA:
6542 return vs.caretLineAlpha;
6543 case SCI_SETCARETLINEBACKALPHA:
6544 vs.caretLineAlpha = static_cast<int>(wParam);
6545 InvalidateStyleRedraw();
6546 break;
6548 // Folding messages
6550 case SCI_VISIBLEFROMDOCLINE:
6551 return cs.DisplayFromDoc(static_cast<int>(wParam));
6553 case SCI_DOCLINEFROMVISIBLE:
6554 return cs.DocFromDisplay(static_cast<int>(wParam));
6556 case SCI_WRAPCOUNT:
6557 return WrapCount(static_cast<int>(wParam));
6559 case SCI_SETFOLDLEVEL: {
6560 int prev = pdoc->SetLevel(static_cast<int>(wParam), static_cast<int>(lParam));
6561 if (prev != static_cast<int>(lParam))
6562 RedrawSelMargin();
6563 return prev;
6566 case SCI_GETFOLDLEVEL:
6567 return pdoc->GetLevel(static_cast<int>(wParam));
6569 case SCI_GETLASTCHILD:
6570 return pdoc->GetLastChild(static_cast<int>(wParam), static_cast<int>(lParam));
6572 case SCI_GETFOLDPARENT:
6573 return pdoc->GetFoldParent(static_cast<int>(wParam));
6575 case SCI_SHOWLINES:
6576 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), true);
6577 SetScrollBars();
6578 Redraw();
6579 break;
6581 case SCI_HIDELINES:
6582 if (wParam > 0)
6583 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), false);
6584 SetScrollBars();
6585 Redraw();
6586 break;
6588 case SCI_GETLINEVISIBLE:
6589 return cs.GetVisible(static_cast<int>(wParam));
6591 case SCI_GETALLLINESVISIBLE:
6592 return cs.HiddenLines() ? 0 : 1;
6594 case SCI_SETFOLDEXPANDED:
6595 SetFoldExpanded(static_cast<int>(wParam), lParam != 0);
6596 break;
6598 case SCI_GETFOLDEXPANDED:
6599 return cs.GetExpanded(static_cast<int>(wParam));
6601 case SCI_SETAUTOMATICFOLD:
6602 foldAutomatic = static_cast<int>(wParam);
6603 break;
6605 case SCI_GETAUTOMATICFOLD:
6606 return foldAutomatic;
6608 case SCI_SETFOLDFLAGS:
6609 foldFlags = static_cast<int>(wParam);
6610 Redraw();
6611 break;
6613 case SCI_TOGGLEFOLD:
6614 FoldLine(static_cast<int>(wParam), SC_FOLDACTION_TOGGLE);
6615 break;
6617 case SCI_FOLDLINE:
6618 FoldLine(static_cast<int>(wParam), static_cast<int>(lParam));
6619 break;
6621 case SCI_FOLDCHILDREN:
6622 FoldExpand(static_cast<int>(wParam), static_cast<int>(lParam), pdoc->GetLevel(static_cast<int>(wParam)));
6623 break;
6625 case SCI_FOLDALL:
6626 FoldAll(static_cast<int>(wParam));
6627 break;
6629 case SCI_EXPANDCHILDREN:
6630 FoldExpand(static_cast<int>(wParam), SC_FOLDACTION_EXPAND, static_cast<int>(lParam));
6631 break;
6633 case SCI_CONTRACTEDFOLDNEXT:
6634 return ContractedFoldNext(static_cast<int>(wParam));
6636 case SCI_ENSUREVISIBLE:
6637 EnsureLineVisible(static_cast<int>(wParam), false);
6638 break;
6640 case SCI_ENSUREVISIBLEENFORCEPOLICY:
6641 EnsureLineVisible(static_cast<int>(wParam), true);
6642 break;
6644 case SCI_SCROLLRANGE:
6645 ScrollRange(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
6646 break;
6648 case SCI_SEARCHANCHOR:
6649 SearchAnchor();
6650 break;
6652 case SCI_SEARCHNEXT:
6653 case SCI_SEARCHPREV:
6654 return SearchText(iMessage, wParam, lParam);
6656 case SCI_SETXCARETPOLICY:
6657 caretXPolicy = static_cast<int>(wParam);
6658 caretXSlop = static_cast<int>(lParam);
6659 break;
6661 case SCI_SETYCARETPOLICY:
6662 caretYPolicy = static_cast<int>(wParam);
6663 caretYSlop = static_cast<int>(lParam);
6664 break;
6666 case SCI_SETVISIBLEPOLICY:
6667 visiblePolicy = static_cast<int>(wParam);
6668 visibleSlop = static_cast<int>(lParam);
6669 break;
6671 case SCI_LINESONSCREEN:
6672 return LinesOnScreen();
6674 case SCI_SETSELFORE:
6675 vs.selColours.fore = ColourOptional(wParam, lParam);
6676 vs.selAdditionalForeground = ColourDesired(static_cast<long>(lParam));
6677 InvalidateStyleRedraw();
6678 break;
6680 case SCI_SETSELBACK:
6681 vs.selColours.back = ColourOptional(wParam, lParam);
6682 vs.selAdditionalBackground = ColourDesired(static_cast<long>(lParam));
6683 InvalidateStyleRedraw();
6684 break;
6686 case SCI_SETSELALPHA:
6687 vs.selAlpha = static_cast<int>(wParam);
6688 vs.selAdditionalAlpha = static_cast<int>(wParam);
6689 InvalidateStyleRedraw();
6690 break;
6692 case SCI_GETSELALPHA:
6693 return vs.selAlpha;
6695 case SCI_GETSELEOLFILLED:
6696 return vs.selEOLFilled;
6698 case SCI_SETSELEOLFILLED:
6699 vs.selEOLFilled = wParam != 0;
6700 InvalidateStyleRedraw();
6701 break;
6703 case SCI_SETWHITESPACEFORE:
6704 vs.whitespaceColours.fore = ColourOptional(wParam, lParam);
6705 InvalidateStyleRedraw();
6706 break;
6708 case SCI_SETWHITESPACEBACK:
6709 vs.whitespaceColours.back = ColourOptional(wParam, lParam);
6710 InvalidateStyleRedraw();
6711 break;
6713 case SCI_SETCARETFORE:
6714 vs.caretcolour = ColourDesired(static_cast<long>(wParam));
6715 InvalidateStyleRedraw();
6716 break;
6718 case SCI_GETCARETFORE:
6719 return vs.caretcolour.AsLong();
6721 case SCI_SETCARETSTYLE:
6722 if (wParam <= CARETSTYLE_BLOCK)
6723 vs.caretStyle = static_cast<int>(wParam);
6724 else
6725 /* Default to the line caret */
6726 vs.caretStyle = CARETSTYLE_LINE;
6727 InvalidateStyleRedraw();
6728 break;
6730 case SCI_GETCARETSTYLE:
6731 return vs.caretStyle;
6733 case SCI_SETCARETWIDTH:
6734 if (static_cast<int>(wParam) <= 0)
6735 vs.caretWidth = 0;
6736 else if (wParam >= 3)
6737 vs.caretWidth = 3;
6738 else
6739 vs.caretWidth = static_cast<int>(wParam);
6740 InvalidateStyleRedraw();
6741 break;
6743 case SCI_GETCARETWIDTH:
6744 return vs.caretWidth;
6746 case SCI_ASSIGNCMDKEY:
6747 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
6748 Platform::HighShortFromLong(static_cast<long>(wParam)), static_cast<unsigned int>(lParam));
6749 break;
6751 case SCI_CLEARCMDKEY:
6752 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
6753 Platform::HighShortFromLong(static_cast<long>(wParam)), SCI_NULL);
6754 break;
6756 case SCI_CLEARALLCMDKEYS:
6757 kmap.Clear();
6758 break;
6760 case SCI_INDICSETSTYLE:
6761 if (wParam <= INDIC_MAX) {
6762 vs.indicators[wParam].style = static_cast<int>(lParam);
6763 InvalidateStyleRedraw();
6765 break;
6767 case SCI_INDICGETSTYLE:
6768 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
6770 case SCI_INDICSETFORE:
6771 if (wParam <= INDIC_MAX) {
6772 vs.indicators[wParam].fore = ColourDesired(static_cast<long>(lParam));
6773 InvalidateStyleRedraw();
6775 break;
6777 case SCI_INDICGETFORE:
6778 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.AsLong() : 0;
6780 case SCI_INDICSETUNDER:
6781 if (wParam <= INDIC_MAX) {
6782 vs.indicators[wParam].under = lParam != 0;
6783 InvalidateStyleRedraw();
6785 break;
6787 case SCI_INDICGETUNDER:
6788 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
6790 case SCI_INDICSETALPHA:
6791 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
6792 vs.indicators[wParam].fillAlpha = static_cast<int>(lParam);
6793 InvalidateStyleRedraw();
6795 break;
6797 case SCI_INDICGETALPHA:
6798 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
6800 case SCI_INDICSETOUTLINEALPHA:
6801 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
6802 vs.indicators[wParam].outlineAlpha = static_cast<int>(lParam);
6803 InvalidateStyleRedraw();
6805 break;
6807 case SCI_INDICGETOUTLINEALPHA:
6808 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
6810 case SCI_SETINDICATORCURRENT:
6811 pdoc->decorations.SetCurrentIndicator(static_cast<int>(wParam));
6812 break;
6813 case SCI_GETINDICATORCURRENT:
6814 return pdoc->decorations.GetCurrentIndicator();
6815 case SCI_SETINDICATORVALUE:
6816 pdoc->decorations.SetCurrentValue(static_cast<int>(wParam));
6817 break;
6818 case SCI_GETINDICATORVALUE:
6819 return pdoc->decorations.GetCurrentValue();
6821 case SCI_INDICATORFILLRANGE:
6822 pdoc->DecorationFillRange(static_cast<int>(wParam), pdoc->decorations.GetCurrentValue(), static_cast<int>(lParam));
6823 break;
6825 case SCI_INDICATORCLEARRANGE:
6826 pdoc->DecorationFillRange(static_cast<int>(wParam), 0, static_cast<int>(lParam));
6827 break;
6829 case SCI_INDICATORALLONFOR:
6830 return pdoc->decorations.AllOnFor(static_cast<int>(wParam));
6832 case SCI_INDICATORVALUEAT:
6833 return pdoc->decorations.ValueAt(static_cast<int>(wParam), static_cast<int>(lParam));
6835 case SCI_INDICATORSTART:
6836 return pdoc->decorations.Start(static_cast<int>(wParam), static_cast<int>(lParam));
6838 case SCI_INDICATOREND:
6839 return pdoc->decorations.End(static_cast<int>(wParam), static_cast<int>(lParam));
6841 case SCI_LINEDOWN:
6842 case SCI_LINEDOWNEXTEND:
6843 case SCI_PARADOWN:
6844 case SCI_PARADOWNEXTEND:
6845 case SCI_LINEUP:
6846 case SCI_LINEUPEXTEND:
6847 case SCI_PARAUP:
6848 case SCI_PARAUPEXTEND:
6849 case SCI_CHARLEFT:
6850 case SCI_CHARLEFTEXTEND:
6851 case SCI_CHARRIGHT:
6852 case SCI_CHARRIGHTEXTEND:
6853 case SCI_WORDLEFT:
6854 case SCI_WORDLEFTEXTEND:
6855 case SCI_WORDRIGHT:
6856 case SCI_WORDRIGHTEXTEND:
6857 case SCI_WORDLEFTEND:
6858 case SCI_WORDLEFTENDEXTEND:
6859 case SCI_WORDRIGHTEND:
6860 case SCI_WORDRIGHTENDEXTEND:
6861 case SCI_HOME:
6862 case SCI_HOMEEXTEND:
6863 case SCI_LINEEND:
6864 case SCI_LINEENDEXTEND:
6865 case SCI_HOMEWRAP:
6866 case SCI_HOMEWRAPEXTEND:
6867 case SCI_LINEENDWRAP:
6868 case SCI_LINEENDWRAPEXTEND:
6869 case SCI_DOCUMENTSTART:
6870 case SCI_DOCUMENTSTARTEXTEND:
6871 case SCI_DOCUMENTEND:
6872 case SCI_DOCUMENTENDEXTEND:
6873 case SCI_SCROLLTOSTART:
6874 case SCI_SCROLLTOEND:
6876 case SCI_STUTTEREDPAGEUP:
6877 case SCI_STUTTEREDPAGEUPEXTEND:
6878 case SCI_STUTTEREDPAGEDOWN:
6879 case SCI_STUTTEREDPAGEDOWNEXTEND:
6881 case SCI_PAGEUP:
6882 case SCI_PAGEUPEXTEND:
6883 case SCI_PAGEDOWN:
6884 case SCI_PAGEDOWNEXTEND:
6885 case SCI_EDITTOGGLEOVERTYPE:
6886 case SCI_CANCEL:
6887 case SCI_DELETEBACK:
6888 case SCI_TAB:
6889 case SCI_BACKTAB:
6890 case SCI_NEWLINE:
6891 case SCI_FORMFEED:
6892 case SCI_VCHOME:
6893 case SCI_VCHOMEEXTEND:
6894 case SCI_VCHOMEWRAP:
6895 case SCI_VCHOMEWRAPEXTEND:
6896 case SCI_VCHOMEDISPLAY:
6897 case SCI_VCHOMEDISPLAYEXTEND:
6898 case SCI_ZOOMIN:
6899 case SCI_ZOOMOUT:
6900 case SCI_DELWORDLEFT:
6901 case SCI_DELWORDRIGHT:
6902 case SCI_DELWORDRIGHTEND:
6903 case SCI_DELLINELEFT:
6904 case SCI_DELLINERIGHT:
6905 case SCI_LINECOPY:
6906 case SCI_LINECUT:
6907 case SCI_LINEDELETE:
6908 case SCI_LINETRANSPOSE:
6909 case SCI_LINEDUPLICATE:
6910 case SCI_LOWERCASE:
6911 case SCI_UPPERCASE:
6912 case SCI_LINESCROLLDOWN:
6913 case SCI_LINESCROLLUP:
6914 case SCI_WORDPARTLEFT:
6915 case SCI_WORDPARTLEFTEXTEND:
6916 case SCI_WORDPARTRIGHT:
6917 case SCI_WORDPARTRIGHTEXTEND:
6918 case SCI_DELETEBACKNOTLINE:
6919 case SCI_HOMEDISPLAY:
6920 case SCI_HOMEDISPLAYEXTEND:
6921 case SCI_LINEENDDISPLAY:
6922 case SCI_LINEENDDISPLAYEXTEND:
6923 case SCI_LINEDOWNRECTEXTEND:
6924 case SCI_LINEUPRECTEXTEND:
6925 case SCI_CHARLEFTRECTEXTEND:
6926 case SCI_CHARRIGHTRECTEXTEND:
6927 case SCI_HOMERECTEXTEND:
6928 case SCI_VCHOMERECTEXTEND:
6929 case SCI_LINEENDRECTEXTEND:
6930 case SCI_PAGEUPRECTEXTEND:
6931 case SCI_PAGEDOWNRECTEXTEND:
6932 case SCI_SELECTIONDUPLICATE:
6933 return KeyCommand(iMessage);
6935 case SCI_BRACEHIGHLIGHT:
6936 SetBraceHighlight(static_cast<int>(wParam), static_cast<int>(lParam), STYLE_BRACELIGHT);
6937 break;
6939 case SCI_BRACEHIGHLIGHTINDICATOR:
6940 if (lParam >= 0 && lParam <= INDIC_MAX) {
6941 vs.braceHighlightIndicatorSet = wParam != 0;
6942 vs.braceHighlightIndicator = static_cast<int>(lParam);
6944 break;
6946 case SCI_BRACEBADLIGHT:
6947 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
6948 break;
6950 case SCI_BRACEBADLIGHTINDICATOR:
6951 if (lParam >= 0 && lParam <= INDIC_MAX) {
6952 vs.braceBadLightIndicatorSet = wParam != 0;
6953 vs.braceBadLightIndicator = static_cast<int>(lParam);
6955 break;
6957 case SCI_BRACEMATCH:
6958 // wParam is position of char to find brace for,
6959 // lParam is maximum amount of text to restyle to find it
6960 return pdoc->BraceMatch(static_cast<int>(wParam), static_cast<int>(lParam));
6962 case SCI_GETVIEWEOL:
6963 return vs.viewEOL;
6965 case SCI_SETVIEWEOL:
6966 vs.viewEOL = wParam != 0;
6967 InvalidateStyleRedraw();
6968 break;
6970 case SCI_SETZOOM:
6971 vs.zoomLevel = static_cast<int>(wParam);
6972 InvalidateStyleRedraw();
6973 NotifyZoom();
6974 break;
6976 case SCI_GETZOOM:
6977 return vs.zoomLevel;
6979 case SCI_GETEDGECOLUMN:
6980 return vs.theEdge;
6982 case SCI_SETEDGECOLUMN:
6983 vs.theEdge = static_cast<int>(wParam);
6984 InvalidateStyleRedraw();
6985 break;
6987 case SCI_GETEDGEMODE:
6988 return vs.edgeState;
6990 case SCI_SETEDGEMODE:
6991 vs.edgeState = static_cast<int>(wParam);
6992 InvalidateStyleRedraw();
6993 break;
6995 case SCI_GETEDGECOLOUR:
6996 return vs.edgecolour.AsLong();
6998 case SCI_SETEDGECOLOUR:
6999 vs.edgecolour = ColourDesired(static_cast<long>(wParam));
7000 InvalidateStyleRedraw();
7001 break;
7003 case SCI_GETDOCPOINTER:
7004 return reinterpret_cast<sptr_t>(pdoc);
7006 case SCI_SETDOCPOINTER:
7007 CancelModes();
7008 SetDocPointer(reinterpret_cast<Document *>(lParam));
7009 return 0;
7011 case SCI_CREATEDOCUMENT: {
7012 Document *doc = new Document();
7013 doc->AddRef();
7014 return reinterpret_cast<sptr_t>(doc);
7017 case SCI_ADDREFDOCUMENT:
7018 (reinterpret_cast<Document *>(lParam))->AddRef();
7019 break;
7021 case SCI_RELEASEDOCUMENT:
7022 (reinterpret_cast<Document *>(lParam))->Release();
7023 break;
7025 case SCI_CREATELOADER: {
7026 Document *doc = new Document();
7027 doc->AddRef();
7028 doc->Allocate(static_cast<int>(wParam));
7029 doc->SetUndoCollection(false);
7030 return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
7033 case SCI_SETMODEVENTMASK:
7034 modEventMask = static_cast<int>(wParam);
7035 return 0;
7037 case SCI_GETMODEVENTMASK:
7038 return modEventMask;
7040 case SCI_CONVERTEOLS:
7041 pdoc->ConvertLineEnds(static_cast<int>(wParam));
7042 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
7043 return 0;
7045 case SCI_SETLENGTHFORENCODE:
7046 lengthForEncode = static_cast<int>(wParam);
7047 return 0;
7049 case SCI_SELECTIONISRECTANGLE:
7050 return sel.selType == Selection::selRectangle ? 1 : 0;
7052 case SCI_SETSELECTIONMODE: {
7053 switch (wParam) {
7054 case SC_SEL_STREAM:
7055 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7056 sel.selType = Selection::selStream;
7057 break;
7058 case SC_SEL_RECTANGLE:
7059 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
7060 sel.selType = Selection::selRectangle;
7061 break;
7062 case SC_SEL_LINES:
7063 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
7064 sel.selType = Selection::selLines;
7065 break;
7066 case SC_SEL_THIN:
7067 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
7068 sel.selType = Selection::selThin;
7069 break;
7070 default:
7071 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7072 sel.selType = Selection::selStream;
7074 InvalidateSelection(sel.RangeMain(), true);
7075 break;
7077 case SCI_GETSELECTIONMODE:
7078 switch (sel.selType) {
7079 case Selection::selStream:
7080 return SC_SEL_STREAM;
7081 case Selection::selRectangle:
7082 return SC_SEL_RECTANGLE;
7083 case Selection::selLines:
7084 return SC_SEL_LINES;
7085 case Selection::selThin:
7086 return SC_SEL_THIN;
7087 default: // ?!
7088 return SC_SEL_STREAM;
7090 case SCI_GETLINESELSTARTPOSITION:
7091 case SCI_GETLINESELENDPOSITION: {
7092 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(static_cast<int>(wParam))),
7093 SelectionPosition(pdoc->LineEnd(static_cast<int>(wParam))));
7094 for (size_t r=0; r<sel.Count(); r++) {
7095 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
7096 if (portion.start.IsValid()) {
7097 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
7100 return INVALID_POSITION;
7103 case SCI_SETOVERTYPE:
7104 inOverstrike = wParam != 0;
7105 break;
7107 case SCI_GETOVERTYPE:
7108 return inOverstrike ? 1 : 0;
7110 case SCI_SETFOCUS:
7111 SetFocusState(wParam != 0);
7112 break;
7114 case SCI_GETFOCUS:
7115 return hasFocus;
7117 case SCI_SETSTATUS:
7118 errorStatus = static_cast<int>(wParam);
7119 break;
7121 case SCI_GETSTATUS:
7122 return errorStatus;
7124 case SCI_SETMOUSEDOWNCAPTURES:
7125 mouseDownCaptures = wParam != 0;
7126 break;
7128 case SCI_GETMOUSEDOWNCAPTURES:
7129 return mouseDownCaptures;
7131 case SCI_SETCURSOR:
7132 cursorMode = static_cast<int>(wParam);
7133 DisplayCursor(Window::cursorText);
7134 break;
7136 case SCI_GETCURSOR:
7137 return cursorMode;
7139 case SCI_SETCONTROLCHARSYMBOL:
7140 vs.controlCharSymbol = static_cast<int>(wParam);
7141 InvalidateStyleRedraw();
7142 break;
7144 case SCI_GETCONTROLCHARSYMBOL:
7145 return vs.controlCharSymbol;
7147 case SCI_SETREPRESENTATION:
7148 reprs.SetRepresentation(reinterpret_cast<const char *>(wParam), CharPtrFromSPtr(lParam));
7149 break;
7151 case SCI_GETREPRESENTATION: {
7152 const Representation *repr = reprs.RepresentationFromCharacter(
7153 reinterpret_cast<const char *>(wParam), UTF8MaxBytes);
7154 if (repr) {
7155 return StringResult(lParam, repr->stringRep.c_str());
7157 return 0;
7160 case SCI_CLEARREPRESENTATION:
7161 reprs.ClearRepresentation(reinterpret_cast<const char *>(wParam));
7162 break;
7164 case SCI_STARTRECORD:
7165 recordingMacro = true;
7166 return 0;
7168 case SCI_STOPRECORD:
7169 recordingMacro = false;
7170 return 0;
7172 case SCI_MOVECARETINSIDEVIEW:
7173 MoveCaretInsideView();
7174 break;
7176 case SCI_SETFOLDMARGINCOLOUR:
7177 vs.foldmarginColour = ColourOptional(wParam, lParam);
7178 InvalidateStyleRedraw();
7179 break;
7181 case SCI_SETFOLDMARGINHICOLOUR:
7182 vs.foldmarginHighlightColour = ColourOptional(wParam, lParam);
7183 InvalidateStyleRedraw();
7184 break;
7186 case SCI_SETHOTSPOTACTIVEFORE:
7187 vs.hotspotColours.fore = ColourOptional(wParam, lParam);
7188 InvalidateStyleRedraw();
7189 break;
7191 case SCI_GETHOTSPOTACTIVEFORE:
7192 return vs.hotspotColours.fore.AsLong();
7194 case SCI_SETHOTSPOTACTIVEBACK:
7195 vs.hotspotColours.back = ColourOptional(wParam, lParam);
7196 InvalidateStyleRedraw();
7197 break;
7199 case SCI_GETHOTSPOTACTIVEBACK:
7200 return vs.hotspotColours.back.AsLong();
7202 case SCI_SETHOTSPOTACTIVEUNDERLINE:
7203 vs.hotspotUnderline = wParam != 0;
7204 InvalidateStyleRedraw();
7205 break;
7207 case SCI_GETHOTSPOTACTIVEUNDERLINE:
7208 return vs.hotspotUnderline ? 1 : 0;
7210 case SCI_SETHOTSPOTSINGLELINE:
7211 vs.hotspotSingleLine = wParam != 0;
7212 InvalidateStyleRedraw();
7213 break;
7215 case SCI_GETHOTSPOTSINGLELINE:
7216 return vs.hotspotSingleLine ? 1 : 0;
7218 case SCI_SETPASTECONVERTENDINGS:
7219 convertPastes = wParam != 0;
7220 break;
7222 case SCI_GETPASTECONVERTENDINGS:
7223 return convertPastes ? 1 : 0;
7225 case SCI_GETCHARACTERPOINTER:
7226 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
7228 case SCI_GETRANGEPOINTER:
7229 return reinterpret_cast<sptr_t>(pdoc->RangePointer(static_cast<int>(wParam), static_cast<int>(lParam)));
7231 case SCI_GETGAPPOSITION:
7232 return pdoc->GapPosition();
7234 case SCI_SETEXTRAASCENT:
7235 vs.extraAscent = static_cast<int>(wParam);
7236 InvalidateStyleRedraw();
7237 break;
7239 case SCI_GETEXTRAASCENT:
7240 return vs.extraAscent;
7242 case SCI_SETEXTRADESCENT:
7243 vs.extraDescent = static_cast<int>(wParam);
7244 InvalidateStyleRedraw();
7245 break;
7247 case SCI_GETEXTRADESCENT:
7248 return vs.extraDescent;
7250 case SCI_MARGINSETSTYLEOFFSET:
7251 vs.marginStyleOffset = static_cast<int>(wParam);
7252 InvalidateStyleRedraw();
7253 break;
7255 case SCI_MARGINGETSTYLEOFFSET:
7256 return vs.marginStyleOffset;
7258 case SCI_SETMARGINOPTIONS:
7259 marginOptions = static_cast<int>(wParam);
7260 break;
7262 case SCI_GETMARGINOPTIONS:
7263 return marginOptions;
7265 case SCI_MARGINSETTEXT:
7266 pdoc->MarginSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7267 break;
7269 case SCI_MARGINGETTEXT: {
7270 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7271 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
7274 case SCI_MARGINSETSTYLE:
7275 pdoc->MarginSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
7276 break;
7278 case SCI_MARGINGETSTYLE: {
7279 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7280 return st.style;
7283 case SCI_MARGINSETSTYLES:
7284 pdoc->MarginSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
7285 break;
7287 case SCI_MARGINGETSTYLES: {
7288 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
7289 return BytesResult(lParam, st.styles, st.length);
7292 case SCI_MARGINTEXTCLEARALL:
7293 pdoc->MarginClearAll();
7294 break;
7296 case SCI_ANNOTATIONSETTEXT:
7297 pdoc->AnnotationSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7298 break;
7300 case SCI_ANNOTATIONGETTEXT: {
7301 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7302 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
7305 case SCI_ANNOTATIONGETSTYLE: {
7306 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7307 return st.style;
7310 case SCI_ANNOTATIONSETSTYLE:
7311 pdoc->AnnotationSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
7312 break;
7314 case SCI_ANNOTATIONSETSTYLES:
7315 pdoc->AnnotationSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
7316 break;
7318 case SCI_ANNOTATIONGETSTYLES: {
7319 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
7320 return BytesResult(lParam, st.styles, st.length);
7323 case SCI_ANNOTATIONGETLINES:
7324 return pdoc->AnnotationLines(static_cast<int>(wParam));
7326 case SCI_ANNOTATIONCLEARALL:
7327 pdoc->AnnotationClearAll();
7328 break;
7330 case SCI_ANNOTATIONSETVISIBLE:
7331 SetAnnotationVisible(static_cast<int>(wParam));
7332 break;
7334 case SCI_ANNOTATIONGETVISIBLE:
7335 return vs.annotationVisible;
7337 case SCI_ANNOTATIONSETSTYLEOFFSET:
7338 vs.annotationStyleOffset = static_cast<int>(wParam);
7339 InvalidateStyleRedraw();
7340 break;
7342 case SCI_ANNOTATIONGETSTYLEOFFSET:
7343 return vs.annotationStyleOffset;
7345 case SCI_RELEASEALLEXTENDEDSTYLES:
7346 vs.ReleaseAllExtendedStyles();
7347 break;
7349 case SCI_ALLOCATEEXTENDEDSTYLES:
7350 return vs.AllocateExtendedStyles(static_cast<int>(wParam));
7352 case SCI_ADDUNDOACTION:
7353 pdoc->AddUndoAction(static_cast<int>(wParam), lParam & UNDO_MAY_COALESCE);
7354 break;
7356 case SCI_SETMOUSESELECTIONRECTANGULARSWITCH:
7357 mouseSelectionRectangularSwitch = wParam != 0;
7358 break;
7360 case SCI_GETMOUSESELECTIONRECTANGULARSWITCH:
7361 return mouseSelectionRectangularSwitch;
7363 case SCI_SETMULTIPLESELECTION:
7364 multipleSelection = wParam != 0;
7365 InvalidateCaret();
7366 break;
7368 case SCI_GETMULTIPLESELECTION:
7369 return multipleSelection;
7371 case SCI_SETADDITIONALSELECTIONTYPING:
7372 additionalSelectionTyping = wParam != 0;
7373 InvalidateCaret();
7374 break;
7376 case SCI_GETADDITIONALSELECTIONTYPING:
7377 return additionalSelectionTyping;
7379 case SCI_SETMULTIPASTE:
7380 multiPasteMode = static_cast<int>(wParam);
7381 break;
7383 case SCI_GETMULTIPASTE:
7384 return multiPasteMode;
7386 case SCI_SETADDITIONALCARETSBLINK:
7387 view.additionalCaretsBlink = wParam != 0;
7388 InvalidateCaret();
7389 break;
7391 case SCI_GETADDITIONALCARETSBLINK:
7392 return view.additionalCaretsBlink;
7394 case SCI_SETADDITIONALCARETSVISIBLE:
7395 view.additionalCaretsVisible = wParam != 0;
7396 InvalidateCaret();
7397 break;
7399 case SCI_GETADDITIONALCARETSVISIBLE:
7400 return view.additionalCaretsVisible;
7402 case SCI_GETSELECTIONS:
7403 return sel.Count();
7405 case SCI_GETSELECTIONEMPTY:
7406 return sel.Empty();
7408 case SCI_CLEARSELECTIONS:
7409 sel.Clear();
7410 Redraw();
7411 break;
7413 case SCI_SETSELECTION:
7414 sel.SetSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
7415 Redraw();
7416 break;
7418 case SCI_ADDSELECTION:
7419 sel.AddSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
7420 Redraw();
7421 break;
7423 case SCI_DROPSELECTIONN:
7424 sel.DropSelection(static_cast<int>(wParam));
7425 Redraw();
7426 break;
7428 case SCI_SETMAINSELECTION:
7429 sel.SetMain(static_cast<int>(wParam));
7430 Redraw();
7431 break;
7433 case SCI_GETMAINSELECTION:
7434 return sel.Main();
7436 case SCI_SETSELECTIONNCARET:
7437 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
7438 Redraw();
7439 break;
7441 case SCI_GETSELECTIONNCARET:
7442 return sel.Range(wParam).caret.Position();
7444 case SCI_SETSELECTIONNANCHOR:
7445 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
7446 Redraw();
7447 break;
7448 case SCI_GETSELECTIONNANCHOR:
7449 return sel.Range(wParam).anchor.Position();
7451 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
7452 sel.Range(wParam).caret.SetVirtualSpace(static_cast<int>(lParam));
7453 Redraw();
7454 break;
7456 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
7457 return sel.Range(wParam).caret.VirtualSpace();
7459 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
7460 sel.Range(wParam).anchor.SetVirtualSpace(static_cast<int>(lParam));
7461 Redraw();
7462 break;
7464 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
7465 return sel.Range(wParam).anchor.VirtualSpace();
7467 case SCI_SETSELECTIONNSTART:
7468 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
7469 Redraw();
7470 break;
7472 case SCI_GETSELECTIONNSTART:
7473 return sel.Range(wParam).Start().Position();
7475 case SCI_SETSELECTIONNEND:
7476 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
7477 Redraw();
7478 break;
7480 case SCI_GETSELECTIONNEND:
7481 return sel.Range(wParam).End().Position();
7483 case SCI_SETRECTANGULARSELECTIONCARET:
7484 if (!sel.IsRectangular())
7485 sel.Clear();
7486 sel.selType = Selection::selRectangle;
7487 sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
7488 SetRectangularRange();
7489 Redraw();
7490 break;
7492 case SCI_GETRECTANGULARSELECTIONCARET:
7493 return sel.Rectangular().caret.Position();
7495 case SCI_SETRECTANGULARSELECTIONANCHOR:
7496 if (!sel.IsRectangular())
7497 sel.Clear();
7498 sel.selType = Selection::selRectangle;
7499 sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
7500 SetRectangularRange();
7501 Redraw();
7502 break;
7504 case SCI_GETRECTANGULARSELECTIONANCHOR:
7505 return sel.Rectangular().anchor.Position();
7507 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
7508 if (!sel.IsRectangular())
7509 sel.Clear();
7510 sel.selType = Selection::selRectangle;
7511 sel.Rectangular().caret.SetVirtualSpace(static_cast<int>(wParam));
7512 SetRectangularRange();
7513 Redraw();
7514 break;
7516 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
7517 return sel.Rectangular().caret.VirtualSpace();
7519 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
7520 if (!sel.IsRectangular())
7521 sel.Clear();
7522 sel.selType = Selection::selRectangle;
7523 sel.Rectangular().anchor.SetVirtualSpace(static_cast<int>(wParam));
7524 SetRectangularRange();
7525 Redraw();
7526 break;
7528 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
7529 return sel.Rectangular().anchor.VirtualSpace();
7531 case SCI_SETVIRTUALSPACEOPTIONS:
7532 virtualSpaceOptions = static_cast<int>(wParam);
7533 break;
7535 case SCI_GETVIRTUALSPACEOPTIONS:
7536 return virtualSpaceOptions;
7538 case SCI_SETADDITIONALSELFORE:
7539 vs.selAdditionalForeground = ColourDesired(static_cast<long>(wParam));
7540 InvalidateStyleRedraw();
7541 break;
7543 case SCI_SETADDITIONALSELBACK:
7544 vs.selAdditionalBackground = ColourDesired(static_cast<long>(wParam));
7545 InvalidateStyleRedraw();
7546 break;
7548 case SCI_SETADDITIONALSELALPHA:
7549 vs.selAdditionalAlpha = static_cast<int>(wParam);
7550 InvalidateStyleRedraw();
7551 break;
7553 case SCI_GETADDITIONALSELALPHA:
7554 return vs.selAdditionalAlpha;
7556 case SCI_SETADDITIONALCARETFORE:
7557 vs.additionalCaretColour = ColourDesired(static_cast<long>(wParam));
7558 InvalidateStyleRedraw();
7559 break;
7561 case SCI_GETADDITIONALCARETFORE:
7562 return vs.additionalCaretColour.AsLong();
7564 case SCI_ROTATESELECTION:
7565 sel.RotateMain();
7566 InvalidateSelection(sel.RangeMain(), true);
7567 break;
7569 case SCI_SWAPMAINANCHORCARET:
7570 InvalidateSelection(sel.RangeMain());
7571 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
7572 break;
7574 case SCI_CHANGELEXERSTATE:
7575 pdoc->ChangeLexerState(static_cast<int>(wParam), static_cast<int>(lParam));
7576 break;
7578 case SCI_SETIDENTIFIER:
7579 SetCtrlID(static_cast<int>(wParam));
7580 break;
7582 case SCI_GETIDENTIFIER:
7583 return GetCtrlID();
7585 case SCI_SETTECHNOLOGY:
7586 // No action by default
7587 break;
7589 case SCI_GETTECHNOLOGY:
7590 return technology;
7592 case SCI_COUNTCHARACTERS:
7593 return pdoc->CountCharacters(static_cast<int>(wParam), static_cast<int>(lParam));
7595 default:
7596 return DefWndProc(iMessage, wParam, lParam);
7598 //Platform::DebugPrintf("end wnd proc\n");
7599 return 0l;