Update Scintilla to 3.4.2 pre-release
[geany-mirror.git] / scintilla / src / Editor.cxx
blob24900dc77f9f617c3c11627fd34bb200dba16179
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 <ctype.h>
12 #include <math.h>
13 #include <assert.h>
15 #include <string>
16 #include <vector>
17 #include <map>
18 #include <algorithm>
19 #include <memory>
21 #include "Platform.h"
23 #include "ILexer.h"
24 #include "Scintilla.h"
26 #include "StringCopy.h"
27 #include "SplitVector.h"
28 #include "Partitioning.h"
29 #include "RunStyles.h"
30 #include "ContractionState.h"
31 #include "CellBuffer.h"
32 #include "KeyMap.h"
33 #include "Indicator.h"
34 #include "XPM.h"
35 #include "LineMarker.h"
36 #include "Style.h"
37 #include "ViewStyle.h"
38 #include "CharClassify.h"
39 #include "Decoration.h"
40 #include "CaseFolder.h"
41 #include "Document.h"
42 #include "UniConversion.h"
43 #include "Selection.h"
44 #include "PositionCache.h"
45 #include "Editor.h"
47 #ifdef SCI_NAMESPACE
48 using namespace Scintilla;
49 #endif
52 return whether this modification represents an operation that
53 may reasonably be deferred (not done now OR [possibly] at all)
55 static bool CanDeferToLastStep(const DocModification &mh) {
56 if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
57 return true; // CAN skip
58 if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)))
59 return false; // MUST do
60 if (mh.modificationType & SC_MULTISTEPUNDOREDO)
61 return true; // CAN skip
62 return false; // PRESUMABLY must do
65 static bool CanEliminate(const DocModification &mh) {
66 return
67 (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
71 return whether this modification represents the FINAL step
72 in a [possibly lengthy] multi-step Undo/Redo sequence
74 static bool IsLastStep(const DocModification &mh) {
75 return
76 (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0
77 && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
78 && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
79 && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
82 Caret::Caret() :
83 active(false), on(false), period(500) {}
85 Timer::Timer() :
86 ticking(false), ticksToWait(0), tickerID(0) {}
88 Idler::Idler() :
89 state(false), idlerID(0) {}
91 static inline bool IsControlCharacter(int ch) {
92 // iscntrl returns true for lots of chars > 127 which are displayable
93 return ch >= 0 && ch < ' ';
96 static inline bool IsAllSpacesOrTabs(char *s, unsigned int len) {
97 for (unsigned int i = 0; i < len; i++) {
98 // This is safe because IsSpaceOrTab() will return false for null terminators
99 if (!IsSpaceOrTab(s[i]))
100 return false;
102 return true;
105 PrintParameters::PrintParameters() {
106 magnification = 0;
107 colourMode = SC_PRINT_NORMAL;
108 wrapState = eWrapWord;
111 Editor::Editor() {
112 ctrlID = 0;
114 stylesValid = false;
115 technology = SC_TECHNOLOGY_DEFAULT;
116 scaleRGBAImage = 100.0f;
118 cursorMode = SC_CURSORNORMAL;
120 hasFocus = false;
121 hideSelection = false;
122 inOverstrike = false;
123 drawOverstrikeCaret = true;
124 errorStatus = 0;
125 mouseDownCaptures = true;
127 bufferedDraw = true;
128 twoPhaseDraw = true;
130 lastClickTime = 0;
131 dwellDelay = SC_TIME_FOREVER;
132 ticksToDwell = SC_TIME_FOREVER;
133 dwelling = false;
134 ptMouseLast.x = 0;
135 ptMouseLast.y = 0;
136 inDragDrop = ddNone;
137 dropWentOutside = false;
138 posDrag = SelectionPosition(invalidPosition);
139 posDrop = SelectionPosition(invalidPosition);
140 hotSpotClickPos = INVALID_POSITION;
141 selectionType = selChar;
143 lastXChosen = 0;
144 lineAnchorPos = 0;
145 originalAnchorPos = 0;
146 wordSelectAnchorStartPos = 0;
147 wordSelectAnchorEndPos = 0;
148 wordSelectInitialCaretPos = -1;
150 primarySelection = true;
152 caretXPolicy = CARET_SLOP | CARET_EVEN;
153 caretXSlop = 50;
155 caretYPolicy = CARET_EVEN;
156 caretYSlop = 0;
158 visiblePolicy = 0;
159 visibleSlop = 0;
161 searchAnchor = 0;
163 xOffset = 0;
164 xCaretMargin = 50;
165 horizontalScrollBarVisible = true;
166 scrollWidth = 2000;
167 trackLineWidth = false;
168 lineWidthMaxSeen = 0;
169 verticalScrollBarVisible = true;
170 endAtLastLine = true;
171 caretSticky = SC_CARETSTICKY_OFF;
172 marginOptions = SC_MARGINOPTION_NONE;
173 mouseSelectionRectangularSwitch = false;
174 multipleSelection = false;
175 additionalSelectionTyping = false;
176 multiPasteMode = SC_MULTIPASTE_ONCE;
177 additionalCaretsBlink = true;
178 additionalCaretsVisible = true;
179 virtualSpaceOptions = SCVS_NONE;
181 pixmapLine = 0;
182 pixmapSelMargin = 0;
183 pixmapSelPattern = 0;
184 pixmapSelPatternOffset1 = 0;
185 pixmapIndentGuide = 0;
186 pixmapIndentGuideHighlight = 0;
188 targetStart = 0;
189 targetEnd = 0;
190 searchFlags = 0;
192 topLine = 0;
193 posTopLine = 0;
195 lengthForEncode = -1;
197 needUpdateUI = 0;
198 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
199 braces[0] = invalidPosition;
200 braces[1] = invalidPosition;
201 bracesMatchStyle = STYLE_BRACEBAD;
202 highlightGuideColumn = 0;
204 paintState = notPainting;
205 paintAbandonedByStyling = false;
206 paintingAllText = false;
207 willRedrawAll = false;
209 modEventMask = SC_MODEVENTMASKALL;
211 pdoc = new Document();
212 pdoc->AddRef();
213 pdoc->AddWatcher(this, 0);
215 recordingMacro = false;
216 foldFlags = 0;
217 foldAutomatic = 0;
219 wrapWidth = LineLayout::wrapWidthInfinite;
221 convertPastes = true;
223 hsStart = -1;
224 hsEnd = -1;
226 llc.SetLevel(LineLayoutCache::llcCaret);
227 posCache.SetSize(0x400);
229 SetRepresentations();
232 Editor::~Editor() {
233 pdoc->RemoveWatcher(this, 0);
234 pdoc->Release();
235 pdoc = 0;
236 DropGraphics(true);
239 void Editor::Finalise() {
240 SetIdle(false);
241 CancelModes();
244 void Editor::SetRepresentations() {
245 reprs.Clear();
247 // C0 control set
248 const char *reps[] = {
249 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
250 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
251 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
252 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
254 for (size_t j=0; j < ELEMENTS(reps); j++) {
255 char c[2] = { static_cast<char>(j), 0 };
256 reprs.SetRepresentation(c, reps[j]);
259 // C1 control set
260 // As well as Unicode mode, ISO-8859-1 should use these
261 if (IsUnicodeMode()) {
262 const char *repsC1[] = {
263 "PAD", "HOP", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
264 "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
265 "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
266 "SOS", "SGCI", "SCI", "CSI", "ST", "OSC", "PM", "APC"
268 for (size_t j=0; j < ELEMENTS(repsC1); j++) {
269 char c1[3] = { '\xc2', static_cast<char>(0x80+j), 0 };
270 reprs.SetRepresentation(c1, repsC1[j]);
272 reprs.SetRepresentation("\xe2\x80\xa8", "LS");
273 reprs.SetRepresentation("\xe2\x80\xa9", "PS");
276 // UTF-8 invalid bytes
277 if (IsUnicodeMode()) {
278 for (int k=0x80; k < 0x100; k++) {
279 char hiByte[2] = { static_cast<char>(k), 0 };
280 char hexits[4];
281 sprintf(hexits, "x%2X", k);
282 reprs.SetRepresentation(hiByte, hexits);
287 void Editor::DropGraphics(bool freeObjects) {
288 if (freeObjects) {
289 delete pixmapLine;
290 pixmapLine = 0;
291 delete pixmapSelMargin;
292 pixmapSelMargin = 0;
293 delete pixmapSelPattern;
294 pixmapSelPattern = 0;
295 delete pixmapSelPatternOffset1;
296 pixmapSelPatternOffset1 = 0;
297 delete pixmapIndentGuide;
298 pixmapIndentGuide = 0;
299 delete pixmapIndentGuideHighlight;
300 pixmapIndentGuideHighlight = 0;
301 } else {
302 if (pixmapLine)
303 pixmapLine->Release();
304 if (pixmapSelMargin)
305 pixmapSelMargin->Release();
306 if (pixmapSelPattern)
307 pixmapSelPattern->Release();
308 if (pixmapSelPatternOffset1)
309 pixmapSelPatternOffset1->Release();
310 if (pixmapIndentGuide)
311 pixmapIndentGuide->Release();
312 if (pixmapIndentGuideHighlight)
313 pixmapIndentGuideHighlight->Release();
317 void Editor::AllocateGraphics() {
318 if (!pixmapLine)
319 pixmapLine = Surface::Allocate(technology);
320 if (!pixmapSelMargin)
321 pixmapSelMargin = Surface::Allocate(technology);
322 if (!pixmapSelPattern)
323 pixmapSelPattern = Surface::Allocate(technology);
324 if (!pixmapSelPatternOffset1)
325 pixmapSelPatternOffset1 = Surface::Allocate(technology);
326 if (!pixmapIndentGuide)
327 pixmapIndentGuide = Surface::Allocate(technology);
328 if (!pixmapIndentGuideHighlight)
329 pixmapIndentGuideHighlight = Surface::Allocate(technology);
332 void Editor::InvalidateStyleData() {
333 stylesValid = false;
334 vs.technology = technology;
335 DropGraphics(false);
336 AllocateGraphics();
337 llc.Invalidate(LineLayout::llInvalid);
338 posCache.Clear();
341 void Editor::InvalidateStyleRedraw() {
342 NeedWrapping();
343 InvalidateStyleData();
344 Redraw();
347 void Editor::RefreshStyleData() {
348 if (!stylesValid) {
349 stylesValid = true;
350 AutoSurface surface(this);
351 if (surface) {
352 vs.Refresh(*surface, pdoc->tabInChars);
354 SetScrollBars();
355 SetRectangularRange();
359 Point Editor::GetVisibleOriginInMain() {
360 return Point(0,0);
363 Point Editor::DocumentPointFromView(Point ptView) {
364 Point ptDocument = ptView;
365 if (wMargin.GetID()) {
366 Point ptOrigin = GetVisibleOriginInMain();
367 ptDocument.x += ptOrigin.x;
368 ptDocument.y += ptOrigin.y;
369 } else {
370 ptDocument.x += xOffset;
371 ptDocument.y += topLine * vs.lineHeight;
373 return ptDocument;
376 int Editor::TopLineOfMain() const {
377 if (wMargin.GetID())
378 return 0;
379 else
380 return topLine;
383 PRectangle Editor::GetClientRectangle() {
384 return wMain.GetClientPosition();
387 PRectangle Editor::GetClientDrawingRectangle() {
388 return GetClientRectangle();
391 PRectangle Editor::GetTextRectangle() {
392 PRectangle rc = GetClientRectangle();
393 rc.left += vs.textStart;
394 rc.right -= vs.rightMarginWidth;
395 return rc;
398 int Editor::LinesOnScreen() {
399 PRectangle rcClient = GetClientRectangle();
400 int htClient = static_cast<int>(rcClient.bottom - rcClient.top);
401 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
402 return htClient / vs.lineHeight;
405 int Editor::LinesToScroll() {
406 int retVal = LinesOnScreen() - 1;
407 if (retVal < 1)
408 return 1;
409 else
410 return retVal;
413 int Editor::MaxScrollPos() {
414 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
415 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
416 int retVal = cs.LinesDisplayed();
417 if (endAtLastLine) {
418 retVal -= LinesOnScreen();
419 } else {
420 retVal--;
422 if (retVal < 0) {
423 return 0;
424 } else {
425 return retVal;
429 const char *ControlCharacterString(unsigned char ch) {
430 const char *reps[] = {
431 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
432 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
433 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
434 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
436 if (ch < ELEMENTS(reps)) {
437 return reps[ch];
438 } else {
439 return "BAD";
444 * Convenience class to ensure LineLayout objects are always disposed.
446 class AutoLineLayout {
447 LineLayoutCache &llc;
448 LineLayout *ll;
449 AutoLineLayout &operator=(const AutoLineLayout &);
450 public:
451 AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
452 ~AutoLineLayout() {
453 llc.Dispose(ll);
454 ll = 0;
456 LineLayout *operator->() const {
457 return ll;
459 operator LineLayout *() const {
460 return ll;
462 void Set(LineLayout *ll_) {
463 llc.Dispose(ll);
464 ll = ll_;
468 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
469 if (sp.Position() < 0) {
470 return SelectionPosition(0);
471 } else if (sp.Position() > pdoc->Length()) {
472 return SelectionPosition(pdoc->Length());
473 } else {
474 // If not at end of line then set offset to 0
475 if (!pdoc->IsLineEndPosition(sp.Position()))
476 sp.SetVirtualSpace(0);
477 return sp;
481 Point Editor::LocationFromPosition(SelectionPosition pos) {
482 Point pt;
483 RefreshStyleData();
484 if (pos.Position() == INVALID_POSITION)
485 return pt;
486 const int line = pdoc->LineFromPosition(pos.Position());
487 const int lineVisible = cs.DisplayFromDoc(line);
488 //Platform::DebugPrintf("line=%d\n", line);
489 AutoSurface surface(this);
490 AutoLineLayout ll(llc, RetrieveLineLayout(line));
491 if (surface && ll) {
492 const int posLineStart = pdoc->LineStart(line);
493 LayoutLine(line, surface, vs, ll, wrapWidth);
494 const int posInLine = pos.Position() - posLineStart;
495 pt = ll->PointFromPosition(posInLine, vs.lineHeight);
496 pt.y += (lineVisible - topLine) * vs.lineHeight;
497 pt.x += vs.textStart - xOffset;
499 pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
500 return pt;
503 Point Editor::LocationFromPosition(int pos) {
504 return LocationFromPosition(SelectionPosition(pos));
507 int Editor::XFromPosition(int pos) {
508 Point pt = LocationFromPosition(pos);
509 return static_cast<int>(pt.x) - vs.textStart + xOffset;
512 int Editor::XFromPosition(SelectionPosition sp) {
513 Point pt = LocationFromPosition(sp);
514 return static_cast<int>(pt.x) - vs.textStart + xOffset;
517 int Editor::LineFromLocation(Point pt) const {
518 return cs.DocFromDisplay(static_cast<int>(pt.y) / vs.lineHeight + topLine);
521 void Editor::SetTopLine(int topLineNew) {
522 if ((topLine != topLineNew) && (topLineNew >= 0)) {
523 topLine = topLineNew;
524 ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
526 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
529 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
530 RefreshStyleData();
531 if (canReturnInvalid) {
532 PRectangle rcClient = GetTextRectangle();
533 // May be in scroll view coordinates so translate back to main view
534 Point ptOrigin = GetVisibleOriginInMain();
535 rcClient.Move(-ptOrigin.x, -ptOrigin.y);
536 if (!rcClient.Contains(pt))
537 return SelectionPosition(INVALID_POSITION);
538 if (pt.x < vs.textStart)
539 return SelectionPosition(INVALID_POSITION);
540 if (pt.y < 0)
541 return SelectionPosition(INVALID_POSITION);
543 pt = DocumentPointFromView(pt);
544 pt.x = pt.x - vs.textStart;
545 int visibleLine = static_cast<int>(floor(pt.y / vs.lineHeight));
546 if (!canReturnInvalid && (visibleLine < 0))
547 visibleLine = 0;
548 const int lineDoc = cs.DocFromDisplay(visibleLine);
549 if (canReturnInvalid && (lineDoc < 0))
550 return SelectionPosition(INVALID_POSITION);
551 if (lineDoc >= pdoc->LinesTotal())
552 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length());
553 const int posLineStart = pdoc->LineStart(lineDoc);
554 AutoSurface surface(this);
555 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
556 if (surface && ll) {
557 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
558 const int lineStartSet = cs.DisplayFromDoc(lineDoc);
559 const int subLine = visibleLine - lineStartSet;
560 if (subLine < ll->lines) {
561 const Range rangeSubLine = ll->SubLineRange(subLine);
562 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
563 if (subLine > 0) // Wrapped
564 pt.x -= ll->wrapIndent;
565 const int positionInLine = ll->FindPositionFromX(pt.x + subLineStart, rangeSubLine, charPosition);
566 if (positionInLine < rangeSubLine.end) {
567 return SelectionPosition(pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
569 if (virtualSpace) {
570 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
571 const int spaceOffset = static_cast<int>(
572 (pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
573 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
574 } else if (canReturnInvalid) {
575 if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) {
576 return SelectionPosition(pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1));
578 } else {
579 return SelectionPosition(rangeSubLine.end + posLineStart);
582 if (!canReturnInvalid)
583 return SelectionPosition(ll->numCharsInLine + posLineStart);
585 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : posLineStart);
588 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
589 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
593 * Find the document position corresponding to an x coordinate on a particular document line.
594 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
595 * This method is used for rectangular selections and does not work on wrapped lines.
597 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
598 RefreshStyleData();
599 if (lineDoc >= pdoc->LinesTotal())
600 return SelectionPosition(pdoc->Length());
601 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
602 AutoSurface surface(this);
603 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
604 if (surface && ll) {
605 const int posLineStart = pdoc->LineStart(lineDoc);
606 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
607 const Range rangeSubLine = ll->SubLineRange(0);
608 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
609 const int positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false);
610 if (positionInLine < rangeSubLine.end) {
611 return SelectionPosition(pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
613 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
614 const int spaceOffset = static_cast<int>(
615 (x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth);
616 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
618 return SelectionPosition(0);
621 int Editor::PositionFromLineX(int lineDoc, int x) {
622 return SPositionFromLineX(lineDoc, x).Position();
626 * If painting then abandon the painting because a wider redraw is needed.
627 * @return true if calling code should stop drawing.
629 bool Editor::AbandonPaint() {
630 if ((paintState == painting) && !paintingAllText) {
631 paintState = paintAbandoned;
633 return paintState == paintAbandoned;
636 void Editor::RedrawRect(PRectangle rc) {
637 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
639 // Clip the redraw rectangle into the client area
640 PRectangle rcClient = GetClientRectangle();
641 if (rc.top < rcClient.top)
642 rc.top = rcClient.top;
643 if (rc.bottom > rcClient.bottom)
644 rc.bottom = rcClient.bottom;
645 if (rc.left < rcClient.left)
646 rc.left = rcClient.left;
647 if (rc.right > rcClient.right)
648 rc.right = rcClient.right;
650 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
651 wMain.InvalidateRectangle(rc);
655 void Editor::DiscardOverdraw() {
656 // Overridden on platforms that may draw outside visible area.
659 void Editor::Redraw() {
660 //Platform::DebugPrintf("Redraw all\n");
661 PRectangle rcClient = GetClientRectangle();
662 wMain.InvalidateRectangle(rcClient);
663 if (wMargin.GetID())
664 wMargin.InvalidateAll();
665 //wMain.InvalidateAll();
668 void Editor::RedrawSelMargin(int line, bool allAfter) {
669 bool abandonDraw = false;
670 if (!wMargin.GetID()) // Margin in main window so may need to abandon and retry
671 abandonDraw = AbandonPaint();
672 if (!abandonDraw) {
673 if (vs.maskInLine) {
674 Redraw();
675 } else {
676 PRectangle rcSelMargin = GetClientRectangle();
677 rcSelMargin.right = rcSelMargin.left + vs.fixedColumnWidth;
678 if (line != -1) {
679 PRectangle rcLine = RectangleFromRange(Range(pdoc->LineStart(line)));
681 // Inflate line rectangle if there are image markers with height larger than line height
682 if (vs.largestMarkerHeight > vs.lineHeight) {
683 int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
684 rcLine.top -= delta;
685 rcLine.bottom += delta;
686 if (rcLine.top < rcSelMargin.top)
687 rcLine.top = rcSelMargin.top;
688 if (rcLine.bottom > rcSelMargin.bottom)
689 rcLine.bottom = rcSelMargin.bottom;
692 rcSelMargin.top = rcLine.top;
693 if (!allAfter)
694 rcSelMargin.bottom = rcLine.bottom;
695 if (rcSelMargin.Empty())
696 return;
698 if (wMargin.GetID()) {
699 Point ptOrigin = GetVisibleOriginInMain();
700 rcSelMargin.Move(-ptOrigin.x, -ptOrigin.y);
701 wMargin.InvalidateRectangle(rcSelMargin);
702 } else {
703 wMain.InvalidateRectangle(rcSelMargin);
709 PRectangle Editor::RectangleFromRange(Range r) {
710 const int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(r.First()));
711 const int maxLine = cs.DisplayLastFromDoc(pdoc->LineFromPosition(r.Last()));
712 const PRectangle rcClientDrawing = GetClientDrawingRectangle();
713 PRectangle rc;
714 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
715 rc.left = static_cast<XYPOSITION>(vs.textStart - leftTextOverlap);
716 rc.top = static_cast<XYPOSITION>((minLine - TopLineOfMain()) * vs.lineHeight);
717 if (rc.top < rcClientDrawing.top)
718 rc.top = rcClientDrawing.top;
719 // Extend to right of prepared area if any to prevent artifacts from caret line highlight
720 rc.right = rcClientDrawing.right;
721 rc.bottom = static_cast<XYPOSITION>((maxLine - TopLineOfMain() + 1) * vs.lineHeight);
723 return rc;
726 void Editor::InvalidateRange(int start, int end) {
727 RedrawRect(RectangleFromRange(Range(start, end)));
730 int Editor::CurrentPosition() const {
731 return sel.MainCaret();
734 bool Editor::SelectionEmpty() const {
735 return sel.Empty();
738 SelectionPosition Editor::SelectionStart() {
739 return sel.RangeMain().Start();
742 SelectionPosition Editor::SelectionEnd() {
743 return sel.RangeMain().End();
746 void Editor::SetRectangularRange() {
747 if (sel.IsRectangular()) {
748 int xAnchor = XFromPosition(sel.Rectangular().anchor);
749 int xCaret = XFromPosition(sel.Rectangular().caret);
750 if (sel.selType == Selection::selThin) {
751 xCaret = xAnchor;
753 int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
754 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
755 int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
756 for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
757 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
758 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
759 range.ClearVirtualSpace();
760 if (line == lineAnchorRect)
761 sel.SetSelection(range);
762 else
763 sel.AddSelectionWithoutTrim(range);
768 void Editor::ThinRectangularRange() {
769 if (sel.IsRectangular()) {
770 sel.selType = Selection::selThin;
771 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
772 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
773 } else {
774 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
776 SetRectangularRange();
780 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
781 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
782 invalidateWholeSelection = true;
784 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
785 // +1 for lastAffected ensures caret repainted
786 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
787 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
788 if (invalidateWholeSelection) {
789 for (size_t r=0; r<sel.Count(); r++) {
790 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
791 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
792 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
793 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
796 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
797 InvalidateRange(firstAffected, lastAffected);
800 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
801 currentPos_ = ClampPositionIntoDocument(currentPos_);
802 anchor_ = ClampPositionIntoDocument(anchor_);
803 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
804 /* For Line selection - ensure the anchor and caret are always
805 at the beginning and end of the region lines. */
806 if (sel.selType == Selection::selLines) {
807 if (currentPos_ > anchor_) {
808 anchor_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
809 currentPos_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
810 } else {
811 currentPos_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
812 anchor_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
815 SelectionRange rangeNew(currentPos_, anchor_);
816 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
817 InvalidateSelection(rangeNew);
819 sel.RangeMain() = rangeNew;
820 SetRectangularRange();
821 ClaimSelection();
823 if (highlightDelimiter.NeedsDrawing(currentLine)) {
824 RedrawSelMargin();
826 QueueIdleWork(WorkNeeded::workUpdateUI);
829 void Editor::SetSelection(int currentPos_, int anchor_) {
830 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
833 // Just move the caret on the main selection
834 void Editor::SetSelection(SelectionPosition currentPos_) {
835 currentPos_ = ClampPositionIntoDocument(currentPos_);
836 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
837 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
838 InvalidateSelection(SelectionRange(currentPos_));
840 if (sel.IsRectangular()) {
841 sel.Rectangular() =
842 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
843 SetRectangularRange();
844 } else {
845 sel.RangeMain() =
846 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
848 ClaimSelection();
850 if (highlightDelimiter.NeedsDrawing(currentLine)) {
851 RedrawSelMargin();
853 QueueIdleWork(WorkNeeded::workUpdateUI);
856 void Editor::SetSelection(int currentPos_) {
857 SetSelection(SelectionPosition(currentPos_));
860 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
861 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
862 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
863 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
864 InvalidateSelection(rangeNew);
866 sel.Clear();
867 sel.RangeMain() = rangeNew;
868 SetRectangularRange();
869 ClaimSelection();
871 if (highlightDelimiter.NeedsDrawing(currentLine)) {
872 RedrawSelMargin();
874 QueueIdleWork(WorkNeeded::workUpdateUI);
877 void Editor::SetEmptySelection(int currentPos_) {
878 SetEmptySelection(SelectionPosition(currentPos_));
881 bool Editor::RangeContainsProtected(int start, int end) const {
882 if (vs.ProtectionActive()) {
883 if (start > end) {
884 int t = start;
885 start = end;
886 end = t;
888 int mask = pdoc->stylingBitsMask;
889 for (int pos = start; pos < end; pos++) {
890 if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())
891 return true;
894 return false;
897 bool Editor::SelectionContainsProtected() {
898 for (size_t r=0; r<sel.Count(); r++) {
899 if (RangeContainsProtected(sel.Range(r).Start().Position(),
900 sel.Range(r).End().Position())) {
901 return true;
904 return false;
908 * Asks document to find a good position and then moves out of any invisible positions.
910 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
911 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
914 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
915 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
916 if (posMoved != pos.Position())
917 pos.SetPosition(posMoved);
918 if (vs.ProtectionActive()) {
919 int mask = pdoc->stylingBitsMask;
920 if (moveDir > 0) {
921 if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()) {
922 while ((pos.Position() < pdoc->Length()) &&
923 (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()))
924 pos.Add(1);
926 } else if (moveDir < 0) {
927 if (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()) {
928 while ((pos.Position() > 0) &&
929 (vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()))
930 pos.Add(-1);
934 return pos;
937 int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
938 bool simpleCaret = (sel.Count() == 1) && sel.Empty();
939 SelectionPosition spCaret = sel.Last();
941 int delta = newPos.Position() - sel.MainCaret();
942 newPos = ClampPositionIntoDocument(newPos);
943 newPos = MovePositionOutsideChar(newPos, delta);
944 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
945 // Can't turn into multiple selection so clear additional selections
946 InvalidateSelection(SelectionRange(newPos), true);
947 SelectionRange rangeMain = sel.RangeMain();
948 sel.SetSelection(rangeMain);
950 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
951 // Switching to rectangular
952 InvalidateSelection(sel.RangeMain(), false);
953 SelectionRange rangeMain = sel.RangeMain();
954 sel.Clear();
955 sel.Rectangular() = rangeMain;
957 if (selt != Selection::noSel) {
958 sel.selType = selt;
960 if (selt != Selection::noSel || sel.MoveExtends()) {
961 SetSelection(newPos);
962 } else {
963 SetEmptySelection(newPos);
965 ShowCaretAtCurrentPosition();
967 int currentLine = pdoc->LineFromPosition(newPos.Position());
968 if (ensureVisible) {
969 // In case in need of wrapping to ensure DisplayFromDoc works.
970 if (currentLine >= wrapPending.start)
971 WrapLines(wsAll);
972 XYScrollPosition newXY = XYScrollToMakeVisible(
973 SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret), xysDefault);
974 if (simpleCaret && (newXY.xOffset == xOffset)) {
975 // simple vertical scroll then invalidate
976 ScrollTo(newXY.topLine);
977 InvalidateSelection(SelectionRange(spCaret), true);
978 } else {
979 SetXYScroll(newXY);
983 if (highlightDelimiter.NeedsDrawing(currentLine)) {
984 RedrawSelMargin();
986 return 0;
989 int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
990 return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
993 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
994 pos = ClampPositionIntoDocument(pos);
995 pos = MovePositionOutsideChar(pos, moveDir);
996 int lineDoc = pdoc->LineFromPosition(pos.Position());
997 if (cs.GetVisible(lineDoc)) {
998 return pos;
999 } else {
1000 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1001 if (moveDir > 0) {
1002 // lineDisplay is already line before fold as lines in fold use display line of line after fold
1003 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
1004 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
1005 } else {
1006 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
1007 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
1012 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
1013 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
1016 Point Editor::PointMainCaret() {
1017 return LocationFromPosition(sel.Range(sel.Main()).caret);
1021 * Choose the x position that the caret will try to stick to
1022 * as it moves up and down.
1024 void Editor::SetLastXChosen() {
1025 Point pt = PointMainCaret();
1026 lastXChosen = static_cast<int>(pt.x) + xOffset;
1029 void Editor::ScrollTo(int line, bool moveThumb) {
1030 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
1031 if (topLineNew != topLine) {
1032 // Try to optimise small scrolls
1033 #ifndef UNDER_CE
1034 int linesToMove = topLine - topLineNew;
1035 bool performBlit = (abs(linesToMove) <= 10) && (paintState == notPainting);
1036 willRedrawAll = !performBlit;
1037 #endif
1038 SetTopLine(topLineNew);
1039 // Optimize by styling the view as this will invalidate any needed area
1040 // which could abort the initial paint if discovered later.
1041 StyleToPositionInView(PositionAfterArea(GetClientRectangle()));
1042 #ifndef UNDER_CE
1043 // Perform redraw rather than scroll if many lines would be redrawn anyway.
1044 if (performBlit) {
1045 ScrollText(linesToMove);
1046 } else {
1047 Redraw();
1049 willRedrawAll = false;
1050 #else
1051 Redraw();
1052 #endif
1053 if (moveThumb) {
1054 SetVerticalScrollPos();
1059 void Editor::ScrollText(int /* linesToMove */) {
1060 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
1061 Redraw();
1064 void Editor::HorizontalScrollTo(int xPos) {
1065 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
1066 if (xPos < 0)
1067 xPos = 0;
1068 if (!Wrapping() && (xOffset != xPos)) {
1069 xOffset = xPos;
1070 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1071 SetHorizontalScrollPos();
1072 RedrawRect(GetClientRectangle());
1076 void Editor::VerticalCentreCaret() {
1077 int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
1078 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1079 int newTop = lineDisplay - (LinesOnScreen() / 2);
1080 if (topLine != newTop) {
1081 SetTopLine(newTop > 0 ? newTop : 0);
1082 RedrawRect(GetClientRectangle());
1086 // Avoid 64 bit compiler warnings.
1087 // Scintilla does not support text buffers larger than 2**31
1088 static int istrlen(const char *s) {
1089 return static_cast<int>(s ? strlen(s) : 0);
1092 void Editor::MoveSelectedLines(int lineDelta) {
1094 // if selection doesn't start at the beginning of the line, set the new start
1095 int selectionStart = SelectionStart().Position();
1096 int startLine = pdoc->LineFromPosition(selectionStart);
1097 int beginningOfStartLine = pdoc->LineStart(startLine);
1098 selectionStart = beginningOfStartLine;
1100 // if selection doesn't end at the beginning of a line greater than that of the start,
1101 // then set it at the beginning of the next one
1102 int selectionEnd = SelectionEnd().Position();
1103 int endLine = pdoc->LineFromPosition(selectionEnd);
1104 int beginningOfEndLine = pdoc->LineStart(endLine);
1105 bool appendEol = false;
1106 if (selectionEnd > beginningOfEndLine
1107 || selectionStart == selectionEnd) {
1108 selectionEnd = pdoc->LineStart(endLine + 1);
1109 appendEol = (selectionEnd == pdoc->Length() && pdoc->LineFromPosition(selectionEnd) == endLine);
1112 // if there's nowhere for the selection to move
1113 // (i.e. at the beginning going up or at the end going down),
1114 // stop it right there!
1115 if ((selectionStart == 0 && lineDelta < 0)
1116 || (selectionEnd == pdoc->Length() && lineDelta > 0)
1117 || selectionStart == selectionEnd) {
1118 return;
1121 UndoGroup ug(pdoc);
1123 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
1124 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
1125 ClearSelection();
1126 selectionEnd = CurrentPosition();
1128 SetSelection(selectionStart, selectionEnd);
1130 SelectionText selectedText;
1131 CopySelectionRange(&selectedText);
1133 int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
1134 Point currentLocation = LocationFromPosition(CurrentPosition());
1135 int currentLine = LineFromLocation(currentLocation);
1137 if (appendEol)
1138 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
1139 ClearSelection();
1141 const char *eol = StringFromEOLMode(pdoc->eolMode);
1142 if (currentLine + lineDelta >= pdoc->LinesTotal())
1143 pdoc->InsertString(pdoc->Length(), eol, istrlen(eol));
1144 GoToLine(currentLine + lineDelta);
1146 selectionLength = pdoc->InsertString(CurrentPosition(), selectedText.Data(), selectionLength);
1147 if (appendEol) {
1148 const int lengthInserted = pdoc->InsertString(CurrentPosition() + selectionLength, eol, istrlen(eol));
1149 selectionLength += lengthInserted;
1151 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1154 void Editor::MoveSelectedLinesUp() {
1155 MoveSelectedLines(-1);
1158 void Editor::MoveSelectedLinesDown() {
1159 MoveSelectedLines(1);
1162 void Editor::MoveCaretInsideView(bool ensureVisible) {
1163 PRectangle rcClient = GetTextRectangle();
1164 Point pt = PointMainCaret();
1165 if (pt.y < rcClient.top) {
1166 MovePositionTo(SPositionFromLocation(
1167 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top)),
1168 false, false, UserVirtualSpace()),
1169 Selection::noSel, ensureVisible);
1170 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1171 int yOfLastLineFullyDisplayed = static_cast<int>(rcClient.top) + (LinesOnScreen() - 1) * vs.lineHeight;
1172 MovePositionTo(SPositionFromLocation(
1173 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top) + yOfLastLineFullyDisplayed),
1174 false, false, UserVirtualSpace()),
1175 Selection::noSel, ensureVisible);
1179 int Editor::DisplayFromPosition(int pos) {
1180 int lineDoc = pdoc->LineFromPosition(pos);
1181 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1182 AutoSurface surface(this);
1183 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
1184 if (surface && ll) {
1185 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
1186 unsigned int posLineStart = pdoc->LineStart(lineDoc);
1187 int posInLine = pos - posLineStart;
1188 lineDisplay--; // To make up for first increment ahead.
1189 for (int subLine = 0; subLine < ll->lines; subLine++) {
1190 if (posInLine >= ll->LineStart(subLine)) {
1191 lineDisplay++;
1195 return lineDisplay;
1199 * Ensure the caret is reasonably visible in context.
1201 Caret policy in SciTE
1203 If slop is set, we can define a slop value.
1204 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1205 This zone is defined as a number of pixels near the vertical margins,
1206 and as a number of lines near the horizontal margins.
1207 By keeping the caret away from the edges, it is seen within its context,
1208 so it is likely that the identifier that the caret is on can be completely seen,
1209 and that the current line is seen with some of the lines following it which are
1210 often dependent on that line.
1212 If strict is set, the policy is enforced... strictly.
1213 The caret is centred on the display if slop is not set,
1214 and cannot go in the UZ if slop is set.
1216 If jumps is set, the display is moved more energetically
1217 so the caret can move in the same direction longer before the policy is applied again.
1218 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1220 If even is not set, instead of having symmetrical UZs,
1221 the left and bottom UZs are extended up to right and top UZs respectively.
1222 This way, we favour the displaying of useful information: the begining of lines,
1223 where most code reside, and the lines after the caret, eg. the body of a function.
1225 | | | | |
1226 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1227 | | | | | visibility or going into the UZ) display is...
1228 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1229 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1230 0 | 0 | 0 | 1 | Yes | moved by one position
1231 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1232 0 | 0 | 1 | 1 | Yes | centred on the caret
1233 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1234 0 | 1 | - | 1 | No, caret is always centred | -
1235 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1236 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1237 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1238 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1239 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1240 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1241 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1244 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange &range, const XYScrollOptions options) {
1245 PRectangle rcClient = GetTextRectangle();
1246 Point pt = LocationFromPosition(range.caret);
1247 Point ptAnchor = LocationFromPosition(range.anchor);
1248 const Point ptOrigin = GetVisibleOriginInMain();
1249 pt.x += ptOrigin.x;
1250 pt.y += ptOrigin.y;
1251 ptAnchor.x += ptOrigin.x;
1252 ptAnchor.y += ptOrigin.y;
1253 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1255 XYScrollPosition newXY(xOffset, topLine);
1256 if (rcClient.Empty()) {
1257 return newXY;
1260 // Vertical positioning
1261 if ((options & xysVertical) && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1262 const int lineCaret = DisplayFromPosition(range.caret.Position());
1263 const int linesOnScreen = LinesOnScreen();
1264 const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1265 const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
1266 const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
1267 const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
1268 const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
1270 // It should be possible to scroll the window to show the caret,
1271 // but this fails to remove the caret on GTK+
1272 if (bSlop) { // A margin is defined
1273 int yMoveT, yMoveB;
1274 if (bStrict) {
1275 int yMarginT, yMarginB;
1276 if (!(options & xysUseMargin)) {
1277 // In drag mode, avoid moves
1278 // otherwise, a double click will select several lines.
1279 yMarginT = yMarginB = 0;
1280 } else {
1281 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1282 // a maximum of slightly less than half the heigth of the text area.
1283 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1284 if (bEven) {
1285 yMarginB = yMarginT;
1286 } else {
1287 yMarginB = linesOnScreen - yMarginT - 1;
1290 yMoveT = yMarginT;
1291 if (bEven) {
1292 if (bJump) {
1293 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1295 yMoveB = yMoveT;
1296 } else {
1297 yMoveB = linesOnScreen - yMoveT - 1;
1299 if (lineCaret < topLine + yMarginT) {
1300 // Caret goes too high
1301 newXY.topLine = lineCaret - yMoveT;
1302 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1303 // Caret goes too low
1304 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1306 } else { // Not strict
1307 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1308 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1309 if (bEven) {
1310 yMoveB = yMoveT;
1311 } else {
1312 yMoveB = linesOnScreen - yMoveT - 1;
1314 if (lineCaret < topLine) {
1315 // Caret goes too high
1316 newXY.topLine = lineCaret - yMoveT;
1317 } else if (lineCaret > topLine + linesOnScreen - 1) {
1318 // Caret goes too low
1319 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1322 } else { // No slop
1323 if (!bStrict && !bJump) {
1324 // Minimal move
1325 if (lineCaret < topLine) {
1326 // Caret goes too high
1327 newXY.topLine = lineCaret;
1328 } else if (lineCaret > topLine + linesOnScreen - 1) {
1329 // Caret goes too low
1330 if (bEven) {
1331 newXY.topLine = lineCaret - linesOnScreen + 1;
1332 } else {
1333 newXY.topLine = lineCaret;
1336 } else { // Strict or going out of display
1337 if (bEven) {
1338 // Always center caret
1339 newXY.topLine = lineCaret - halfScreen;
1340 } else {
1341 // Always put caret on top of display
1342 newXY.topLine = lineCaret;
1346 if (!(range.caret == range.anchor)) {
1347 const int lineAnchor = DisplayFromPosition(range.anchor.Position());
1348 if (lineAnchor < lineCaret) {
1349 // Shift up to show anchor or as much of range as possible
1350 newXY.topLine = std::min(newXY.topLine, lineAnchor);
1351 newXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());
1352 } else {
1353 // Shift down to show anchor or as much of range as possible
1354 newXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());
1355 newXY.topLine = std::min(newXY.topLine, lineCaret);
1358 newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
1361 // Horizontal positioning
1362 if ((options & xysHorizontal) && !Wrapping()) {
1363 const int halfScreen = Platform::Maximum(static_cast<int>(rcClient.Width()) - 4, 4) / 2;
1364 const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
1365 const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
1366 const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
1367 const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
1369 if (bSlop) { // A margin is defined
1370 int xMoveL, xMoveR;
1371 if (bStrict) {
1372 int xMarginL, xMarginR;
1373 if (!(options & xysUseMargin)) {
1374 // In drag mode, avoid moves unless very near of the margin
1375 // otherwise, a simple click will select text.
1376 xMarginL = xMarginR = 2;
1377 } else {
1378 // xMargin must equal to caretXSlop, with a minimum of 2 and
1379 // a maximum of slightly less than half the width of the text area.
1380 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1381 if (bEven) {
1382 xMarginL = xMarginR;
1383 } else {
1384 xMarginL = static_cast<int>(rcClient.Width()) - xMarginR - 4;
1387 if (bJump && bEven) {
1388 // Jump is used only in even mode
1389 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1390 } else {
1391 xMoveL = xMoveR = 0; // Not used, avoid a warning
1393 if (pt.x < rcClient.left + xMarginL) {
1394 // Caret is on the left of the display
1395 if (bJump && bEven) {
1396 newXY.xOffset -= xMoveL;
1397 } else {
1398 // Move just enough to allow to display the caret
1399 newXY.xOffset -= static_cast<int>((rcClient.left + xMarginL) - pt.x);
1401 } else if (pt.x >= rcClient.right - xMarginR) {
1402 // Caret is on the right of the display
1403 if (bJump && bEven) {
1404 newXY.xOffset += xMoveR;
1405 } else {
1406 // Move just enough to allow to display the caret
1407 newXY.xOffset += static_cast<int>(pt.x - (rcClient.right - xMarginR) + 1);
1410 } else { // Not strict
1411 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1412 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1413 if (bEven) {
1414 xMoveL = xMoveR;
1415 } else {
1416 xMoveL = static_cast<int>(rcClient.Width()) - xMoveR - 4;
1418 if (pt.x < rcClient.left) {
1419 // Caret is on the left of the display
1420 newXY.xOffset -= xMoveL;
1421 } else if (pt.x >= rcClient.right) {
1422 // Caret is on the right of the display
1423 newXY.xOffset += xMoveR;
1426 } else { // No slop
1427 if (bStrict ||
1428 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1429 // Strict or going out of display
1430 if (bEven) {
1431 // Center caret
1432 newXY.xOffset += static_cast<int>(pt.x - rcClient.left - halfScreen);
1433 } else {
1434 // Put caret on right
1435 newXY.xOffset += static_cast<int>(pt.x - rcClient.right + 1);
1437 } else {
1438 // Move just enough to allow to display the caret
1439 if (pt.x < rcClient.left) {
1440 // Caret is on the left of the display
1441 if (bEven) {
1442 newXY.xOffset -= static_cast<int>(rcClient.left - pt.x);
1443 } else {
1444 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1446 } else if (pt.x >= rcClient.right) {
1447 // Caret is on the right of the display
1448 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1452 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1453 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1454 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 2;
1455 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1456 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 2;
1457 if (vs.caretStyle == CARETSTYLE_BLOCK) {
1458 // Ensure we can see a good portion of the block caret
1459 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1462 if (!(range.caret == range.anchor)) {
1463 if (ptAnchor.x < pt.x) {
1464 // Shift to left to show anchor or as much of range as possible
1465 int maxOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.left) - 1;
1466 int minOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 1;
1467 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1468 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1469 } else {
1470 // Shift to right to show anchor or as much of range as possible
1471 int minOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.right) + 1;
1472 int maxOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 1;
1473 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1474 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1477 if (newXY.xOffset < 0) {
1478 newXY.xOffset = 0;
1482 return newXY;
1485 void Editor::SetXYScroll(XYScrollPosition newXY) {
1486 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1487 if (newXY.topLine != topLine) {
1488 SetTopLine(newXY.topLine);
1489 SetVerticalScrollPos();
1491 if (newXY.xOffset != xOffset) {
1492 xOffset = newXY.xOffset;
1493 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1494 if (newXY.xOffset > 0) {
1495 PRectangle rcText = GetTextRectangle();
1496 if (horizontalScrollBarVisible &&
1497 rcText.Width() + xOffset > scrollWidth) {
1498 scrollWidth = xOffset + static_cast<int>(rcText.Width());
1499 SetScrollBars();
1502 SetHorizontalScrollPos();
1504 Redraw();
1505 UpdateSystemCaret();
1509 void Editor::ScrollRange(SelectionRange range) {
1510 SetXYScroll(XYScrollToMakeVisible(range, xysDefault));
1513 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1514 SetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret),
1515 static_cast<XYScrollOptions>((useMargin?xysUseMargin:0)|(vert?xysVertical:0)|(horiz?xysHorizontal:0))));
1518 void Editor::ShowCaretAtCurrentPosition() {
1519 if (hasFocus) {
1520 caret.active = true;
1521 caret.on = true;
1522 SetTicking(true);
1523 } else {
1524 caret.active = false;
1525 caret.on = false;
1527 InvalidateCaret();
1530 void Editor::DropCaret() {
1531 caret.active = false;
1532 InvalidateCaret();
1535 void Editor::CaretSetPeriod(int period) {
1536 if (caret.period != period) {
1537 caret.period = period;
1538 caret.on = true;
1539 InvalidateCaret();
1543 void Editor::InvalidateCaret() {
1544 if (posDrag.IsValid()) {
1545 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1546 } else {
1547 for (size_t r=0; r<sel.Count(); r++) {
1548 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1551 UpdateSystemCaret();
1554 void Editor::UpdateSystemCaret() {
1557 bool Editor::Wrapping() const {
1558 return vs.wrapState != eWrapNone;
1561 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1562 //Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd);
1563 if (wrapPending.AddRange(docLineStart, docLineEnd)) {
1564 llc.Invalidate(LineLayout::llPositions);
1566 // Wrap lines during idle.
1567 if (Wrapping() && wrapPending.NeedsWrap()) {
1568 SetIdle(true);
1572 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1573 AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
1574 int linesWrapped = 1;
1575 if (ll) {
1576 LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
1577 linesWrapped = ll->lines;
1579 return cs.SetHeight(lineToWrap, linesWrapped +
1580 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1583 // Perform wrapping for a subset of the lines needing wrapping.
1584 // wsAll: wrap all lines which need wrapping in this single call
1585 // wsVisible: wrap currently visible lines
1586 // wsIdle: wrap one page + 100 lines
1587 // Return true if wrapping occurred.
1588 bool Editor::WrapLines(enum wrapScope ws) {
1589 int goodTopLine = topLine;
1590 bool wrapOccurred = false;
1591 if (!Wrapping()) {
1592 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1593 wrapWidth = LineLayout::wrapWidthInfinite;
1594 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1595 cs.SetHeight(lineDoc, 1 +
1596 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1598 wrapOccurred = true;
1600 wrapPending.Reset();
1602 } else if (wrapPending.NeedsWrap()) {
1603 wrapPending.start = std::min(wrapPending.start, pdoc->LinesTotal());
1604 if (!SetIdle(true)) {
1605 // Idle processing not supported so full wrap required.
1606 ws = wsAll;
1608 // Decide where to start wrapping
1609 int lineToWrap = wrapPending.start;
1610 int lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal());
1611 const int lineDocTop = cs.DocFromDisplay(topLine);
1612 const int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1613 if (ws == wsVisible) {
1614 lineToWrap = Platform::Clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal());
1615 // Priority wrap to just after visible area.
1616 // Since wrapping could reduce display lines, treat each
1617 // as taking only one display line.
1618 lineToWrapEnd = lineDocTop;
1619 int lines = LinesOnScreen() + 1;
1620 while ((lineToWrapEnd < cs.LinesInDoc()) && (lines>0)) {
1621 if (cs.GetVisible(lineToWrapEnd))
1622 lines--;
1623 lineToWrapEnd++;
1625 // .. and if the paint window is outside pending wraps
1626 if ((lineToWrap > wrapPending.end) || (lineToWrapEnd < wrapPending.start)) {
1627 // Currently visible text does not need wrapping
1628 return false;
1630 } else if (ws == wsIdle) {
1631 lineToWrapEnd = lineToWrap + LinesOnScreen() + 100;
1633 const int lineEndNeedWrap = std::min(wrapPending.end, pdoc->LinesTotal());
1634 lineToWrapEnd = std::min(lineToWrapEnd, lineEndNeedWrap);
1636 // Ensure all lines being wrapped are styled.
1637 pdoc->EnsureStyledTo(pdoc->LineStart(lineToWrapEnd));
1639 if (lineToWrap < lineToWrapEnd) {
1641 PRectangle rcTextArea = GetClientRectangle();
1642 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1643 rcTextArea.right -= vs.rightMarginWidth;
1644 wrapWidth = static_cast<int>(rcTextArea.Width());
1645 RefreshStyleData();
1646 AutoSurface surface(this);
1647 if (surface) {
1648 //Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
1650 while (lineToWrap < lineToWrapEnd) {
1651 if (WrapOneLine(surface, lineToWrap)) {
1652 wrapOccurred = true;
1654 wrapPending.Wrapped(lineToWrap);
1655 lineToWrap++;
1658 goodTopLine = cs.DisplayFromDoc(lineDocTop) + std::min(subLineTop, cs.GetHeight(lineDocTop)-1);
1662 // If wrapping is done, bring it to resting position
1663 if (wrapPending.start >= lineEndNeedWrap) {
1664 wrapPending.Reset();
1668 if (wrapOccurred) {
1669 SetScrollBars();
1670 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1671 SetVerticalScrollPos();
1674 return wrapOccurred;
1677 void Editor::LinesJoin() {
1678 if (!RangeContainsProtected(targetStart, targetEnd)) {
1679 UndoGroup ug(pdoc);
1680 bool prevNonWS = true;
1681 for (int pos = targetStart; pos < targetEnd; pos++) {
1682 if (pdoc->IsPositionInLineEnd(pos)) {
1683 targetEnd -= pdoc->LenChar(pos);
1684 pdoc->DelChar(pos);
1685 if (prevNonWS) {
1686 // Ensure at least one space separating previous lines
1687 const int lengthInserted = pdoc->InsertString(pos, " ", 1);
1688 targetEnd += lengthInserted;
1690 } else {
1691 prevNonWS = pdoc->CharAt(pos) != ' ';
1697 const char *Editor::StringFromEOLMode(int eolMode) {
1698 if (eolMode == SC_EOL_CRLF) {
1699 return "\r\n";
1700 } else if (eolMode == SC_EOL_CR) {
1701 return "\r";
1702 } else {
1703 return "\n";
1707 void Editor::LinesSplit(int pixelWidth) {
1708 if (!RangeContainsProtected(targetStart, targetEnd)) {
1709 if (pixelWidth == 0) {
1710 PRectangle rcText = GetTextRectangle();
1711 pixelWidth = static_cast<int>(rcText.Width());
1713 int lineStart = pdoc->LineFromPosition(targetStart);
1714 int lineEnd = pdoc->LineFromPosition(targetEnd);
1715 const char *eol = StringFromEOLMode(pdoc->eolMode);
1716 UndoGroup ug(pdoc);
1717 for (int line = lineStart; line <= lineEnd; line++) {
1718 AutoSurface surface(this);
1719 AutoLineLayout ll(llc, RetrieveLineLayout(line));
1720 if (surface && ll) {
1721 unsigned int posLineStart = pdoc->LineStart(line);
1722 LayoutLine(line, surface, vs, ll, pixelWidth);
1723 int lengthInsertedTotal = 0;
1724 for (int subLine = 1; subLine < ll->lines; subLine++) {
1725 const int lengthInserted = pdoc->InsertString(
1726 static_cast<int>(posLineStart + lengthInsertedTotal +
1727 ll->LineStart(subLine)),
1728 eol, istrlen(eol));
1729 targetEnd += lengthInserted;
1730 lengthInsertedTotal += lengthInserted;
1733 lineEnd = pdoc->LineFromPosition(targetEnd);
1738 int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) const {
1739 if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
1740 return markerDefault;
1741 return markerCheck;
1744 bool ValidStyledText(ViewStyle &vs, size_t styleOffset, const StyledText &st) {
1745 if (st.multipleStyles) {
1746 for (size_t iStyle=0; iStyle<st.length; iStyle++) {
1747 if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
1748 return false;
1750 } else {
1751 if (!vs.ValidStyle(styleOffset + st.style))
1752 return false;
1754 return true;
1757 static int WidthStyledText(Surface *surface, ViewStyle &vs, int styleOffset,
1758 const char *text, const unsigned char *styles, size_t len) {
1759 int width = 0;
1760 size_t start = 0;
1761 while (start < len) {
1762 size_t style = styles[start];
1763 size_t endSegment = start;
1764 while ((endSegment+1 < len) && (static_cast<size_t>(styles[endSegment+1]) == style))
1765 endSegment++;
1766 width += static_cast<int>(surface->WidthText(vs.styles[style + styleOffset].font, text + start,
1767 static_cast<int>(endSegment - start + 1)));
1768 start = endSegment + 1;
1770 return width;
1773 static int WidestLineWidth(Surface *surface, ViewStyle &vs, int styleOffset, const StyledText &st) {
1774 int widthMax = 0;
1775 size_t start = 0;
1776 while (start < st.length) {
1777 size_t lenLine = st.LineLength(start);
1778 int widthSubLine;
1779 if (st.multipleStyles) {
1780 widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
1781 } else {
1782 widthSubLine = static_cast<int>(surface->WidthText(vs.styles[styleOffset + st.style].font,
1783 st.text + start, static_cast<int>(lenLine)));
1785 if (widthSubLine > widthMax)
1786 widthMax = widthSubLine;
1787 start += lenLine + 1;
1789 return widthMax;
1792 void DrawStyledText(Surface *surface, ViewStyle &vs, int styleOffset, PRectangle rcText, int ascent,
1793 const StyledText &st, size_t start, size_t length) {
1795 if (st.multipleStyles) {
1796 int x = static_cast<int>(rcText.left);
1797 size_t i = 0;
1798 while (i < length) {
1799 size_t end = i;
1800 int style = st.styles[i + start];
1801 while (end < length-1 && st.styles[start+end+1] == style)
1802 end++;
1803 style += styleOffset;
1804 int width = static_cast<int>(surface->WidthText(vs.styles[style].font,
1805 st.text + start + i, static_cast<int>(end - i + 1)));
1806 PRectangle rcSegment = rcText;
1807 rcSegment.left = static_cast<XYPOSITION>(x);
1808 rcSegment.right = static_cast<XYPOSITION>(x + width + 1);
1809 surface->DrawTextNoClip(rcSegment, vs.styles[style].font,
1810 static_cast<XYPOSITION>(ascent), st.text + start + i,
1811 static_cast<int>(end - i + 1),
1812 vs.styles[style].fore,
1813 vs.styles[style].back);
1814 x += width;
1815 i = end + 1;
1817 } else {
1818 size_t style = st.style + styleOffset;
1819 surface->DrawTextNoClip(rcText, vs.styles[style].font,
1820 rcText.top + vs.maxAscent, st.text + start,
1821 static_cast<int>(length),
1822 vs.styles[style].fore,
1823 vs.styles[style].back);
1827 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1828 if (vs.fixedColumnWidth == 0)
1829 return;
1831 AllocateGraphics();
1832 RefreshStyleData();
1833 RefreshPixMaps(surfWindow);
1835 PRectangle rcMargin = GetClientRectangle();
1836 Point ptOrigin = GetVisibleOriginInMain();
1837 rcMargin.Move(0, -ptOrigin.y);
1838 rcMargin.left = 0;
1839 rcMargin.right = static_cast<XYPOSITION>(vs.fixedColumnWidth);
1841 if (!rc.Intersects(rcMargin))
1842 return;
1844 Surface *surface;
1845 if (bufferedDraw) {
1846 surface = pixmapSelMargin;
1847 } else {
1848 surface = surfWindow;
1851 // Clip vertically to paint area to avoid drawing line numbers
1852 if (rcMargin.bottom > rc.bottom)
1853 rcMargin.bottom = rc.bottom;
1854 if (rcMargin.top < rc.top)
1855 rcMargin.top = rc.top;
1857 PRectangle rcSelMargin = rcMargin;
1858 rcSelMargin.right = rcMargin.left;
1859 if (rcSelMargin.bottom < rc.bottom)
1860 rcSelMargin.bottom = rc.bottom;
1862 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
1863 if (vs.ms[margin].width > 0) {
1865 rcSelMargin.left = rcSelMargin.right;
1866 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
1868 if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
1869 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1870 // Required because of special way brush is created for selection margin
1871 // Ensure patterns line up when scrolling with separate margin view
1872 // by choosing correctly aligned variant.
1873 bool invertPhase = static_cast<int>(ptOrigin.y) & 1;
1874 surface->FillRectangle(rcSelMargin,
1875 invertPhase ? *pixmapSelPattern : *pixmapSelPatternOffset1);
1876 } else {
1877 ColourDesired colour;
1878 switch (vs.ms[margin].style) {
1879 case SC_MARGIN_BACK:
1880 colour = vs.styles[STYLE_DEFAULT].back;
1881 break;
1882 case SC_MARGIN_FORE:
1883 colour = vs.styles[STYLE_DEFAULT].fore;
1884 break;
1885 default:
1886 colour = vs.styles[STYLE_LINENUMBER].back;
1887 break;
1889 surface->FillRectangle(rcSelMargin, colour);
1891 } else {
1892 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back);
1895 const int lineStartPaint = static_cast<int>(rcMargin.top + ptOrigin.y) / vs.lineHeight;
1896 int visibleLine = TopLineOfMain() + lineStartPaint;
1897 int yposScreen = lineStartPaint * vs.lineHeight - static_cast<int>(ptOrigin.y);
1898 // Work out whether the top line is whitespace located after a
1899 // lessening of fold level which implies a 'fold tail' but which should not
1900 // be displayed until the last of a sequence of whitespace.
1901 bool needWhiteClosure = false;
1902 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1903 int level = pdoc->GetLevel(cs.DocFromDisplay(visibleLine));
1904 if (level & SC_FOLDLEVELWHITEFLAG) {
1905 int lineBack = cs.DocFromDisplay(visibleLine);
1906 int levelPrev = level;
1907 while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
1908 lineBack--;
1909 levelPrev = pdoc->GetLevel(lineBack);
1911 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
1912 if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK))
1913 needWhiteClosure = true;
1916 if (highlightDelimiter.isEnabled) {
1917 int lastLine = cs.DocFromDisplay(topLine + LinesOnScreen()) + 1;
1918 pdoc->GetHighlightDelimiters(highlightDelimiter, pdoc->LineFromPosition(CurrentPosition()), lastLine);
1922 // Old code does not know about new markers needed to distinguish all cases
1923 const int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
1924 SC_MARKNUM_FOLDEROPEN);
1925 const int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
1926 SC_MARKNUM_FOLDER);
1928 while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rc.bottom) {
1930 PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed());
1931 const int lineDoc = cs.DocFromDisplay(visibleLine);
1932 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
1933 const bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
1934 const bool lastSubLine = visibleLine == cs.DisplayLastFromDoc(lineDoc);
1936 int marks = pdoc->GetMark(lineDoc);
1937 if (!firstSubLine)
1938 marks = 0;
1940 bool headWithTail = false;
1942 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1943 // Decide which fold indicator should be displayed
1944 const int level = pdoc->GetLevel(lineDoc);
1945 const int levelNext = pdoc->GetLevel(lineDoc + 1);
1946 const int levelNum = level & SC_FOLDLEVELNUMBERMASK;
1947 const int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
1948 if (level & SC_FOLDLEVELHEADERFLAG) {
1949 if (firstSubLine) {
1950 if (levelNum < levelNextNum) {
1951 if (cs.GetExpanded(lineDoc)) {
1952 if (levelNum == SC_FOLDLEVELBASE)
1953 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
1954 else
1955 marks |= 1 << folderOpenMid;
1956 } else {
1957 if (levelNum == SC_FOLDLEVELBASE)
1958 marks |= 1 << SC_MARKNUM_FOLDER;
1959 else
1960 marks |= 1 << folderEnd;
1962 } else if (levelNum > SC_FOLDLEVELBASE) {
1963 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1965 } else {
1966 if (levelNum < levelNextNum) {
1967 if (cs.GetExpanded(lineDoc)) {
1968 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1969 } else if (levelNum > SC_FOLDLEVELBASE) {
1970 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1972 } else if (levelNum > SC_FOLDLEVELBASE) {
1973 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1976 needWhiteClosure = false;
1977 const int firstFollowupLine = cs.DocFromDisplay(cs.DisplayFromDoc(lineDoc + 1));
1978 const int firstFollowupLineLevel = pdoc->GetLevel(firstFollowupLine);
1979 const int secondFollowupLineLevelNum = pdoc->GetLevel(firstFollowupLine + 1) & SC_FOLDLEVELNUMBERMASK;
1980 if (!cs.GetExpanded(lineDoc)) {
1981 if ((firstFollowupLineLevel & SC_FOLDLEVELWHITEFLAG) &&
1982 (levelNum > secondFollowupLineLevelNum))
1983 needWhiteClosure = true;
1985 if (highlightDelimiter.IsFoldBlockHighlighted(firstFollowupLine))
1986 headWithTail = true;
1988 } else if (level & SC_FOLDLEVELWHITEFLAG) {
1989 if (needWhiteClosure) {
1990 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1991 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1992 } else if (levelNextNum > SC_FOLDLEVELBASE) {
1993 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1994 needWhiteClosure = false;
1995 } else {
1996 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1997 needWhiteClosure = false;
1999 } else if (levelNum > SC_FOLDLEVELBASE) {
2000 if (levelNextNum < levelNum) {
2001 if (levelNextNum > SC_FOLDLEVELBASE) {
2002 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
2003 } else {
2004 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
2006 } else {
2007 marks |= 1 << SC_MARKNUM_FOLDERSUB;
2010 } else if (levelNum > SC_FOLDLEVELBASE) {
2011 if (levelNextNum < levelNum) {
2012 needWhiteClosure = false;
2013 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
2014 marks |= 1 << SC_MARKNUM_FOLDERSUB;
2015 needWhiteClosure = true;
2016 } else if (lastSubLine) {
2017 if (levelNextNum > SC_FOLDLEVELBASE) {
2018 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
2019 } else {
2020 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
2022 } else {
2023 marks |= 1 << SC_MARKNUM_FOLDERSUB;
2025 } else {
2026 marks |= 1 << SC_MARKNUM_FOLDERSUB;
2031 marks &= vs.ms[margin].mask;
2033 PRectangle rcMarker = rcSelMargin;
2034 rcMarker.top = static_cast<XYPOSITION>(yposScreen);
2035 rcMarker.bottom = static_cast<XYPOSITION>(yposScreen + vs.lineHeight);
2036 if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
2037 if (firstSubLine) {
2038 char number[100] = "";
2039 if (lineDoc >= 0)
2040 sprintf(number, "%d", lineDoc + 1);
2041 if (foldFlags & (SC_FOLDFLAG_LEVELNUMBERS | SC_FOLDFLAG_LINESTATE)) {
2042 if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
2043 int lev = pdoc->GetLevel(lineDoc);
2044 sprintf(number, "%c%c %03X %03X",
2045 (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
2046 (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
2047 lev & SC_FOLDLEVELNUMBERMASK,
2048 lev >> 16
2050 } else {
2051 int state = pdoc->GetLineState(lineDoc);
2052 sprintf(number, "%0X", state);
2055 PRectangle rcNumber = rcMarker;
2056 // Right justify
2057 XYPOSITION width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
2058 XYPOSITION xpos = rcNumber.right - width - vs.marginNumberPadding;
2059 rcNumber.left = xpos;
2060 surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font,
2061 rcNumber.top + vs.maxAscent, number, istrlen(number),
2062 vs.styles[STYLE_LINENUMBER].fore,
2063 vs.styles[STYLE_LINENUMBER].back);
2064 } else if (vs.wrapVisualFlags & SC_WRAPVISUALFLAG_MARGIN) {
2065 PRectangle rcWrapMarker = rcMarker;
2066 rcWrapMarker.right -= 3;
2067 rcWrapMarker.left = rcWrapMarker.right - vs.styles[STYLE_LINENUMBER].aveCharWidth;
2068 DrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
2070 } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
2071 if (firstSubLine) {
2072 const StyledText stMargin = pdoc->MarginStyledText(lineDoc);
2073 if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
2074 surface->FillRectangle(rcMarker,
2075 vs.styles[stMargin.StyleAt(0)+vs.marginStyleOffset].back);
2076 if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
2077 int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
2078 rcMarker.left = rcMarker.right - width - 3;
2080 DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, static_cast<int>(rcMarker.top) + vs.maxAscent,
2081 stMargin, 0, stMargin.length);
2086 if (marks) {
2087 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
2088 if (marks & 1) {
2089 LineMarker::typeOfFold tFold = LineMarker::undefined;
2090 if ((vs.ms[margin].mask & SC_MASK_FOLDERS) && highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) {
2091 if (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) {
2092 tFold = LineMarker::body;
2093 } else if (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) {
2094 if (firstSubLine) {
2095 tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head;
2096 } else {
2097 if (cs.GetExpanded(lineDoc) || headWithTail) {
2098 tFold = LineMarker::body;
2099 } else {
2100 tFold = LineMarker::undefined;
2103 } else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) {
2104 tFold = LineMarker::tail;
2107 vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font, tFold, vs.ms[margin].style);
2109 marks >>= 1;
2113 visibleLine++;
2114 yposScreen += vs.lineHeight;
2119 PRectangle rcBlankMargin = rcMargin;
2120 rcBlankMargin.left = rcSelMargin.right;
2121 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back);
2123 if (bufferedDraw) {
2124 surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *pixmapSelMargin);
2128 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
2129 int ydiff = static_cast<int>(rcTab.bottom - rcTab.top) / 2;
2130 int xhead = static_cast<int>(rcTab.right) - 1 - ydiff;
2131 if (xhead <= rcTab.left) {
2132 ydiff -= static_cast<int>(rcTab.left) - xhead - 1;
2133 xhead = static_cast<int>(rcTab.left) - 1;
2135 if ((rcTab.left + 2) < (rcTab.right - 1))
2136 surface->MoveTo(static_cast<int>(rcTab.left) + 2, ymid);
2137 else
2138 surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
2139 surface->LineTo(static_cast<int>(rcTab.right) - 1, ymid);
2140 surface->LineTo(xhead, ymid - ydiff);
2141 surface->MoveTo(static_cast<int>(rcTab.right) - 1, ymid);
2142 surface->LineTo(xhead, ymid + ydiff);
2145 LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
2146 int posLineStart = pdoc->LineStart(lineNumber);
2147 int posLineEnd = pdoc->LineStart(lineNumber + 1);
2148 PLATFORM_ASSERT(posLineEnd >= posLineStart);
2149 int lineCaret = pdoc->LineFromPosition(sel.MainCaret());
2150 return llc.Retrieve(lineNumber, lineCaret,
2151 posLineEnd - posLineStart, pdoc->GetStyleClock(),
2152 LinesOnScreen() + 1, pdoc->LinesTotal());
2156 * Fill in the LineLayout data for the given line.
2157 * Copy the given @a line and its styles from the document into local arrays.
2158 * Also determine the x position at which each character starts.
2160 void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) {
2161 //void LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width, Document *pdoc, PositionCache &posCache, SpecialRepresentations &reprs) {
2162 if (!ll)
2163 return;
2165 PLATFORM_ASSERT(line < pdoc->LinesTotal());
2166 PLATFORM_ASSERT(ll->chars != NULL);
2167 int posLineStart = pdoc->LineStart(line);
2168 int posLineEnd = pdoc->LineStart(line + 1);
2169 // If the line is very long, limit the treatment to a length that should fit in the viewport
2170 if (posLineEnd > (posLineStart + ll->maxLineLength)) {
2171 posLineEnd = posLineStart + ll->maxLineLength;
2173 if (ll->validity == LineLayout::llCheckTextAndStyle) {
2174 int lineLength = posLineEnd - posLineStart;
2175 if (!vstyle.viewEOL) {
2176 lineLength = pdoc->LineEnd(line) - posLineStart;
2178 if (lineLength == ll->numCharsInLine) {
2179 // See if chars, styles, indicators, are all the same
2180 bool allSame = true;
2181 const int styleMask = pdoc->stylingBitsMask;
2182 // Check base line layout
2183 char styleByte = 0;
2184 int numCharsInLine = 0;
2185 while (numCharsInLine < lineLength) {
2186 int charInDoc = numCharsInLine + posLineStart;
2187 char chDoc = pdoc->CharAt(charInDoc);
2188 styleByte = pdoc->StyleAt(charInDoc);
2189 allSame = allSame &&
2190 (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte & styleMask));
2191 allSame = allSame &&
2192 (ll->indicators[numCharsInLine] == static_cast<char>(styleByte & ~styleMask));
2193 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
2194 allSame = allSame &&
2195 (ll->chars[numCharsInLine] == chDoc);
2196 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
2197 allSame = allSame &&
2198 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
2199 else // Style::caseUpper
2200 allSame = allSame &&
2201 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
2202 numCharsInLine++;
2204 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
2205 if (allSame) {
2206 ll->validity = LineLayout::llPositions;
2207 } else {
2208 ll->validity = LineLayout::llInvalid;
2210 } else {
2211 ll->validity = LineLayout::llInvalid;
2214 if (ll->validity == LineLayout::llInvalid) {
2215 ll->widthLine = LineLayout::wrapWidthInfinite;
2216 ll->lines = 1;
2217 if (vstyle.edgeState == EDGE_BACKGROUND) {
2218 ll->edgeColumn = pdoc->FindColumn(line, vstyle.theEdge);
2219 if (ll->edgeColumn >= posLineStart) {
2220 ll->edgeColumn -= posLineStart;
2222 } else {
2223 ll->edgeColumn = -1;
2226 const int styleMask = pdoc->stylingBitsMask;
2227 ll->styleBitsSet = 0;
2228 // Fill base line layout
2229 const int lineLength = posLineEnd - posLineStart;
2230 pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
2231 pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
2232 int numCharsBeforeEOL = pdoc->LineEnd(line) - posLineStart;
2233 const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
2234 for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
2235 const unsigned char styleByte = ll->styles[styleInLine];
2236 ll->styleBitsSet |= styleByte;
2237 ll->styles[styleInLine] = styleByte & styleMask;
2238 ll->indicators[styleInLine] = static_cast<char>(styleByte & ~styleMask);
2240 const unsigned char styleByteLast = ((lineLength > 0) ? ll->styles[lineLength-1] : 0) & styleMask;
2241 if (vstyle.someStylesForceCase) {
2242 for (int charInLine = 0; charInLine<lineLength; charInLine++) {
2243 char chDoc = ll->chars[charInLine];
2244 if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
2245 ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
2246 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
2247 ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
2250 ll->xHighlightGuide = 0;
2251 // Extra element at the end of the line to hold end x position and act as
2252 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
2253 ll->styles[numCharsInLine] = styleByteLast; // For eolFilled
2254 ll->indicators[numCharsInLine] = 0;
2256 // Layout the line, determining the position of each character,
2257 // with an extra element at the end for the end of the line.
2258 ll->positions[0] = 0;
2259 bool lastSegItalics = false;
2261 BreakFinder bfLayout(ll, 0, numCharsInLine, posLineStart, 0, false, pdoc, &reprs);
2262 while (bfLayout.More()) {
2264 const TextSegment ts = bfLayout.Next();
2266 std::fill(&ll->positions[ts.start+1], &ll->positions[ts.end()+1], 0.0f);
2267 if (vstyle.styles[ll->styles[ts.start]].visible) {
2268 if (ts.representation) {
2269 XYPOSITION representationWidth = vstyle.controlCharWidth;
2270 if (ll->chars[ts.start] == '\t') {
2271 // Tab is a special case of representation, taking a variable amount of space
2272 representationWidth =
2273 ((static_cast<int>((ll->positions[ts.start] + 2) / vstyle.tabWidth) + 1) * vstyle.tabWidth) - ll->positions[ts.start];
2274 } else {
2275 if (representationWidth <= 0.0) {
2276 XYPOSITION positionsRepr[256]; // Should expand when needed
2277 posCache.MeasureWidths(surface, vstyle, STYLE_CONTROLCHAR, ts.representation->stringRep.c_str(),
2278 static_cast<unsigned int>(ts.representation->stringRep.length()), positionsRepr, pdoc);
2279 representationWidth = positionsRepr[ts.representation->stringRep.length()-1] + vstyle.ctrlCharPadding;
2282 for (int ii=0; ii < ts.length; ii++)
2283 ll->positions[ts.start + 1 + ii] = representationWidth;
2284 } else {
2285 if ((ts.length == 1) && (' ' == ll->chars[ts.start])) {
2286 // Over half the segments are single characters and of these about half are space characters.
2287 ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth;
2288 } else {
2289 posCache.MeasureWidths(surface, vstyle, ll->styles[ts.start], ll->chars + ts.start,
2290 ts.length, ll->positions + ts.start + 1, pdoc);
2293 lastSegItalics = (!ts.representation) && ((ll->chars[ts.end()-1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic);
2296 for (int posToIncrease = ts.start+1; posToIncrease <= ts.end(); posToIncrease++) {
2297 ll->positions[posToIncrease] += ll->positions[ts.start];
2301 // Small hack to make lines that end with italics not cut off the edge of the last character
2302 if (lastSegItalics) {
2303 ll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset;
2305 ll->numCharsInLine = numCharsInLine;
2306 ll->numCharsBeforeEOL = numCharsBeforeEOL;
2307 ll->validity = LineLayout::llPositions;
2309 // Hard to cope when too narrow, so just assume there is space
2310 if (width < 20) {
2311 width = 20;
2313 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
2314 ll->widthLine = width;
2315 if (width == LineLayout::wrapWidthInfinite) {
2316 ll->lines = 1;
2317 } else if (width > ll->positions[ll->numCharsInLine]) {
2318 // Simple common case where line does not need wrapping.
2319 ll->lines = 1;
2320 } else {
2321 if (vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2322 width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
2324 XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
2325 if (vstyle.wrapIndentMode == SC_WRAPINDENT_INDENT) {
2326 wrapAddIndent = pdoc->IndentSize() * vstyle.spaceWidth;
2327 } else if (vstyle.wrapIndentMode == SC_WRAPINDENT_FIXED) {
2328 wrapAddIndent = vstyle.wrapVisualStartIndent * vstyle.aveCharWidth;
2330 ll->wrapIndent = wrapAddIndent;
2331 if (vstyle.wrapIndentMode != SC_WRAPINDENT_FIXED)
2332 for (int i = 0; i < ll->numCharsInLine; i++) {
2333 if (!IsSpaceOrTab(ll->chars[i])) {
2334 ll->wrapIndent += ll->positions[i]; // Add line indent
2335 break;
2338 // Check for text width minimum
2339 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
2340 ll->wrapIndent = wrapAddIndent;
2341 // Check for wrapIndent minimum
2342 if ((vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
2343 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
2344 ll->lines = 0;
2345 // Calculate line start positions based upon width.
2346 int lastGoodBreak = 0;
2347 int lastLineStart = 0;
2348 XYACCUMULATOR startOffset = 0;
2349 int p = 0;
2350 while (p < ll->numCharsInLine) {
2351 if ((ll->positions[p + 1] - startOffset) >= width) {
2352 if (lastGoodBreak == lastLineStart) {
2353 // Try moving to start of last character
2354 if (p > 0) {
2355 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2356 - posLineStart;
2358 if (lastGoodBreak == lastLineStart) {
2359 // Ensure at least one character on line.
2360 lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
2361 - posLineStart;
2364 lastLineStart = lastGoodBreak;
2365 ll->lines++;
2366 ll->SetLineStart(ll->lines, lastGoodBreak);
2367 startOffset = ll->positions[lastGoodBreak];
2368 // take into account the space for start wrap mark and indent
2369 startOffset -= ll->wrapIndent;
2370 p = lastGoodBreak + 1;
2371 continue;
2373 if (p > 0) {
2374 if (vstyle.wrapState == eWrapChar) {
2375 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2376 - posLineStart;
2377 p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
2378 continue;
2379 } else if ((vstyle.wrapState == eWrapWord) && (ll->styles[p] != ll->styles[p - 1])) {
2380 lastGoodBreak = p;
2381 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
2382 lastGoodBreak = p;
2385 p++;
2387 ll->lines++;
2389 ll->validity = LineLayout::llLines;
2393 ColourDesired Editor::SelectionBackground(ViewStyle &vsDraw, bool main) const {
2394 return main ?
2395 (primarySelection ? vsDraw.selColours.back : vsDraw.selBackground2) :
2396 vsDraw.selAdditionalBackground;
2399 ColourDesired Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
2400 ColourDesired background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) const {
2401 if (inSelection == 1) {
2402 if (vsDraw.selColours.back.isSet && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
2403 return SelectionBackground(vsDraw, true);
2405 } else if (inSelection == 2) {
2406 if (vsDraw.selColours.back.isSet && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
2407 return SelectionBackground(vsDraw, false);
2409 } else {
2410 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
2411 (i >= ll->edgeColumn) &&
2412 (i < ll->numCharsBeforeEOL))
2413 return vsDraw.edgecolour;
2414 if (inHotspot && vsDraw.hotspotColours.back.isSet)
2415 return vsDraw.hotspotColours.back;
2417 if (overrideBackground && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
2418 return background;
2419 } else {
2420 return vsDraw.styles[styleMain].back;
2424 void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
2425 Point from = Point::FromInts(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
2426 PRectangle rcCopyArea = PRectangle::FromInts(start + 1, static_cast<int>(rcSegment.top), start + 2, static_cast<int>(rcSegment.bottom));
2427 surface->Copy(rcCopyArea, from,
2428 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
2431 void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
2432 bool isEndMarker, ColourDesired wrapColour) {
2433 surface->PenColour(wrapColour);
2435 enum { xa = 1 }; // gap before start
2436 int w = static_cast<int>(rcPlace.right - rcPlace.left) - xa - 1;
2438 bool xStraight = isEndMarker; // x-mirrored symbol for start marker
2440 int x0 = static_cast<int>(xStraight ? rcPlace.left : rcPlace.right - 1);
2441 int y0 = static_cast<int>(rcPlace.top);
2443 int dy = static_cast<int>(rcPlace.bottom - rcPlace.top) / 5;
2444 int y = static_cast<int>(rcPlace.bottom - rcPlace.top) / 2 + dy;
2446 struct Relative {
2447 Surface *surface;
2448 int xBase;
2449 int xDir;
2450 int yBase;
2451 int yDir;
2452 void MoveTo(int xRelative, int yRelative) {
2453 surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2455 void LineTo(int xRelative, int yRelative) {
2456 surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2459 Relative rel = {surface, x0, xStraight ? 1 : -1, y0, 1};
2461 // arrow head
2462 rel.MoveTo(xa, y);
2463 rel.LineTo(xa + 2*w / 3, y - dy);
2464 rel.MoveTo(xa, y);
2465 rel.LineTo(xa + 2*w / 3, y + dy);
2467 // arrow body
2468 rel.MoveTo(xa, y);
2469 rel.LineTo(xa + w, y);
2470 rel.LineTo(xa + w, y - 2 * dy);
2471 rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
2472 y - 2 * dy);
2475 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
2476 if (alpha != SC_ALPHA_NOALPHA) {
2477 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
2481 void DrawTextBlob(Surface *surface, ViewStyle &vsDraw, PRectangle rcSegment,
2482 const char *s, ColourDesired textBack, ColourDesired textFore, bool twoPhaseDraw) {
2483 if (!twoPhaseDraw) {
2484 surface->FillRectangle(rcSegment, textBack);
2486 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2487 int normalCharHeight = static_cast<int>(surface->Ascent(ctrlCharsFont) -
2488 surface->InternalLeading(ctrlCharsFont));
2489 PRectangle rcCChar = rcSegment;
2490 rcCChar.left = rcCChar.left + 1;
2491 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
2492 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
2493 PRectangle rcCentral = rcCChar;
2494 rcCentral.top++;
2495 rcCentral.bottom--;
2496 surface->FillRectangle(rcCentral, textFore);
2497 PRectangle rcChar = rcCChar;
2498 rcChar.left++;
2499 rcChar.right--;
2500 surface->DrawTextClipped(rcChar, ctrlCharsFont,
2501 rcSegment.top + vsDraw.maxAscent, s, istrlen(s),
2502 textBack, textFore);
2505 void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
2506 int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
2507 bool overrideBackground, ColourDesired background,
2508 bool drawWrapMarkEnd, ColourDesired wrapColour) {
2510 const int posLineStart = pdoc->LineStart(line);
2511 const int styleMask = pdoc->stylingBitsMask;
2512 PRectangle rcSegment = rcLine;
2514 const bool lastSubLine = subLine == (ll->lines - 1);
2515 XYPOSITION virtualSpace = 0;
2516 if (lastSubLine) {
2517 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
2518 virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * spaceWidth;
2520 XYPOSITION xEol = static_cast<XYPOSITION>(ll->positions[lineEnd] - subLineStart);
2522 // Fill the virtual space and show selections within it
2523 if (virtualSpace) {
2524 rcSegment.left = xEol + xStart;
2525 rcSegment.right = xEol + xStart + virtualSpace;
2526 surface->FillRectangle(rcSegment, overrideBackground ? background : vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2527 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
2528 SelectionSegment virtualSpaceRange(SelectionPosition(pdoc->LineEnd(line)), SelectionPosition(pdoc->LineEnd(line), sel.VirtualSpaceFor(pdoc->LineEnd(line))));
2529 for (size_t r=0; r<sel.Count(); r++) {
2530 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2531 if (alpha == SC_ALPHA_NOALPHA) {
2532 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
2533 if (!portion.Empty()) {
2534 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
2535 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
2536 static_cast<XYPOSITION>(subLineStart) + portion.start.VirtualSpace() * spaceWidth;
2537 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
2538 static_cast<XYPOSITION>(subLineStart) + portion.end.VirtualSpace() * spaceWidth;
2539 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
2540 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
2541 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == sel.Main()));
2548 int eolInSelection = 0;
2549 int alpha = SC_ALPHA_NOALPHA;
2550 if (!hideSelection) {
2551 int posAfterLineEnd = pdoc->LineStart(line + 1);
2552 eolInSelection = (subLine == (ll->lines - 1)) ? sel.InSelectionForEOL(posAfterLineEnd) : 0;
2553 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2556 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
2557 XYPOSITION blobsWidth = 0;
2558 if (lastSubLine) {
2559 for (int eolPos=ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
2560 rcSegment.left = xStart + ll->positions[eolPos] - static_cast<XYPOSITION>(subLineStart) + virtualSpace;
2561 rcSegment.right = xStart + ll->positions[eolPos + 1] - static_cast<XYPOSITION>(subLineStart) + virtualSpace;
2562 blobsWidth += rcSegment.Width();
2563 char hexits[4];
2564 const char *ctrlChar;
2565 unsigned char chEOL = ll->chars[eolPos];
2566 int styleMain = ll->styles[eolPos];
2567 ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, eolInSelection, false, styleMain, eolPos, ll);
2568 if (UTF8IsAscii(chEOL)) {
2569 ctrlChar = ControlCharacterString(chEOL);
2570 } else {
2571 Representation *repr = reprs.RepresentationFromCharacter(ll->chars + eolPos, ll->numCharsInLine - eolPos);
2572 if (repr) {
2573 ctrlChar = repr->stringRep.c_str();
2574 eolPos = ll->numCharsInLine;
2575 } else {
2576 sprintf(hexits, "x%2X", chEOL);
2577 ctrlChar = hexits;
2580 ColourDesired textFore = vsDraw.styles[styleMain].fore;
2581 if (eolInSelection && vsDraw.selColours.fore.isSet) {
2582 textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
2584 if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1)) {
2585 if (alpha == SC_ALPHA_NOALPHA) {
2586 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2587 } else {
2588 surface->FillRectangle(rcSegment, textBack);
2590 } else {
2591 surface->FillRectangle(rcSegment, textBack);
2593 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
2594 if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2595 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2600 // Draw the eol-is-selected rectangle
2601 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
2602 rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
2604 if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2605 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2606 } else {
2607 if (overrideBackground) {
2608 surface->FillRectangle(rcSegment, background);
2609 } else if (line < pdoc->LinesTotal() - 1) {
2610 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2611 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2612 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2613 } else {
2614 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
2616 if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2617 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2621 // Fill the remainder of the line
2622 rcSegment.left = rcSegment.right;
2623 if (rcSegment.left < rcLine.left)
2624 rcSegment.left = rcLine.left;
2625 rcSegment.right = rcLine.right;
2627 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2628 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2629 } else {
2630 if (overrideBackground) {
2631 surface->FillRectangle(rcSegment, background);
2632 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2633 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2634 } else {
2635 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
2637 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2638 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2642 if (drawWrapMarkEnd) {
2643 PRectangle rcPlace = rcSegment;
2645 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
2646 rcPlace.left = xEol + xStart + virtualSpace;
2647 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2648 } else {
2649 // rcLine is clipped to text area
2650 rcPlace.right = rcLine.right;
2651 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2653 DrawWrapMarker(surface, rcPlace, true, wrapColour);
2657 void Editor::DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, ViewStyle &vsDraw,
2658 int xStart, PRectangle rcLine, LineLayout *ll, int subLine) {
2659 const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
2660 PRectangle rcIndic(
2661 ll->positions[startPos] + xStart - subLineStart,
2662 rcLine.top + vsDraw.maxAscent,
2663 ll->positions[endPos] + xStart - subLineStart,
2664 rcLine.top + vsDraw.maxAscent + 3);
2665 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine);
2668 void Editor::DrawIndicators(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2669 PRectangle rcLine, LineLayout *ll, int subLine, int lineEnd, bool under) {
2670 // Draw decorators
2671 const int posLineStart = pdoc->LineStart(line);
2672 const int lineStart = ll->LineStart(subLine);
2673 const int posLineEnd = posLineStart + lineEnd;
2675 if (!under) {
2676 // Draw indicators
2677 // foreach indicator...
2678 for (int indicnum = 0, mask = 1 << pdoc->stylingBits; mask < 0x100; indicnum++) {
2679 if (!(mask & ll->styleBitsSet)) {
2680 mask <<= 1;
2681 continue;
2683 int startPos = -1;
2684 // foreach style pos in line...
2685 for (int indicPos = lineStart; indicPos <= lineEnd; indicPos++) {
2686 // look for starts...
2687 if (startPos < 0) {
2688 // NOT in indicator run, looking for START
2689 if (indicPos < lineEnd && (ll->indicators[indicPos] & mask))
2690 startPos = indicPos;
2692 // ... or ends
2693 if (startPos >= 0) {
2694 // IN indicator run, looking for END
2695 if (indicPos >= lineEnd || !(ll->indicators[indicPos] & mask)) {
2696 // AT end of indicator run, DRAW it!
2697 DrawIndicator(indicnum, startPos, indicPos, surface, vsDraw, xStart, rcLine, ll, subLine);
2698 // RESET control var
2699 startPos = -1;
2703 mask <<= 1;
2707 for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
2708 if (under == vsDraw.indicators[deco->indicator].under) {
2709 int startPos = posLineStart + lineStart;
2710 if (!deco->rs.ValueAt(startPos)) {
2711 startPos = deco->rs.EndRun(startPos);
2713 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
2714 int endPos = deco->rs.EndRun(startPos);
2715 if (endPos > posLineEnd)
2716 endPos = posLineEnd;
2717 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
2718 surface, vsDraw, xStart, rcLine, ll, subLine);
2719 startPos = endPos;
2720 if (!deco->rs.ValueAt(startPos)) {
2721 startPos = deco->rs.EndRun(startPos);
2727 // Use indicators to highlight matching braces
2728 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
2729 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
2730 int braceIndicator = (bracesMatchStyle == STYLE_BRACELIGHT) ? vs.braceHighlightIndicator : vs.braceBadLightIndicator;
2731 if (under == vsDraw.indicators[braceIndicator].under) {
2732 Range rangeLine(posLineStart + lineStart, posLineEnd);
2733 if (rangeLine.ContainsCharacter(braces[0])) {
2734 int braceOffset = braces[0] - posLineStart;
2735 if (braceOffset < ll->numCharsInLine) {
2736 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2739 if (rangeLine.ContainsCharacter(braces[1])) {
2740 int braceOffset = braces[1] - posLineStart;
2741 if (braceOffset < ll->numCharsInLine) {
2742 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2749 void Editor::DrawAnnotation(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2750 PRectangle rcLine, LineLayout *ll, int subLine) {
2751 int indent = static_cast<int>(pdoc->GetLineIndentation(line) * vsDraw.spaceWidth);
2752 PRectangle rcSegment = rcLine;
2753 int annotationLine = subLine - ll->lines;
2754 const StyledText stAnnotation = pdoc->AnnotationStyledText(line);
2755 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
2756 surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
2757 rcSegment.left = static_cast<XYPOSITION>(xStart);
2758 if (trackLineWidth || (vs.annotationVisible == ANNOTATION_BOXED)) {
2759 // Only care about calculating width if tracking or need to draw box
2760 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
2761 if (vs.annotationVisible == ANNOTATION_BOXED) {
2762 widthAnnotation += static_cast<int>(vsDraw.spaceWidth * 2); // Margins
2764 if (widthAnnotation > lineWidthMaxSeen)
2765 lineWidthMaxSeen = widthAnnotation;
2766 if (vs.annotationVisible == ANNOTATION_BOXED) {
2767 rcSegment.left = static_cast<XYPOSITION>(xStart + indent);
2768 rcSegment.right = rcSegment.left + widthAnnotation;
2771 const int annotationLines = pdoc->AnnotationLines(line);
2772 size_t start = 0;
2773 size_t lengthAnnotation = stAnnotation.LineLength(start);
2774 int lineInAnnotation = 0;
2775 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
2776 start += lengthAnnotation + 1;
2777 lengthAnnotation = stAnnotation.LineLength(start);
2778 lineInAnnotation++;
2780 PRectangle rcText = rcSegment;
2781 if (vs.annotationVisible == ANNOTATION_BOXED) {
2782 surface->FillRectangle(rcText,
2783 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
2784 rcText.left += vsDraw.spaceWidth;
2786 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText, static_cast<int>(rcText.top + vsDraw.maxAscent),
2787 stAnnotation, start, lengthAnnotation);
2788 if (vs.annotationVisible == ANNOTATION_BOXED) {
2789 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
2790 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
2791 surface->LineTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom));
2792 surface->MoveTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
2793 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom));
2794 if (subLine == ll->lines) {
2795 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.top));
2796 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.top));
2798 if (subLine == ll->lines+annotationLines-1) {
2799 surface->MoveTo(static_cast<int>(rcSegment.left), static_cast<int>(rcSegment.bottom - 1));
2800 surface->LineTo(static_cast<int>(rcSegment.right), static_cast<int>(rcSegment.bottom - 1));
2806 void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
2807 PRectangle rcLine, LineLayout *ll, int subLine) {
2809 if (subLine >= ll->lines) {
2810 DrawAnnotation(surface, vsDraw, line, xStart, rcLine, ll, subLine);
2811 return; // No further drawing
2814 PRectangle rcSegment = rcLine;
2816 // Using one font for all control characters so it can be controlled independently to ensure
2817 // the box goes around the characters tightly. Seems to be no way to work out what height
2818 // is taken by an individual character - internal leading gives varying results.
2819 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2821 // See if something overrides the line background color: Either if caret is on the line
2822 // and background color is set for that, or if a marker is defined that forces its background
2823 // color onto the line, or if a marker is defined but has no selection margin in which to
2824 // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
2825 // with the earlier taking precedence. When multiple markers cause background override,
2826 // the color for the highest numbered one is used.
2827 bool overrideBackground = false;
2828 ColourDesired background;
2829 if ((caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && (vsDraw.caretLineAlpha == SC_ALPHA_NOALPHA) && ll->containsCaret) {
2830 overrideBackground = true;
2831 background = vsDraw.caretLineBackground;
2833 if (!overrideBackground) {
2834 int marks = pdoc->GetMark(line);
2835 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
2836 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) &&
2837 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2838 background = vsDraw.markers[markBit].back;
2839 overrideBackground = true;
2841 marks >>= 1;
2844 if (!overrideBackground) {
2845 if (vsDraw.maskInLine) {
2846 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
2847 if (marksMasked) {
2848 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
2849 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY) &&
2850 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2851 overrideBackground = true;
2852 background = vsDraw.markers[markBit].back;
2854 marksMasked >>= 1;
2860 const bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
2861 (!overrideBackground) && (vsDraw.whitespaceColours.back.isSet);
2863 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2864 const XYPOSITION indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
2865 const XYPOSITION epsilon = 0.0001f; // A small nudge to avoid floating point precision issues
2867 const int posLineStart = pdoc->LineStart(line);
2869 const int startseg = ll->LineStart(subLine);
2870 const XYACCUMULATOR subLineStart = ll->positions[startseg];
2871 int lineStart = 0;
2872 int lineEnd = 0;
2873 if (subLine < ll->lines) {
2874 lineStart = ll->LineStart(subLine);
2875 lineEnd = ll->LineStart(subLine + 1);
2876 if (subLine == ll->lines - 1) {
2877 lineEnd = ll->numCharsBeforeEOL;
2881 const ColourDesired wrapColour = vsDraw.WrapColour();
2883 bool drawWrapMarkEnd = false;
2885 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2886 if (subLine + 1 < ll->lines) {
2887 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
2891 if (ll->wrapIndent != 0) {
2893 bool continuedWrapLine = false;
2894 if (subLine < ll->lines) {
2895 continuedWrapLine = ll->LineStart(subLine) != 0;
2898 if (continuedWrapLine) {
2899 // draw continuation rect
2900 PRectangle rcPlace = rcSegment;
2902 rcPlace.left = ll->positions[startseg] + xStart - static_cast<XYPOSITION>(subLineStart);
2903 rcPlace.right = rcPlace.left + ll->wrapIndent;
2905 // default bgnd here..
2906 surface->FillRectangle(rcSegment, overrideBackground ? background :
2907 vsDraw.styles[STYLE_DEFAULT].back);
2909 // main line style would be below but this would be inconsistent with end markers
2910 // also would possibly not be the style at wrap point
2911 //int styleMain = ll->styles[lineStart];
2912 //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back);
2914 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
2916 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
2917 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2918 else
2919 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2921 DrawWrapMarker(surface, rcPlace, false, wrapColour);
2924 xStart += static_cast<int>(ll->wrapIndent);
2928 const bool selBackDrawn = vsDraw.selColours.back.isSet &&
2929 ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA));
2931 // Does not take margin into account but not significant
2932 const int xStartVisible = static_cast<int>(subLineStart) - xStart;
2934 ll->psel = &sel;
2936 if (twoPhaseDraw) {
2937 BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, xStartVisible, selBackDrawn, pdoc, &reprs);
2939 // Background drawing loop
2940 while (bfBack.More()) {
2942 const TextSegment ts = bfBack.Next();
2943 const int i = ts.end() - 1;
2944 const int iDoc = i + posLineStart;
2946 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
2947 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
2948 // Only try to draw if really visible - enhances performance by not calling environment to
2949 // draw strings that are completely past the right side of the window.
2950 if (rcSegment.Intersects(rcLine)) {
2951 // Clip to line rectangle, since may have a huge position which will not work with some platforms
2952 if (rcSegment.left < rcLine.left)
2953 rcSegment.left = rcLine.left;
2954 if (rcSegment.right > rcLine.right)
2955 rcSegment.right = rcLine.right;
2957 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2958 const bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2959 ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, inSelection,
2960 inHotspot, ll->styles[i], i, ll);
2961 if (ts.representation) {
2962 if (ll->chars[i] == '\t') {
2963 // Tab display
2964 if (drawWhitespaceBackground &&
2965 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2966 textBack = vsDraw.whitespaceColours.back;
2967 } else {
2968 // Blob display
2969 inIndentation = false;
2971 surface->FillRectangle(rcSegment, textBack);
2972 } else {
2973 // Normal text display
2974 surface->FillRectangle(rcSegment, textBack);
2975 if (vsDraw.viewWhitespace != wsInvisible ||
2976 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
2977 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
2978 if (ll->chars[cpos + ts.start] == ' ') {
2979 if (drawWhitespaceBackground &&
2980 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
2981 PRectangle rcSpace(
2982 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
2983 rcSegment.top,
2984 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
2985 rcSegment.bottom);
2986 surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back);
2988 } else {
2989 inIndentation = false;
2994 } else if (rcSegment.left > rcLine.right) {
2995 break;
2999 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
3000 xStart, subLine, subLineStart, overrideBackground, background,
3001 drawWrapMarkEnd, wrapColour);
3004 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, true);
3006 if (vsDraw.edgeState == EDGE_LINE) {
3007 int edgeX = static_cast<int>(vsDraw.theEdge * vsDraw.spaceWidth);
3008 rcSegment.left = static_cast<XYPOSITION>(edgeX + xStart);
3009 if ((ll->wrapIndent != 0) && (lineStart != 0))
3010 rcSegment.left -= ll->wrapIndent;
3011 rcSegment.right = rcSegment.left + 1;
3012 surface->FillRectangle(rcSegment, vsDraw.edgecolour);
3015 // Draw underline mark as part of background if not transparent
3016 int marks = pdoc->GetMark(line);
3017 int markBit;
3018 for (markBit = 0; (markBit < 32) && marks; markBit++) {
3019 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
3020 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
3021 PRectangle rcUnderline = rcLine;
3022 rcUnderline.top = rcUnderline.bottom - 2;
3023 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
3025 marks >>= 1;
3028 inIndentation = subLine == 0; // Do not handle indentation except on first subline.
3029 // Foreground drawing loop
3030 BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, xStartVisible,
3031 ((!twoPhaseDraw && selBackDrawn) || vsDraw.selColours.fore.isSet), pdoc, &reprs);
3033 while (bfFore.More()) {
3035 const TextSegment ts = bfFore.Next();
3036 const int i = ts.end() - 1;
3037 const int iDoc = i + posLineStart;
3039 rcSegment.left = ll->positions[ts.start] + xStart - static_cast<XYPOSITION>(subLineStart);
3040 rcSegment.right = ll->positions[ts.end()] + xStart - static_cast<XYPOSITION>(subLineStart);
3041 // Only try to draw if really visible - enhances performance by not calling environment to
3042 // draw strings that are completely past the right side of the window.
3043 if (rcSegment.Intersects(rcLine)) {
3044 int styleMain = ll->styles[i];
3045 ColourDesired textFore = vsDraw.styles[styleMain].fore;
3046 Font &textFont = vsDraw.styles[styleMain].font;
3047 //hotspot foreground
3048 if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) {
3049 if (vsDraw.hotspotColours.fore.isSet)
3050 textFore = vsDraw.hotspotColours.fore;
3052 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
3053 if (inSelection && (vsDraw.selColours.fore.isSet)) {
3054 textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
3056 const bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
3057 ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
3058 if (ts.representation) {
3059 if (ll->chars[i] == '\t') {
3060 // Tab display
3061 if (!twoPhaseDraw) {
3062 if (drawWhitespaceBackground &&
3063 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
3064 textBack = vsDraw.whitespaceColours.back;
3065 surface->FillRectangle(rcSegment, textBack);
3067 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
3068 for (int indentCount = static_cast<int>((ll->positions[i] + epsilon) / indentWidth);
3069 indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
3070 indentCount++) {
3071 if (indentCount > 0) {
3072 int xIndent = static_cast<int>(indentCount * indentWidth);
3073 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3074 (ll->xHighlightGuide == xIndent));
3078 if (vsDraw.viewWhitespace != wsInvisible) {
3079 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3080 if (vsDraw.whitespaceColours.fore.isSet)
3081 textFore = vsDraw.whitespaceColours.fore;
3082 surface->PenColour(textFore);
3083 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
3084 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
3085 DrawTabArrow(surface, rcTab, static_cast<int>(rcSegment.top + vsDraw.lineHeight / 2));
3088 } else {
3089 inIndentation = false;
3090 if (vsDraw.controlCharSymbol >= 32) {
3091 char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\0' };
3092 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
3093 rcSegment.top + vsDraw.maxAscent,
3094 cc, 1, textBack, textFore);
3095 } else {
3096 DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(), textBack, textFore, twoPhaseDraw);
3099 } else {
3100 // Normal text display
3101 if (vsDraw.styles[styleMain].visible) {
3102 if (twoPhaseDraw) {
3103 surface->DrawTextTransparent(rcSegment, textFont,
3104 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
3105 i - ts.start + 1, textFore);
3106 } else {
3107 surface->DrawTextNoClip(rcSegment, textFont,
3108 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
3109 i - ts.start + 1, textFore, textBack);
3112 if (vsDraw.viewWhitespace != wsInvisible ||
3113 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
3114 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
3115 if (ll->chars[cpos + ts.start] == ' ') {
3116 if (vsDraw.viewWhitespace != wsInvisible) {
3117 if (vsDraw.whitespaceColours.fore.isSet)
3118 textFore = vsDraw.whitespaceColours.fore;
3119 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3120 XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2;
3121 if (!twoPhaseDraw && drawWhitespaceBackground &&
3122 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
3123 textBack = vsDraw.whitespaceColours.back;
3124 PRectangle rcSpace(
3125 ll->positions[cpos + ts.start] + xStart - static_cast<XYPOSITION>(subLineStart),
3126 rcSegment.top,
3127 ll->positions[cpos + ts.start + 1] + xStart - static_cast<XYPOSITION>(subLineStart),
3128 rcSegment.bottom);
3129 surface->FillRectangle(rcSpace, textBack);
3131 PRectangle rcDot(xmid + xStart - static_cast<XYPOSITION>(subLineStart),
3132 rcSegment.top + vsDraw.lineHeight / 2, 0.0f, 0.0f);
3133 rcDot.right = rcDot.left + vs.whitespaceSize;
3134 rcDot.bottom = rcDot.top + vs.whitespaceSize;
3135 surface->FillRectangle(rcDot, textFore);
3138 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
3139 for (int indentCount = static_cast<int>((ll->positions[cpos + ts.start] + epsilon) / indentWidth);
3140 indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth;
3141 indentCount++) {
3142 if (indentCount > 0) {
3143 int xIndent = static_cast<int>(indentCount * indentWidth);
3144 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3145 (ll->xHighlightGuide == xIndent));
3149 } else {
3150 inIndentation = false;
3155 if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd) {
3156 PRectangle rcUL = rcSegment;
3157 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3158 rcUL.bottom = rcUL.top + 1;
3159 if (vsDraw.hotspotColours.fore.isSet)
3160 surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore);
3161 else
3162 surface->FillRectangle(rcUL, textFore);
3163 } else if (vsDraw.styles[styleMain].underline) {
3164 PRectangle rcUL = rcSegment;
3165 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3166 rcUL.bottom = rcUL.top + 1;
3167 surface->FillRectangle(rcUL, textFore);
3169 } else if (rcSegment.left > rcLine.right) {
3170 break;
3173 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
3174 && (subLine == 0)) {
3175 int indentSpace = pdoc->GetLineIndentation(line);
3176 int xStartText = static_cast<int>(ll->positions[pdoc->GetLineIndentPosition(line) - posLineStart]);
3178 // Find the most recent line with some text
3180 int lineLastWithText = line;
3181 while (lineLastWithText > Platform::Maximum(line-20, 0) && pdoc->IsWhiteLine(lineLastWithText)) {
3182 lineLastWithText--;
3184 if (lineLastWithText < line) {
3185 xStartText = 100000; // Don't limit to visible indentation on empty line
3186 // This line is empty, so use indentation of last line with text
3187 int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText);
3188 int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
3189 if (isFoldHeader) {
3190 // Level is one more level than parent
3191 indentLastWithText += pdoc->IndentSize();
3193 if (vsDraw.viewIndentationGuides == ivLookForward) {
3194 // In viLookForward mode, previous line only used if it is a fold header
3195 if (isFoldHeader) {
3196 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3198 } else { // viLookBoth
3199 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3203 int lineNextWithText = line;
3204 while (lineNextWithText < Platform::Minimum(line+20, pdoc->LinesTotal()) && pdoc->IsWhiteLine(lineNextWithText)) {
3205 lineNextWithText++;
3207 if (lineNextWithText > line) {
3208 xStartText = 100000; // Don't limit to visible indentation on empty line
3209 // This line is empty, so use indentation of first next line with text
3210 indentSpace = Platform::Maximum(indentSpace,
3211 pdoc->GetLineIndentation(lineNextWithText));
3214 for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) {
3215 int xIndent = static_cast<int>(indentPos * vsDraw.spaceWidth);
3216 if (xIndent < xStartText) {
3217 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3218 (ll->xHighlightGuide == xIndent));
3223 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, false);
3225 // End of the drawing of the current line
3226 if (!twoPhaseDraw) {
3227 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
3228 xStart, subLine, subLineStart, overrideBackground, background,
3229 drawWrapMarkEnd, wrapColour);
3231 if (!hideSelection && ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA))) {
3232 // For each selection draw
3233 int virtualSpaces = 0;
3234 if (subLine == (ll->lines - 1)) {
3235 virtualSpaces = sel.VirtualSpaceFor(pdoc->LineEnd(line));
3237 SelectionPosition posStart(posLineStart + lineStart);
3238 SelectionPosition posEnd(posLineStart + lineEnd, virtualSpaces);
3239 SelectionSegment virtualSpaceRange(posStart, posEnd);
3240 for (size_t r=0; r<sel.Count(); r++) {
3241 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
3242 if (alpha != SC_ALPHA_NOALPHA) {
3243 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
3244 if (!portion.Empty()) {
3245 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
3246 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] -
3247 static_cast<XYPOSITION>(subLineStart) + portion.start.VirtualSpace() * spaceWidth;
3248 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] -
3249 static_cast<XYPOSITION>(subLineStart) + portion.end.VirtualSpace() * spaceWidth;
3250 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3251 if ((portion.start.Position() - posLineStart) == lineStart && sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
3252 rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
3254 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
3255 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
3256 if (rcSegment.right > rcLine.left)
3257 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == sel.Main()), alpha);
3263 // Draw any translucent whole line states
3264 rcSegment = rcLine;
3265 if ((caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) {
3266 SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
3268 marks = pdoc->GetMark(line);
3269 for (markBit = 0; (markBit < 32) && marks; markBit++) {
3270 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
3271 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3272 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
3273 PRectangle rcUnderline = rcSegment;
3274 rcUnderline.top = rcUnderline.bottom - 2;
3275 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3277 marks >>= 1;
3279 if (vsDraw.maskInLine) {
3280 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
3281 if (marksMasked) {
3282 for (markBit = 0; (markBit < 32) && marksMasked; markBit++) {
3283 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
3284 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3286 marksMasked >>= 1;
3292 void Editor::DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine,
3293 int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) {
3295 int lineStart = ll->LineStart(subLine);
3296 int posBefore = posCaret;
3297 int posAfter = MovePositionOutsideChar(posCaret + 1, 1);
3298 int numCharsToDraw = posAfter - posCaret;
3300 // Work out where the starting and ending offsets are. We need to
3301 // see if the previous character shares horizontal space, such as a
3302 // glyph / combining character. If so we'll need to draw that too.
3303 int offsetFirstChar = offset;
3304 int offsetLastChar = offset + (posAfter - posCaret);
3305 while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
3306 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
3307 // The char does not share horizontal space
3308 break;
3310 // Char shares horizontal space, update the numChars to draw
3311 // Update posBefore to point to the prev char
3312 posBefore = MovePositionOutsideChar(posBefore - 1, -1);
3313 numCharsToDraw = posAfter - posBefore;
3314 offsetFirstChar = offset - (posCaret - posBefore);
3317 // See if the next character shares horizontal space, if so we'll
3318 // need to draw that too.
3319 if (offsetFirstChar < 0)
3320 offsetFirstChar = 0;
3321 numCharsToDraw = offsetLastChar - offsetFirstChar;
3322 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
3323 // Update posAfter to point to the 2nd next char, this is where
3324 // the next character ends, and 2nd next begins. We'll need
3325 // to compare these two
3326 posBefore = posAfter;
3327 posAfter = MovePositionOutsideChar(posAfter + 1, 1);
3328 offsetLastChar = offset + (posAfter - posCaret);
3329 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
3330 // The char does not share horizontal space
3331 break;
3333 // Char shares horizontal space, update the numChars to draw
3334 numCharsToDraw = offsetLastChar - offsetFirstChar;
3337 // We now know what to draw, update the caret drawing rectangle
3338 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
3339 rcCaret.right = ll->positions[offsetFirstChar+numCharsToDraw] - ll->positions[lineStart] + xStart;
3341 // Adjust caret position to take into account any word wrapping symbols.
3342 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3343 XYPOSITION wordWrapCharWidth = ll->wrapIndent;
3344 rcCaret.left += wordWrapCharWidth;
3345 rcCaret.right += wordWrapCharWidth;
3348 // This character is where the caret block is, we override the colours
3349 // (inversed) for drawing the caret here.
3350 int styleMain = ll->styles[offsetFirstChar];
3351 surface->DrawTextClipped(rcCaret, vsDraw.styles[styleMain].font,
3352 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
3353 numCharsToDraw, vsDraw.styles[styleMain].back,
3354 caretColour);
3357 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
3358 if (!pixmapSelPattern->Initialised()) {
3359 const int patternSize = 8;
3360 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3361 pixmapSelPatternOffset1->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3362 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
3363 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
3364 // way between the chrome colour and the chrome highlight colour making a nice transition
3365 // between the window chrome and the content area. And it works in low colour depths.
3366 PRectangle rcPattern = PRectangle::FromInts(0, 0, patternSize, patternSize);
3368 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
3369 ColourDesired colourFMFill = vs.selbar;
3370 ColourDesired colourFMStripes = vs.selbarlight;
3372 if (!(vs.selbarlight == ColourDesired(0xff, 0xff, 0xff))) {
3373 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
3374 // (Typically, the highlight colour is white.)
3375 colourFMFill = vs.selbarlight;
3378 if (vs.foldmarginColour.isSet) {
3379 // override default fold margin colour
3380 colourFMFill = vs.foldmarginColour;
3382 if (vs.foldmarginHighlightColour.isSet) {
3383 // override default fold margin highlight colour
3384 colourFMStripes = vs.foldmarginHighlightColour;
3387 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
3388 pixmapSelPatternOffset1->FillRectangle(rcPattern, colourFMStripes);
3389 for (int y = 0; y < patternSize; y++) {
3390 for (int x = y % 2; x < patternSize; x+=2) {
3391 PRectangle rcPixel = PRectangle::FromInts(x, y, x + 1, y + 1);
3392 pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);
3393 pixmapSelPatternOffset1->FillRectangle(rcPixel, colourFMFill);
3398 if (!pixmapIndentGuide->Initialised()) {
3399 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
3400 pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3401 pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3402 PRectangle rcIG = PRectangle::FromInts(0, 0, 1, vs.lineHeight);
3403 pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back);
3404 pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore);
3405 pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back);
3406 pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore);
3407 for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
3408 PRectangle rcPixel = PRectangle::FromInts(0, stripe, 1, stripe + 1);
3409 pixmapIndentGuide->FillRectangle(rcPixel, vs.styles[STYLE_INDENTGUIDE].fore);
3410 pixmapIndentGuideHighlight->FillRectangle(rcPixel, vs.styles[STYLE_BRACELIGHT].fore);
3414 if (bufferedDraw) {
3415 if (!pixmapLine->Initialised()) {
3416 PRectangle rcClient = GetClientRectangle();
3417 pixmapLine->InitPixMap(static_cast<int>(rcClient.Width()), vs.lineHeight,
3418 surfaceWindow, wMain.GetID());
3419 pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
3420 static_cast<int>(rcClient.Height()), surfaceWindow, wMain.GetID());
3425 void Editor::DrawCarets(Surface *surface, ViewStyle &vsDraw, int lineDoc, int xStart,
3426 PRectangle rcLine, LineLayout *ll, int subLine) {
3427 // When drag is active it is the only caret drawn
3428 bool drawDrag = posDrag.IsValid();
3429 if (hideSelection && !drawDrag)
3430 return;
3431 const int posLineStart = pdoc->LineStart(lineDoc);
3432 // For each selection draw
3433 for (size_t r=0; (r<sel.Count()) || drawDrag; r++) {
3434 const bool mainCaret = r == sel.Main();
3435 const SelectionPosition posCaret = (drawDrag ? posDrag : sel.Range(r).caret);
3436 const int offset = posCaret.Position() - posLineStart;
3437 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
3438 const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
3439 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
3440 XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
3441 if (ll->wrapIndent != 0) {
3442 int lineStart = ll->LineStart(subLine);
3443 if (lineStart != 0) // Wrapped
3444 xposCaret += ll->wrapIndent;
3446 bool caretBlinkState = (caret.active && caret.on) || (!additionalCaretsBlink && !mainCaret);
3447 bool caretVisibleState = additionalCaretsVisible || mainCaret;
3448 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
3449 ((posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
3450 bool caretAtEOF = false;
3451 bool caretAtEOL = false;
3452 bool drawBlockCaret = false;
3453 XYPOSITION widthOverstrikeCaret;
3454 XYPOSITION caretWidthOffset = 0;
3455 PRectangle rcCaret = rcLine;
3457 if (posCaret.Position() == pdoc->Length()) { // At end of document
3458 caretAtEOF = true;
3459 widthOverstrikeCaret = vsDraw.aveCharWidth;
3460 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
3461 caretAtEOL = true;
3462 widthOverstrikeCaret = vsDraw.aveCharWidth;
3463 } else {
3464 widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
3466 if (widthOverstrikeCaret < 3) // Make sure its visible
3467 widthOverstrikeCaret = 3;
3469 if (xposCaret > 0)
3470 caretWidthOffset = 0.51f; // Move back so overlaps both character cells.
3471 xposCaret += xStart;
3472 if (posDrag.IsValid()) {
3473 /* Dragging text, use a line caret */
3474 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
3475 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3476 } else if (inOverstrike && drawOverstrikeCaret) {
3477 /* Overstrike (insert mode), use a modified bar caret */
3478 rcCaret.top = rcCaret.bottom - 2;
3479 rcCaret.left = xposCaret + 1;
3480 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
3481 } else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) {
3482 /* Block caret */
3483 rcCaret.left = xposCaret;
3484 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
3485 drawBlockCaret = true;
3486 rcCaret.right = xposCaret + widthOverstrikeCaret;
3487 } else {
3488 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
3490 } else {
3491 /* Line caret */
3492 rcCaret.left = static_cast<XYPOSITION>(RoundXYPosition(xposCaret - caretWidthOffset));
3493 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3495 ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
3496 if (drawBlockCaret) {
3497 DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
3498 } else {
3499 surface->FillRectangle(rcCaret, caretColour);
3503 if (drawDrag)
3504 break;
3508 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
3509 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
3510 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
3511 AllocateGraphics();
3513 RefreshStyleData();
3514 if (paintState == paintAbandoned)
3515 return; // Scroll bars may have changed so need redraw
3516 RefreshPixMaps(surfaceWindow);
3518 paintAbandonedByStyling = false;
3520 StyleToPositionInView(PositionAfterArea(rcArea));
3522 PRectangle rcClient = GetClientRectangle();
3523 Point ptOrigin = GetVisibleOriginInMain();
3524 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
3525 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3527 int screenLinePaintFirst = static_cast<int>(rcArea.top) / vs.lineHeight;
3529 int xStart = vs.textStart - xOffset + static_cast<int>(ptOrigin.x);
3530 int ypos = 0;
3531 if (!bufferedDraw)
3532 ypos += screenLinePaintFirst * vs.lineHeight;
3533 int yposScreen = screenLinePaintFirst * vs.lineHeight;
3535 if (NotifyUpdateUI()) {
3536 RefreshStyleData();
3537 RefreshPixMaps(surfaceWindow);
3540 // Wrap the visible lines if needed.
3541 if (WrapLines(wsVisible)) {
3542 // The wrapping process has changed the height of some lines so
3543 // abandon this paint for a complete repaint.
3544 if (AbandonPaint()) {
3545 return;
3547 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
3549 PLATFORM_ASSERT(pixmapSelPattern->Initialised());
3551 if (!bufferedDraw)
3552 surfaceWindow->SetClip(rcArea);
3554 if (paintState != paintAbandoned) {
3555 if (vs.marginInside) {
3556 PaintSelMargin(surfaceWindow, rcArea);
3557 PRectangle rcRightMargin = rcClient;
3558 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
3559 if (rcArea.Intersects(rcRightMargin)) {
3560 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
3562 } else { // Else separate view so separate paint event but leftMargin included to allow overlap
3563 PRectangle rcLeftMargin = rcArea;
3564 rcLeftMargin.left = 0;
3565 rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
3566 if (rcArea.Intersects(rcLeftMargin)) {
3567 surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[STYLE_DEFAULT].back);
3572 if (paintState == paintAbandoned) {
3573 // Either styling or NotifyUpdateUI noticed that painting is needed
3574 // outside the current painting rectangle
3575 //Platform::DebugPrintf("Abandoning paint\n");
3576 if (Wrapping()) {
3577 if (paintAbandonedByStyling) {
3578 // Styling has spilled over a line end, such as occurs by starting a multiline
3579 // comment. The width of subsequent text may have changed, so rewrap.
3580 NeedWrapping(cs.DocFromDisplay(topLine));
3583 return;
3585 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
3587 // Allow text at start of line to overlap 1 pixel into the margin as this displays
3588 // serifs and italic stems for aliased text.
3589 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
3591 // Do the painting
3592 if (rcArea.right > vs.textStart - leftTextOverlap) {
3594 Surface *surface = surfaceWindow;
3595 if (bufferedDraw) {
3596 surface = pixmapLine;
3597 PLATFORM_ASSERT(pixmapLine->Initialised());
3599 surface->SetUnicodeMode(IsUnicodeMode());
3600 surface->SetDBCSMode(CodePage());
3602 int visibleLine = TopLineOfMain() + screenLinePaintFirst;
3604 SelectionPosition posCaret = sel.RangeMain().caret;
3605 if (posDrag.IsValid())
3606 posCaret = posDrag;
3607 int lineCaret = pdoc->LineFromPosition(posCaret.Position());
3609 PRectangle rcTextArea = rcClient;
3610 if (vs.marginInside) {
3611 rcTextArea.left += vs.textStart;
3612 rcTextArea.right -= vs.rightMarginWidth;
3613 } else {
3614 rcTextArea = rcArea;
3617 // Remove selection margin from drawing area so text will not be drawn
3618 // on it in unbuffered mode.
3619 if (!bufferedDraw && vs.marginInside) {
3620 PRectangle rcClipText = rcTextArea;
3621 rcClipText.left -= leftTextOverlap;
3622 surfaceWindow->SetClip(rcClipText);
3625 // Loop on visible lines
3626 //double durLayout = 0.0;
3627 //double durPaint = 0.0;
3628 //double durCopy = 0.0;
3629 //ElapsedTime etWhole;
3630 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
3631 AutoLineLayout ll(llc, 0);
3632 while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
3634 int lineDoc = cs.DocFromDisplay(visibleLine);
3635 // Only visible lines should be handled by the code within the loop
3636 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
3637 int lineStartSet = cs.DisplayFromDoc(lineDoc);
3638 int subLine = visibleLine - lineStartSet;
3640 // Copy this line and its styles from the document into local arrays
3641 // and determine the x position at which each character starts.
3642 //ElapsedTime et;
3643 if (lineDoc != lineDocPrevious) {
3644 ll.Set(0);
3645 ll.Set(RetrieveLineLayout(lineDoc));
3646 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
3647 lineDocPrevious = lineDoc;
3649 //durLayout += et.Duration(true);
3651 if (ll) {
3652 ll->containsCaret = lineDoc == lineCaret;
3653 if (hideSelection) {
3654 ll->containsCaret = false;
3657 GetHotSpotRange(ll->hsStart, ll->hsEnd);
3659 PRectangle rcLine = rcTextArea;
3660 rcLine.top = static_cast<XYPOSITION>(ypos);
3661 rcLine.bottom = static_cast<XYPOSITION>(ypos + vs.lineHeight);
3663 bool bracesIgnoreStyle = false;
3664 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
3665 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
3666 bracesIgnoreStyle = true;
3668 Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
3669 // Highlight the current braces if any
3670 ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
3671 static_cast<int>(highlightGuideColumn * vs.spaceWidth), bracesIgnoreStyle);
3673 if (leftTextOverlap && bufferedDraw) {
3674 PRectangle rcSpacer = rcLine;
3675 rcSpacer.right = rcSpacer.left;
3676 rcSpacer.left -= 1;
3677 surface->FillRectangle(rcSpacer, vs.styles[STYLE_DEFAULT].back);
3680 // Draw the line
3681 DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
3682 //durPaint += et.Duration(true);
3684 // Restore the previous styles for the brace highlights in case layout is in cache.
3685 ll->RestoreBracesHighlight(rangeLine, braces, bracesIgnoreStyle);
3687 bool expanded = cs.GetExpanded(lineDoc);
3688 const int level = pdoc->GetLevel(lineDoc);
3689 const int levelNext = pdoc->GetLevel(lineDoc + 1);
3690 if ((level & SC_FOLDLEVELHEADERFLAG) &&
3691 ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
3692 // Paint the line above the fold
3693 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
3695 (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
3696 PRectangle rcFoldLine = rcLine;
3697 rcFoldLine.bottom = rcFoldLine.top + 1;
3698 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore);
3700 // Paint the line below the fold
3701 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
3703 (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
3704 PRectangle rcFoldLine = rcLine;
3705 rcFoldLine.top = rcFoldLine.bottom - 1;
3706 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore);
3710 DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine);
3712 if (bufferedDraw) {
3713 Point from = Point::FromInts(vs.textStart-leftTextOverlap, 0);
3714 PRectangle rcCopyArea = PRectangle::FromInts(vs.textStart - leftTextOverlap, yposScreen,
3715 static_cast<int>(rcClient.right - vs.rightMarginWidth),
3716 yposScreen + vs.lineHeight);
3717 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
3720 lineWidthMaxSeen = Platform::Maximum(
3721 lineWidthMaxSeen, static_cast<int>(ll->positions[ll->numCharsInLine]));
3722 //durCopy += et.Duration(true);
3725 if (!bufferedDraw) {
3726 ypos += vs.lineHeight;
3729 yposScreen += vs.lineHeight;
3730 visibleLine++;
3732 //gdk_flush();
3734 ll.Set(0);
3735 //if (durPaint < 0.00000001)
3736 // durPaint = 0.00000001;
3738 // Right column limit indicator
3739 PRectangle rcBeyondEOF = (vs.marginInside) ? rcClient : rcArea;
3740 rcBeyondEOF.left = static_cast<XYPOSITION>(vs.textStart);
3741 rcBeyondEOF.right = rcBeyondEOF.right - ((vs.marginInside) ? vs.rightMarginWidth : 0);
3742 rcBeyondEOF.top = static_cast<XYPOSITION>((cs.LinesDisplayed() - TopLineOfMain()) * vs.lineHeight);
3743 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
3744 surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back);
3745 if (vs.edgeState == EDGE_LINE) {
3746 int edgeX = static_cast<int>(vs.theEdge * vs.spaceWidth);
3747 rcBeyondEOF.left = static_cast<XYPOSITION>(edgeX + xStart);
3748 rcBeyondEOF.right = rcBeyondEOF.left + 1;
3749 surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour);
3752 //Platform::DebugPrintf(
3753 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
3754 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
3755 NotifyPainted();
3759 // Space (3 space characters) between line numbers and text when printing.
3760 #define lineNumberPrintSpace " "
3762 ColourDesired InvertedLight(ColourDesired orig) {
3763 unsigned int r = orig.GetRed();
3764 unsigned int g = orig.GetGreen();
3765 unsigned int b = orig.GetBlue();
3766 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
3767 unsigned int il = 0xff - l;
3768 if (l == 0)
3769 return ColourDesired(0xff, 0xff, 0xff);
3770 r = r * il / l;
3771 g = g * il / l;
3772 b = b * il / l;
3773 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
3776 // This is mostly copied from the Paint method but with some things omitted
3777 // such as the margin markers, line numbers, selection and caret
3778 // Should be merged back into a combined Draw method.
3779 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
3780 if (!pfr)
3781 return 0;
3783 AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
3784 if (!surface)
3785 return 0;
3786 AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
3787 if (!surfaceMeasure) {
3788 return 0;
3791 // Can't use measurements cached for screen
3792 posCache.Clear();
3794 ViewStyle vsPrint(vs);
3795 vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
3797 // Modify the view style for printing as do not normally want any of the transient features to be printed
3798 // Printing supports only the line number margin.
3799 int lineNumberIndex = -1;
3800 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
3801 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
3802 lineNumberIndex = margin;
3803 } else {
3804 vsPrint.ms[margin].width = 0;
3807 vsPrint.fixedColumnWidth = 0;
3808 vsPrint.zoomLevel = printParameters.magnification;
3809 // Don't show indentation guides
3810 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
3811 vsPrint.viewIndentationGuides = ivNone;
3812 // Don't show the selection when printing
3813 vsPrint.selColours.back.isSet = false;
3814 vsPrint.selColours.fore.isSet = false;
3815 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
3816 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
3817 vsPrint.whitespaceColours.back.isSet = false;
3818 vsPrint.whitespaceColours.fore.isSet = false;
3819 vsPrint.showCaretLineBackground = false;
3820 vsPrint.alwaysShowCaretLineBackground = false;
3821 // Don't highlight matching braces using indicators
3822 vsPrint.braceHighlightIndicatorSet = false;
3823 vsPrint.braceBadLightIndicatorSet = false;
3825 // Set colours for printing according to users settings
3826 for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) {
3827 if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) {
3828 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
3829 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
3830 } else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) {
3831 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
3832 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3833 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) {
3834 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3835 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
3836 if (sty <= STYLE_DEFAULT) {
3837 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3841 // White background for the line numbers
3842 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
3844 // Printing uses different margins, so reset screen margins
3845 vsPrint.leftMarginWidth = 0;
3846 vsPrint.rightMarginWidth = 0;
3848 vsPrint.Refresh(*surfaceMeasure, pdoc->tabInChars);
3849 // Determining width must hapen after fonts have been realised in Refresh
3850 int lineNumberWidth = 0;
3851 if (lineNumberIndex >= 0) {
3852 lineNumberWidth = static_cast<int>(surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
3853 "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace)));
3854 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
3855 vsPrint.Refresh(*surfaceMeasure, pdoc->tabInChars); // Recalculate fixedColumnWidth
3858 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
3859 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
3860 if (linePrintLast < linePrintStart)
3861 linePrintLast = linePrintStart;
3862 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
3863 if (linePrintLast > linePrintMax)
3864 linePrintLast = linePrintMax;
3865 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
3866 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
3867 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
3868 int endPosPrint = pdoc->Length();
3869 if (linePrintLast < pdoc->LinesTotal())
3870 endPosPrint = pdoc->LineStart(linePrintLast + 1);
3872 // Ensure we are styled to where we are formatting.
3873 pdoc->EnsureStyledTo(endPosPrint);
3875 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
3876 int ypos = pfr->rc.top;
3878 int lineDoc = linePrintStart;
3880 int nPrintPos = pfr->chrg.cpMin;
3881 int visibleLine = 0;
3882 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
3883 if (printParameters.wrapState == eWrapNone)
3884 widthPrint = LineLayout::wrapWidthInfinite;
3886 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
3888 // When printing, the hdc and hdcTarget may be the same, so
3889 // changing the state of surfaceMeasure may change the underlying
3890 // state of surface. Therefore, any cached state is discarded before
3891 // using each surface.
3892 surfaceMeasure->FlushCachedState();
3894 // Copy this line and its styles from the document into local arrays
3895 // and determine the x position at which each character starts.
3896 LineLayout ll(pdoc->LineStart(lineDoc+1)-pdoc->LineStart(lineDoc)+1);
3897 LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
3899 ll.containsCaret = false;
3901 PRectangle rcLine = PRectangle::FromInts(
3902 pfr->rc.left,
3903 ypos,
3904 pfr->rc.right - 1,
3905 ypos + vsPrint.lineHeight);
3907 // When document line is wrapped over multiple display lines, find where
3908 // to start printing from to ensure a particular position is on the first
3909 // line of the page.
3910 if (visibleLine == 0) {
3911 int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
3912 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
3913 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
3914 visibleLine = -iwl;
3918 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
3919 visibleLine = -(ll.lines - 1);
3923 if (draw && lineNumberWidth &&
3924 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
3925 (visibleLine >= 0)) {
3926 char number[100];
3927 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
3928 PRectangle rcNumber = rcLine;
3929 rcNumber.right = rcNumber.left + lineNumberWidth;
3930 // Right justify
3931 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
3932 vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
3933 surface->FlushCachedState();
3934 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
3935 static_cast<XYPOSITION>(ypos + vsPrint.maxAscent), number, istrlen(number),
3936 vsPrint.styles[STYLE_LINENUMBER].fore,
3937 vsPrint.styles[STYLE_LINENUMBER].back);
3940 // Draw the line
3941 surface->FlushCachedState();
3943 for (int iwl = 0; iwl < ll.lines; iwl++) {
3944 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
3945 if (visibleLine >= 0) {
3946 if (draw) {
3947 rcLine.top = static_cast<XYPOSITION>(ypos);
3948 rcLine.bottom = static_cast<XYPOSITION>(ypos + vsPrint.lineHeight);
3949 DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
3951 ypos += vsPrint.lineHeight;
3953 visibleLine++;
3954 if (iwl == ll.lines - 1)
3955 nPrintPos = pdoc->LineStart(lineDoc + 1);
3956 else
3957 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
3961 ++lineDoc;
3964 // Clear cache so measurements are not used for screen
3965 posCache.Clear();
3967 return nPrintPos;
3970 int Editor::TextWidth(int style, const char *text) {
3971 RefreshStyleData();
3972 AutoSurface surface(this);
3973 if (surface) {
3974 return static_cast<int>(surface->WidthText(vs.styles[style].font, text, istrlen(text)));
3975 } else {
3976 return 1;
3980 // Empty method is overridden on GTK+ to show / hide scrollbars
3981 void Editor::ReconfigureScrollBars() {}
3983 void Editor::SetScrollBars() {
3984 RefreshStyleData();
3986 int nMax = MaxScrollPos();
3987 int nPage = LinesOnScreen();
3988 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
3989 if (modified) {
3990 DwellEnd(true);
3993 // TODO: ensure always showing as many lines as possible
3994 // May not be, if, for example, window made larger
3995 if (topLine > MaxScrollPos()) {
3996 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
3997 SetVerticalScrollPos();
3998 Redraw();
4000 if (modified) {
4001 if (!AbandonPaint())
4002 Redraw();
4004 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
4007 void Editor::ChangeSize() {
4008 DropGraphics(false);
4009 SetScrollBars();
4010 if (Wrapping()) {
4011 PRectangle rcTextArea = GetClientRectangle();
4012 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
4013 rcTextArea.right -= vs.rightMarginWidth;
4014 if (wrapWidth != rcTextArea.Width()) {
4015 NeedWrapping();
4016 Redraw();
4021 int Editor::InsertSpace(int position, unsigned int spaces) {
4022 if (spaces > 0) {
4023 std::string spaceText(spaces, ' ');
4024 const int lengthInserted = pdoc->InsertString(position, spaceText.c_str(), spaces);
4025 position += lengthInserted;
4027 return position;
4030 void Editor::AddChar(char ch) {
4031 char s[2];
4032 s[0] = ch;
4033 s[1] = '\0';
4034 AddCharUTF(s, 1);
4037 void Editor::FilterSelections() {
4038 if (!additionalSelectionTyping && (sel.Count() > 1)) {
4039 SelectionRange rangeOnly = sel.RangeMain();
4040 InvalidateSelection(rangeOnly, true);
4041 sel.SetSelection(rangeOnly);
4045 static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
4046 return *a < *b;
4049 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
4050 void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
4051 FilterSelections();
4053 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
4055 std::vector<SelectionRange *> selPtrs;
4056 for (size_t r = 0; r < sel.Count(); r++) {
4057 selPtrs.push_back(&sel.Range(r));
4059 std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
4061 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
4062 rit != selPtrs.rend(); ++rit) {
4063 SelectionRange *currentSel = *rit;
4064 if (!RangeContainsProtected(currentSel->Start().Position(),
4065 currentSel->End().Position())) {
4066 int positionInsert = currentSel->Start().Position();
4067 if (!currentSel->Empty()) {
4068 if (currentSel->Length()) {
4069 pdoc->DeleteChars(positionInsert, currentSel->Length());
4070 currentSel->ClearVirtualSpace();
4071 } else {
4072 // Range is all virtual so collapse to start of virtual space
4073 currentSel->MinimizeVirtualSpace();
4075 } else if (inOverstrike) {
4076 if (positionInsert < pdoc->Length()) {
4077 if (!pdoc->IsPositionInLineEnd(positionInsert)) {
4078 pdoc->DelChar(positionInsert);
4079 currentSel->ClearVirtualSpace();
4083 positionInsert = InsertSpace(positionInsert, currentSel->caret.VirtualSpace());
4084 const int lengthInserted = pdoc->InsertString(positionInsert, s, len);
4085 if (lengthInserted > 0) {
4086 currentSel->caret.SetPosition(positionInsert + lengthInserted);
4087 currentSel->anchor.SetPosition(positionInsert + lengthInserted);
4089 currentSel->ClearVirtualSpace();
4090 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4091 if (Wrapping()) {
4092 AutoSurface surface(this);
4093 if (surface) {
4094 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
4095 SetScrollBars();
4096 SetVerticalScrollPos();
4097 Redraw();
4104 if (Wrapping()) {
4105 SetScrollBars();
4107 ThinRectangularRange();
4108 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4109 EnsureCaretVisible();
4110 // Avoid blinking during rapid typing:
4111 ShowCaretAtCurrentPosition();
4112 if ((caretSticky == SC_CARETSTICKY_OFF) ||
4113 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
4114 SetLastXChosen();
4117 if (treatAsDBCS) {
4118 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
4119 static_cast<unsigned char>(s[1]));
4120 } else if (len > 0) {
4121 int byte = static_cast<unsigned char>(s[0]);
4122 if ((byte < 0xC0) || (1 == len)) {
4123 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
4124 // characters when not in UTF-8 mode.
4125 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
4126 // characters representing themselves.
4127 } else {
4128 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
4129 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
4130 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
4131 if (byte < 0xE0) {
4132 int byte2 = static_cast<unsigned char>(s[1]);
4133 if ((byte2 & 0xC0) == 0x80) {
4134 // Two-byte-character lead-byte followed by a trail-byte.
4135 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
4137 // A two-byte-character lead-byte not followed by trail-byte
4138 // represents itself.
4139 } else if (byte < 0xF0) {
4140 int byte2 = static_cast<unsigned char>(s[1]);
4141 int byte3 = static_cast<unsigned char>(s[2]);
4142 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
4143 // Three-byte-character lead byte followed by two trail bytes.
4144 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
4145 (byte3 & 0x3F));
4147 // A three-byte-character lead-byte not followed by two trail-bytes
4148 // represents itself.
4151 NotifyChar(byte);
4154 if (recordingMacro) {
4155 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
4159 void Editor::InsertPaste(const char *text, int len) {
4160 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
4161 SelectionPosition selStart = sel.Start();
4162 selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
4163 const int lengthInserted = pdoc->InsertString(selStart.Position(), text, len);
4164 if (lengthInserted > 0) {
4165 SetEmptySelection(selStart.Position() + lengthInserted);
4167 } else {
4168 // SC_MULTIPASTE_EACH
4169 for (size_t r=0; r<sel.Count(); r++) {
4170 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4171 sel.Range(r).End().Position())) {
4172 int positionInsert = sel.Range(r).Start().Position();
4173 if (!sel.Range(r).Empty()) {
4174 if (sel.Range(r).Length()) {
4175 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
4176 sel.Range(r).ClearVirtualSpace();
4177 } else {
4178 // Range is all virtual so collapse to start of virtual space
4179 sel.Range(r).MinimizeVirtualSpace();
4182 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
4183 const int lengthInserted = pdoc->InsertString(positionInsert, text, len);
4184 if (lengthInserted > 0) {
4185 sel.Range(r).caret.SetPosition(positionInsert + lengthInserted);
4186 sel.Range(r).anchor.SetPosition(positionInsert + lengthInserted);
4188 sel.Range(r).ClearVirtualSpace();
4194 void Editor::InsertPasteShape(const char *text, int len, PasteShape shape) {
4195 std::string convertedText;
4196 if (convertPastes) {
4197 // Convert line endings of the paste into our local line-endings mode
4198 convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
4199 len = static_cast<int>(convertedText.length());
4200 text = convertedText.c_str();
4202 if (shape == pasteRectangular) {
4203 PasteRectangular(sel.Start(), text, len);
4204 } else {
4205 if (shape == pasteLine) {
4206 int insertPos = pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
4207 int lengthInserted = pdoc->InsertString(insertPos, text, len);
4208 // add the newline if necessary
4209 if ((len > 0) && (text[len - 1] != '\n' && text[len - 1] != '\r')) {
4210 const char *endline = StringFromEOLMode(pdoc->eolMode);
4211 int length = static_cast<int>(strlen(endline));
4212 lengthInserted += pdoc->InsertString(insertPos + lengthInserted, endline, length);
4214 if (sel.MainCaret() == insertPos) {
4215 SetEmptySelection(sel.MainCaret() + lengthInserted);
4217 } else {
4218 InsertPaste(text, len);
4223 void Editor::ClearSelection(bool retainMultipleSelections) {
4224 if (!sel.IsRectangular() && !retainMultipleSelections)
4225 FilterSelections();
4226 UndoGroup ug(pdoc);
4227 for (size_t r=0; r<sel.Count(); r++) {
4228 if (!sel.Range(r).Empty()) {
4229 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4230 sel.Range(r).End().Position())) {
4231 pdoc->DeleteChars(sel.Range(r).Start().Position(),
4232 sel.Range(r).Length());
4233 sel.Range(r) = SelectionRange(sel.Range(r).Start());
4237 ThinRectangularRange();
4238 sel.RemoveDuplicates();
4239 ClaimSelection();
4242 void Editor::ClearAll() {
4244 UndoGroup ug(pdoc);
4245 if (0 != pdoc->Length()) {
4246 pdoc->DeleteChars(0, pdoc->Length());
4248 if (!pdoc->IsReadOnly()) {
4249 cs.Clear();
4250 pdoc->AnnotationClearAll();
4251 pdoc->MarginClearAll();
4254 sel.Clear();
4255 SetTopLine(0);
4256 SetVerticalScrollPos();
4257 InvalidateStyleRedraw();
4260 void Editor::ClearDocumentStyle() {
4261 Decoration *deco = pdoc->decorations.root;
4262 while (deco) {
4263 // Save next in case deco deleted
4264 Decoration *decoNext = deco->next;
4265 if (deco->indicator < INDIC_CONTAINER) {
4266 pdoc->decorations.SetCurrentIndicator(deco->indicator);
4267 pdoc->DecorationFillRange(0, 0, pdoc->Length());
4269 deco = decoNext;
4271 pdoc->StartStyling(0, '\377');
4272 pdoc->SetStyleFor(pdoc->Length(), 0);
4273 cs.ShowAll();
4274 pdoc->ClearLevels();
4277 void Editor::CopyAllowLine() {
4278 SelectionText selectedText;
4279 CopySelectionRange(&selectedText, true);
4280 CopyToClipboard(selectedText);
4283 void Editor::Cut() {
4284 pdoc->CheckReadOnly();
4285 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
4286 Copy();
4287 ClearSelection();
4291 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
4292 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
4293 return;
4295 sel.Clear();
4296 sel.RangeMain() = SelectionRange(pos);
4297 int line = pdoc->LineFromPosition(sel.MainCaret());
4298 UndoGroup ug(pdoc);
4299 sel.RangeMain().caret = SelectionPosition(
4300 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
4301 int xInsert = XFromPosition(sel.RangeMain().caret);
4302 bool prevCr = false;
4303 while ((len > 0) && IsEOLChar(ptr[len-1]))
4304 len--;
4305 for (int i = 0; i < len; i++) {
4306 if (IsEOLChar(ptr[i])) {
4307 if ((ptr[i] == '\r') || (!prevCr))
4308 line++;
4309 if (line >= pdoc->LinesTotal()) {
4310 if (pdoc->eolMode != SC_EOL_LF)
4311 pdoc->InsertString(pdoc->Length(), "\r", 1);
4312 if (pdoc->eolMode != SC_EOL_CR)
4313 pdoc->InsertString(pdoc->Length(), "\n", 1);
4315 // Pad the end of lines with spaces if required
4316 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
4317 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
4318 while (XFromPosition(sel.MainCaret()) < xInsert) {
4319 const int lengthInserted = pdoc->InsertString(sel.MainCaret(), " ", 1);
4320 sel.RangeMain().caret.Add(lengthInserted);
4323 prevCr = ptr[i] == '\r';
4324 } else {
4325 const int lengthInserted = pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
4326 sel.RangeMain().caret.Add(lengthInserted);
4327 prevCr = false;
4330 SetEmptySelection(pos);
4333 bool Editor::CanPaste() {
4334 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
4337 void Editor::Clear() {
4338 // If multiple selections, don't delete EOLS
4339 if (sel.Empty()) {
4340 bool singleVirtual = false;
4341 if ((sel.Count() == 1) &&
4342 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
4343 sel.RangeMain().Start().VirtualSpace()) {
4344 singleVirtual = true;
4346 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
4347 for (size_t r=0; r<sel.Count(); r++) {
4348 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
4349 if (sel.Range(r).Start().VirtualSpace()) {
4350 if (sel.Range(r).anchor < sel.Range(r).caret)
4351 sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
4352 else
4353 sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
4355 if ((sel.Count() == 1) || !pdoc->IsPositionInLineEnd(sel.Range(r).caret.Position())) {
4356 pdoc->DelChar(sel.Range(r).caret.Position());
4357 sel.Range(r).ClearVirtualSpace();
4358 } // else multiple selection so don't eat line ends
4359 } else {
4360 sel.Range(r).ClearVirtualSpace();
4363 } else {
4364 ClearSelection();
4366 sel.RemoveDuplicates();
4369 void Editor::SelectAll() {
4370 sel.Clear();
4371 SetSelection(0, pdoc->Length());
4372 Redraw();
4375 void Editor::Undo() {
4376 if (pdoc->CanUndo()) {
4377 InvalidateCaret();
4378 int newPos = pdoc->Undo();
4379 if (newPos >= 0)
4380 SetEmptySelection(newPos);
4381 EnsureCaretVisible();
4385 void Editor::Redo() {
4386 if (pdoc->CanRedo()) {
4387 int newPos = pdoc->Redo();
4388 if (newPos >= 0)
4389 SetEmptySelection(newPos);
4390 EnsureCaretVisible();
4394 void Editor::DelChar() {
4395 if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) {
4396 pdoc->DelChar(sel.MainCaret());
4398 // Avoid blinking during rapid typing:
4399 ShowCaretAtCurrentPosition();
4402 void Editor::DelCharBack(bool allowLineStartDeletion) {
4403 if (!sel.IsRectangular())
4404 FilterSelections();
4405 if (sel.IsRectangular())
4406 allowLineStartDeletion = false;
4407 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
4408 if (sel.Empty()) {
4409 for (size_t r=0; r<sel.Count(); r++) {
4410 if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
4411 if (sel.Range(r).caret.VirtualSpace()) {
4412 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
4413 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
4414 } else {
4415 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4416 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
4417 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
4418 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
4419 UndoGroup ugInner(pdoc, !ug.Needed());
4420 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4421 int indentationStep = pdoc->IndentSize();
4422 int indentationChange = indentation % indentationStep;
4423 if (indentationChange == 0)
4424 indentationChange = indentationStep;
4425 const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationChange);
4426 // SetEmptySelection
4427 sel.Range(r) = SelectionRange(posSelect);
4428 } else {
4429 pdoc->DelCharBack(sel.Range(r).caret.Position());
4433 } else {
4434 sel.Range(r).ClearVirtualSpace();
4437 ThinRectangularRange();
4438 } else {
4439 ClearSelection();
4441 sel.RemoveDuplicates();
4442 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
4443 // Avoid blinking during rapid typing:
4444 ShowCaretAtCurrentPosition();
4447 int Editor::ModifierFlags(bool shift, bool ctrl, bool alt, bool meta) {
4448 return
4449 (shift ? SCI_SHIFT : 0) |
4450 (ctrl ? SCI_CTRL : 0) |
4451 (alt ? SCI_ALT : 0) |
4452 (meta ? SCI_META : 0);
4455 void Editor::NotifyFocus(bool focus) {
4456 SCNotification scn = {};
4457 scn.nmhdr.code = focus ? SCN_FOCUSIN : SCN_FOCUSOUT;
4458 NotifyParent(scn);
4461 void Editor::SetCtrlID(int identifier) {
4462 ctrlID = identifier;
4465 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
4466 SCNotification scn = {};
4467 scn.nmhdr.code = SCN_STYLENEEDED;
4468 scn.position = endStyleNeeded;
4469 NotifyParent(scn);
4472 void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
4473 NotifyStyleToNeeded(endStyleNeeded);
4476 void Editor::NotifyLexerChanged(Document *, void *) {
4479 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
4480 errorStatus = status;
4483 void Editor::NotifyChar(int ch) {
4484 SCNotification scn = {};
4485 scn.nmhdr.code = SCN_CHARADDED;
4486 scn.ch = ch;
4487 NotifyParent(scn);
4490 void Editor::NotifySavePoint(bool isSavePoint) {
4491 SCNotification scn = {};
4492 if (isSavePoint) {
4493 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
4494 } else {
4495 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
4497 NotifyParent(scn);
4500 void Editor::NotifyModifyAttempt() {
4501 SCNotification scn = {};
4502 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
4503 NotifyParent(scn);
4506 void Editor::NotifyDoubleClick(Point pt, int modifiers) {
4507 SCNotification scn = {};
4508 scn.nmhdr.code = SCN_DOUBLECLICK;
4509 scn.line = LineFromLocation(pt);
4510 scn.position = PositionFromLocation(pt, true);
4511 scn.modifiers = modifiers;
4512 NotifyParent(scn);
4515 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
4516 NotifyDoubleClick(pt, ModifierFlags(shift, ctrl, alt));
4519 void Editor::NotifyHotSpotDoubleClicked(int position, int modifiers) {
4520 SCNotification scn = {};
4521 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
4522 scn.position = position;
4523 scn.modifiers = modifiers;
4524 NotifyParent(scn);
4527 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
4528 NotifyHotSpotDoubleClicked(position, ModifierFlags(shift, ctrl, alt));
4531 void Editor::NotifyHotSpotClicked(int position, int modifiers) {
4532 SCNotification scn = {};
4533 scn.nmhdr.code = SCN_HOTSPOTCLICK;
4534 scn.position = position;
4535 scn.modifiers = modifiers;
4536 NotifyParent(scn);
4539 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
4540 NotifyHotSpotClicked(position, ModifierFlags(shift, ctrl, alt));
4543 void Editor::NotifyHotSpotReleaseClick(int position, int modifiers) {
4544 SCNotification scn = {};
4545 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
4546 scn.position = position;
4547 scn.modifiers = modifiers;
4548 NotifyParent(scn);
4551 void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
4552 NotifyHotSpotReleaseClick(position, ModifierFlags(shift, ctrl, alt));
4555 bool Editor::NotifyUpdateUI() {
4556 if (needUpdateUI) {
4557 SCNotification scn = {};
4558 scn.nmhdr.code = SCN_UPDATEUI;
4559 scn.updated = needUpdateUI;
4560 NotifyParent(scn);
4561 needUpdateUI = 0;
4562 return true;
4564 return false;
4567 void Editor::NotifyPainted() {
4568 SCNotification scn = {};
4569 scn.nmhdr.code = SCN_PAINTED;
4570 NotifyParent(scn);
4573 void Editor::NotifyIndicatorClick(bool click, int position, int modifiers) {
4574 int mask = pdoc->decorations.AllOnFor(position);
4575 if ((click && mask) || pdoc->decorations.clickNotified) {
4576 SCNotification scn = {};
4577 pdoc->decorations.clickNotified = click;
4578 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
4579 scn.modifiers = modifiers;
4580 scn.position = position;
4581 NotifyParent(scn);
4585 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
4586 NotifyIndicatorClick(click, position, ModifierFlags(shift, ctrl, alt));
4589 bool Editor::NotifyMarginClick(Point pt, int modifiers) {
4590 int marginClicked = -1;
4591 int x = vs.textStart - vs.fixedColumnWidth;
4592 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
4593 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
4594 marginClicked = margin;
4595 x += vs.ms[margin].width;
4597 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
4598 int position = pdoc->LineStart(LineFromLocation(pt));
4599 if ((vs.ms[marginClicked].mask & SC_MASK_FOLDERS) && (foldAutomatic & SC_AUTOMATICFOLD_CLICK)) {
4600 const bool ctrl = (modifiers & SCI_CTRL) != 0;
4601 const bool shift = (modifiers & SCI_SHIFT) != 0;
4602 int lineClick = pdoc->LineFromPosition(position);
4603 if (shift && ctrl) {
4604 FoldAll(SC_FOLDACTION_TOGGLE);
4605 } else {
4606 int levelClick = pdoc->GetLevel(lineClick);
4607 if (levelClick & SC_FOLDLEVELHEADERFLAG) {
4608 if (shift) {
4609 // Ensure all children visible
4610 FoldExpand(lineClick, SC_FOLDACTION_EXPAND, levelClick);
4611 } else if (ctrl) {
4612 FoldExpand(lineClick, SC_FOLDACTION_TOGGLE, levelClick);
4613 } else {
4614 // Toggle this line
4615 FoldLine(lineClick, SC_FOLDACTION_TOGGLE);
4619 return true;
4621 SCNotification scn = {};
4622 scn.nmhdr.code = SCN_MARGINCLICK;
4623 scn.modifiers = modifiers;
4624 scn.position = position;
4625 scn.margin = marginClicked;
4626 NotifyParent(scn);
4627 return true;
4628 } else {
4629 return false;
4633 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
4634 return NotifyMarginClick(pt, ModifierFlags(shift, ctrl, alt));
4637 void Editor::NotifyNeedShown(int pos, int len) {
4638 SCNotification scn = {};
4639 scn.nmhdr.code = SCN_NEEDSHOWN;
4640 scn.position = pos;
4641 scn.length = len;
4642 NotifyParent(scn);
4645 void Editor::NotifyDwelling(Point pt, bool state) {
4646 SCNotification scn = {};
4647 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
4648 scn.position = PositionFromLocation(pt, true);
4649 scn.x = static_cast<int>(pt.x + vs.ExternalMarginWidth());
4650 scn.y = static_cast<int>(pt.y);
4651 NotifyParent(scn);
4654 void Editor::NotifyZoom() {
4655 SCNotification scn = {};
4656 scn.nmhdr.code = SCN_ZOOM;
4657 NotifyParent(scn);
4660 // Notifications from document
4661 void Editor::NotifyModifyAttempt(Document *, void *) {
4662 //Platform::DebugPrintf("** Modify Attempt\n");
4663 NotifyModifyAttempt();
4666 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
4667 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
4668 NotifySavePoint(atSavePoint);
4671 void Editor::CheckModificationForWrap(DocModification mh) {
4672 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
4673 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4674 int lineDoc = pdoc->LineFromPosition(mh.position);
4675 int lines = Platform::Maximum(0, mh.linesAdded);
4676 if (Wrapping()) {
4677 NeedWrapping(lineDoc, lineDoc + lines + 1);
4679 RefreshStyleData();
4680 // Fix up annotation heights
4681 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
4685 // Move a position so it is still after the same character as before the insertion.
4686 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
4687 if (position > startInsertion) {
4688 return position + length;
4690 return position;
4693 // Move a position so it is still after the same character as before the deletion if that
4694 // character is still present else after the previous surviving character.
4695 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
4696 if (position > startDeletion) {
4697 int endDeletion = startDeletion + length;
4698 if (position > endDeletion) {
4699 return position - length;
4700 } else {
4701 return startDeletion;
4703 } else {
4704 return position;
4708 void Editor::NotifyModified(Document *, DocModification mh, void *) {
4709 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
4710 if (paintState == painting) {
4711 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
4713 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
4714 if (paintState == painting) {
4715 CheckForChangeOutsidePaint(
4716 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
4717 } else {
4718 // Could check that change is before last visible line.
4719 Redraw();
4722 if (mh.modificationType & SC_MOD_LEXERSTATE) {
4723 if (paintState == painting) {
4724 CheckForChangeOutsidePaint(
4725 Range(mh.position, mh.position + mh.length));
4726 } else {
4727 Redraw();
4730 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
4731 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4732 pdoc->IncrementStyleClock();
4734 if (paintState == notPainting) {
4735 if (mh.position < pdoc->LineStart(topLine)) {
4736 // Styling performed before this view
4737 Redraw();
4738 } else {
4739 InvalidateRange(mh.position, mh.position + mh.length);
4742 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4743 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4745 } else {
4746 // Move selection and brace highlights
4747 if (mh.modificationType & SC_MOD_INSERTTEXT) {
4748 sel.MovePositions(true, mh.position, mh.length);
4749 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
4750 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
4751 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
4752 sel.MovePositions(false, mh.position, mh.length);
4753 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
4754 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
4756 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && cs.HiddenLines()) {
4757 // Some lines are hidden so may need shown.
4758 // TODO: check if the modified area is hidden.
4759 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
4760 int lineOfPos = pdoc->LineFromPosition(mh.position);
4761 bool insertingNewLine = false;
4762 for (int i=0; i < mh.length; i++) {
4763 if ((mh.text[i] == '\n') || (mh.text[i] == '\r'))
4764 insertingNewLine = true;
4766 if (insertingNewLine && (mh.position != pdoc->LineStart(lineOfPos)))
4767 NeedShown(mh.position, pdoc->LineStart(lineOfPos+1) - mh.position);
4768 else
4769 NeedShown(mh.position, 0);
4770 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
4771 NeedShown(mh.position, mh.length);
4774 if (mh.linesAdded != 0) {
4775 // Update contraction state for inserted and removed lines
4776 // lineOfPos should be calculated in context of state before modification, shouldn't it
4777 int lineOfPos = pdoc->LineFromPosition(mh.position);
4778 if (mh.position > pdoc->LineStart(lineOfPos))
4779 lineOfPos++; // Affecting subsequent lines
4780 if (mh.linesAdded > 0) {
4781 cs.InsertLines(lineOfPos, mh.linesAdded);
4782 } else {
4783 cs.DeleteLines(lineOfPos, -mh.linesAdded);
4786 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
4787 int lineDoc = pdoc->LineFromPosition(mh.position);
4788 if (vs.annotationVisible) {
4789 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
4790 Redraw();
4793 CheckModificationForWrap(mh);
4794 if (mh.linesAdded != 0) {
4795 // Avoid scrolling of display if change before current display
4796 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
4797 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
4798 if (newTop != topLine) {
4799 SetTopLine(newTop);
4800 SetVerticalScrollPos();
4804 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
4805 QueueIdleWork(WorkNeeded::workStyle, pdoc->Length());
4806 Redraw();
4808 } else {
4809 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
4810 QueueIdleWork(WorkNeeded::workStyle, mh.position + mh.length);
4811 InvalidateRange(mh.position, mh.position + mh.length);
4816 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
4817 SetScrollBars();
4820 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
4821 if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) {
4822 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
4823 // Fold changes can affect the drawing of following lines so redraw whole margin
4824 RedrawSelMargin(highlightDelimiter.isEnabled ? -1 : mh.line-1, true);
4825 } else {
4826 RedrawSelMargin(mh.line);
4830 if ((mh.modificationType & SC_MOD_CHANGEFOLD) && (foldAutomatic & SC_AUTOMATICFOLD_CHANGE)) {
4831 FoldChanged(mh.line, mh.foldLevelNow, mh.foldLevelPrev);
4834 // NOW pay the piper WRT "deferred" visual updates
4835 if (IsLastStep(mh)) {
4836 SetScrollBars();
4837 Redraw();
4840 // If client wants to see this modification
4841 if (mh.modificationType & modEventMask) {
4842 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
4843 // Real modification made to text of document.
4844 NotifyChange(); // Send EN_CHANGE
4847 SCNotification scn = {};
4848 scn.nmhdr.code = SCN_MODIFIED;
4849 scn.position = mh.position;
4850 scn.modificationType = mh.modificationType;
4851 scn.text = mh.text;
4852 scn.length = mh.length;
4853 scn.linesAdded = mh.linesAdded;
4854 scn.line = mh.line;
4855 scn.foldLevelNow = mh.foldLevelNow;
4856 scn.foldLevelPrev = mh.foldLevelPrev;
4857 scn.token = mh.token;
4858 scn.annotationLinesAdded = mh.annotationLinesAdded;
4859 NotifyParent(scn);
4863 void Editor::NotifyDeleted(Document *, void *) {
4864 /* Do nothing */
4867 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
4869 // Enumerates all macroable messages
4870 switch (iMessage) {
4871 case SCI_CUT:
4872 case SCI_COPY:
4873 case SCI_PASTE:
4874 case SCI_CLEAR:
4875 case SCI_REPLACESEL:
4876 case SCI_ADDTEXT:
4877 case SCI_INSERTTEXT:
4878 case SCI_APPENDTEXT:
4879 case SCI_CLEARALL:
4880 case SCI_SELECTALL:
4881 case SCI_GOTOLINE:
4882 case SCI_GOTOPOS:
4883 case SCI_SEARCHANCHOR:
4884 case SCI_SEARCHNEXT:
4885 case SCI_SEARCHPREV:
4886 case SCI_LINEDOWN:
4887 case SCI_LINEDOWNEXTEND:
4888 case SCI_PARADOWN:
4889 case SCI_PARADOWNEXTEND:
4890 case SCI_LINEUP:
4891 case SCI_LINEUPEXTEND:
4892 case SCI_PARAUP:
4893 case SCI_PARAUPEXTEND:
4894 case SCI_CHARLEFT:
4895 case SCI_CHARLEFTEXTEND:
4896 case SCI_CHARRIGHT:
4897 case SCI_CHARRIGHTEXTEND:
4898 case SCI_WORDLEFT:
4899 case SCI_WORDLEFTEXTEND:
4900 case SCI_WORDRIGHT:
4901 case SCI_WORDRIGHTEXTEND:
4902 case SCI_WORDPARTLEFT:
4903 case SCI_WORDPARTLEFTEXTEND:
4904 case SCI_WORDPARTRIGHT:
4905 case SCI_WORDPARTRIGHTEXTEND:
4906 case SCI_WORDLEFTEND:
4907 case SCI_WORDLEFTENDEXTEND:
4908 case SCI_WORDRIGHTEND:
4909 case SCI_WORDRIGHTENDEXTEND:
4910 case SCI_HOME:
4911 case SCI_HOMEEXTEND:
4912 case SCI_LINEEND:
4913 case SCI_LINEENDEXTEND:
4914 case SCI_HOMEWRAP:
4915 case SCI_HOMEWRAPEXTEND:
4916 case SCI_LINEENDWRAP:
4917 case SCI_LINEENDWRAPEXTEND:
4918 case SCI_DOCUMENTSTART:
4919 case SCI_DOCUMENTSTARTEXTEND:
4920 case SCI_DOCUMENTEND:
4921 case SCI_DOCUMENTENDEXTEND:
4922 case SCI_STUTTEREDPAGEUP:
4923 case SCI_STUTTEREDPAGEUPEXTEND:
4924 case SCI_STUTTEREDPAGEDOWN:
4925 case SCI_STUTTEREDPAGEDOWNEXTEND:
4926 case SCI_PAGEUP:
4927 case SCI_PAGEUPEXTEND:
4928 case SCI_PAGEDOWN:
4929 case SCI_PAGEDOWNEXTEND:
4930 case SCI_EDITTOGGLEOVERTYPE:
4931 case SCI_CANCEL:
4932 case SCI_DELETEBACK:
4933 case SCI_TAB:
4934 case SCI_BACKTAB:
4935 case SCI_FORMFEED:
4936 case SCI_VCHOME:
4937 case SCI_VCHOMEEXTEND:
4938 case SCI_VCHOMEWRAP:
4939 case SCI_VCHOMEWRAPEXTEND:
4940 case SCI_VCHOMEDISPLAY:
4941 case SCI_VCHOMEDISPLAYEXTEND:
4942 case SCI_DELWORDLEFT:
4943 case SCI_DELWORDRIGHT:
4944 case SCI_DELWORDRIGHTEND:
4945 case SCI_DELLINELEFT:
4946 case SCI_DELLINERIGHT:
4947 case SCI_LINECOPY:
4948 case SCI_LINECUT:
4949 case SCI_LINEDELETE:
4950 case SCI_LINETRANSPOSE:
4951 case SCI_LINEDUPLICATE:
4952 case SCI_LOWERCASE:
4953 case SCI_UPPERCASE:
4954 case SCI_LINESCROLLDOWN:
4955 case SCI_LINESCROLLUP:
4956 case SCI_DELETEBACKNOTLINE:
4957 case SCI_HOMEDISPLAY:
4958 case SCI_HOMEDISPLAYEXTEND:
4959 case SCI_LINEENDDISPLAY:
4960 case SCI_LINEENDDISPLAYEXTEND:
4961 case SCI_SETSELECTIONMODE:
4962 case SCI_LINEDOWNRECTEXTEND:
4963 case SCI_LINEUPRECTEXTEND:
4964 case SCI_CHARLEFTRECTEXTEND:
4965 case SCI_CHARRIGHTRECTEXTEND:
4966 case SCI_HOMERECTEXTEND:
4967 case SCI_VCHOMERECTEXTEND:
4968 case SCI_LINEENDRECTEXTEND:
4969 case SCI_PAGEUPRECTEXTEND:
4970 case SCI_PAGEDOWNRECTEXTEND:
4971 case SCI_SELECTIONDUPLICATE:
4972 case SCI_COPYALLOWLINE:
4973 case SCI_VERTICALCENTRECARET:
4974 case SCI_MOVESELECTEDLINESUP:
4975 case SCI_MOVESELECTEDLINESDOWN:
4976 case SCI_SCROLLTOSTART:
4977 case SCI_SCROLLTOEND:
4978 break;
4980 // Filter out all others like display changes. Also, newlines are redundant
4981 // with char insert messages.
4982 case SCI_NEWLINE:
4983 default:
4984 // printf("Filtered out %ld of macro recording\n", iMessage);
4985 return;
4988 // Send notification
4989 SCNotification scn = {};
4990 scn.nmhdr.code = SCN_MACRORECORD;
4991 scn.message = iMessage;
4992 scn.wParam = wParam;
4993 scn.lParam = lParam;
4994 NotifyParent(scn);
4997 // Something has changed that the container should know about
4998 void Editor::ContainerNeedsUpdate(int flags) {
4999 needUpdateUI |= flags;
5003 * Force scroll and keep position relative to top of window.
5005 * If stuttered = true and not already at first/last row, move to first/last row of window.
5006 * If stuttered = true and already at first/last row, scroll as normal.
5008 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
5009 int topLineNew;
5010 SelectionPosition newPos;
5012 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
5013 int topStutterLine = topLine + caretYSlop;
5014 int bottomStutterLine =
5015 pdoc->LineFromPosition(PositionFromLocation(
5016 Point::FromInts(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
5017 - caretYSlop - 1;
5019 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
5020 topLineNew = topLine;
5021 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
5022 false, false, UserVirtualSpace());
5024 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
5025 topLineNew = topLine;
5026 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
5027 false, false, UserVirtualSpace());
5029 } else {
5030 Point pt = LocationFromPosition(sel.MainCaret());
5032 topLineNew = Platform::Clamp(
5033 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
5034 newPos = SPositionFromLocation(
5035 Point::FromInts(lastXChosen - xOffset, static_cast<int>(pt.y) + direction * (vs.lineHeight * LinesToScroll())),
5036 false, false, UserVirtualSpace());
5039 if (topLineNew != topLine) {
5040 SetTopLine(topLineNew);
5041 MovePositionTo(newPos, selt);
5042 Redraw();
5043 SetVerticalScrollPos();
5044 } else {
5045 MovePositionTo(newPos, selt);
5049 void Editor::ChangeCaseOfSelection(int caseMapping) {
5050 UndoGroup ug(pdoc);
5051 for (size_t r=0; r<sel.Count(); r++) {
5052 SelectionRange current = sel.Range(r);
5053 SelectionRange currentNoVS = current;
5054 currentNoVS.ClearVirtualSpace();
5055 size_t rangeBytes = currentNoVS.Length();
5056 if (rangeBytes > 0) {
5057 std::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position());
5059 std::string sMapped = CaseMapString(sText, caseMapping);
5061 if (sMapped != sText) {
5062 size_t firstDifference = 0;
5063 while (sMapped[firstDifference] == sText[firstDifference])
5064 firstDifference++;
5065 size_t lastDifferenceText = sText.size() - 1;
5066 size_t lastDifferenceMapped = sMapped.size() - 1;
5067 while (sMapped[lastDifferenceMapped] == sText[lastDifferenceText]) {
5068 lastDifferenceText--;
5069 lastDifferenceMapped--;
5071 size_t endDifferenceText = sText.size() - 1 - lastDifferenceText;
5072 pdoc->DeleteChars(
5073 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
5074 static_cast<int>(rangeBytes - firstDifference - endDifferenceText));
5075 const int lengthChange = static_cast<int>(lastDifferenceMapped - firstDifference + 1);
5076 const int lengthInserted = pdoc->InsertString(
5077 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
5078 sMapped.c_str() + firstDifference,
5079 lengthChange);
5080 // Automatic movement changes selection so reset to exactly the same as it was.
5081 int diffSizes = static_cast<int>(sMapped.size() - sText.size()) + lengthInserted - lengthChange;
5082 if (diffSizes != 0) {
5083 if (current.anchor > current.caret)
5084 current.anchor.Add(diffSizes);
5085 else
5086 current.caret.Add(diffSizes);
5088 sel.Range(r) = current;
5094 void Editor::LineTranspose() {
5095 int line = pdoc->LineFromPosition(sel.MainCaret());
5096 if (line > 0) {
5097 UndoGroup ug(pdoc);
5099 const int startPrevious = pdoc->LineStart(line - 1);
5100 const std::string linePrevious = RangeText(startPrevious, pdoc->LineEnd(line - 1));
5102 int startCurrent = pdoc->LineStart(line);
5103 const std::string lineCurrent = RangeText(startCurrent, pdoc->LineEnd(line));
5105 pdoc->DeleteChars(startCurrent, static_cast<int>(lineCurrent.length()));
5106 pdoc->DeleteChars(startPrevious, static_cast<int>(linePrevious.length()));
5107 startCurrent -= static_cast<int>(linePrevious.length());
5109 startCurrent += pdoc->InsertString(startPrevious, lineCurrent.c_str(),
5110 static_cast<int>(lineCurrent.length()));
5111 pdoc->InsertString(startCurrent, linePrevious.c_str(),
5112 static_cast<int>(linePrevious.length()));
5113 // Move caret to start of current line
5114 MovePositionTo(SelectionPosition(startCurrent));
5118 void Editor::Duplicate(bool forLine) {
5119 if (sel.Empty()) {
5120 forLine = true;
5122 UndoGroup ug(pdoc);
5123 const char *eol = "";
5124 int eolLen = 0;
5125 if (forLine) {
5126 eol = StringFromEOLMode(pdoc->eolMode);
5127 eolLen = istrlen(eol);
5129 for (size_t r=0; r<sel.Count(); r++) {
5130 SelectionPosition start = sel.Range(r).Start();
5131 SelectionPosition end = sel.Range(r).End();
5132 if (forLine) {
5133 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
5134 start = SelectionPosition(pdoc->LineStart(line));
5135 end = SelectionPosition(pdoc->LineEnd(line));
5137 std::string text = RangeText(start.Position(), end.Position());
5138 int lengthInserted = eolLen;
5139 if (forLine)
5140 lengthInserted = pdoc->InsertString(end.Position(), eol, eolLen);
5141 pdoc->InsertString(end.Position() + lengthInserted, text.c_str(), static_cast<int>(text.length()));
5143 if (sel.Count() && sel.IsRectangular()) {
5144 SelectionPosition last = sel.Last();
5145 if (forLine) {
5146 int line = pdoc->LineFromPosition(last.Position());
5147 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
5149 if (sel.Rectangular().anchor > sel.Rectangular().caret)
5150 sel.Rectangular().anchor = last;
5151 else
5152 sel.Rectangular().caret = last;
5153 SetRectangularRange();
5157 void Editor::CancelModes() {
5158 sel.SetMoveExtends(false);
5161 void Editor::NewLine() {
5162 // Remove non-main ranges
5163 InvalidateSelection(sel.RangeMain(), true);
5164 sel.SetSelection(sel.RangeMain());
5165 sel.RangeMain().ClearVirtualSpace();
5167 // Clear main range and insert line end
5168 bool needGroupUndo = !sel.Empty();
5169 if (needGroupUndo)
5170 pdoc->BeginUndoAction();
5172 if (!sel.Empty())
5173 ClearSelection();
5174 const char *eol = "\n";
5175 if (pdoc->eolMode == SC_EOL_CRLF) {
5176 eol = "\r\n";
5177 } else if (pdoc->eolMode == SC_EOL_CR) {
5178 eol = "\r";
5179 } // else SC_EOL_LF -> "\n" already set
5180 const int insertLength = pdoc->InsertString(sel.MainCaret(), eol, istrlen(eol));
5181 // Want to end undo group before NotifyChar as applications often modify text here
5182 if (needGroupUndo)
5183 pdoc->EndUndoAction();
5184 if (insertLength > 0) {
5185 SetEmptySelection(sel.MainCaret() + insertLength);
5186 while (*eol) {
5187 NotifyChar(*eol);
5188 if (recordingMacro) {
5189 char txt[2];
5190 txt[0] = *eol;
5191 txt[1] = '\0';
5192 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
5194 eol++;
5197 SetLastXChosen();
5198 SetScrollBars();
5199 EnsureCaretVisible();
5200 // Avoid blinking during rapid typing:
5201 ShowCaretAtCurrentPosition();
5204 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
5205 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
5206 if (sel.IsRectangular()) {
5207 if (selt == Selection::noSel) {
5208 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
5209 } else {
5210 caretToUse = sel.Rectangular().caret;
5214 Point pt = LocationFromPosition(caretToUse);
5215 int skipLines = 0;
5217 if (vs.annotationVisible) {
5218 int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
5219 Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
5220 int subLine = static_cast<int>(pt.y - ptStartLine.y) / vs.lineHeight;
5222 if (direction < 0 && subLine == 0) {
5223 int lineDisplay = cs.DisplayFromDoc(lineDoc);
5224 if (lineDisplay > 0) {
5225 skipLines = pdoc->AnnotationLines(cs.DocFromDisplay(lineDisplay - 1));
5227 } else if (direction > 0 && subLine >= (cs.GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
5228 skipLines = pdoc->AnnotationLines(lineDoc);
5232 int newY = static_cast<int>(pt.y) + (1 + skipLines) * direction * vs.lineHeight;
5233 SelectionPosition posNew = SPositionFromLocation(
5234 Point::FromInts(lastXChosen - xOffset, newY), false, false, UserVirtualSpace());
5236 if (direction < 0) {
5237 // Line wrapping may lead to a location on the same line, so
5238 // seek back if that is the case.
5239 Point ptNew = LocationFromPosition(posNew.Position());
5240 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
5241 posNew.Add(-1);
5242 posNew.SetVirtualSpace(0);
5243 ptNew = LocationFromPosition(posNew.Position());
5245 } else if (direction > 0 && posNew.Position() != pdoc->Length()) {
5246 // There is an equivalent case when moving down which skips
5247 // over a line.
5248 Point ptNew = LocationFromPosition(posNew.Position());
5249 while ((posNew.Position() > caretToUse.Position()) && (ptNew.y > newY)) {
5250 posNew.Add(-1);
5251 posNew.SetVirtualSpace(0);
5252 ptNew = LocationFromPosition(posNew.Position());
5256 MovePositionTo(MovePositionSoVisible(posNew, direction), selt);
5259 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
5260 int lineDoc, savedPos = sel.MainCaret();
5261 do {
5262 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
5263 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
5264 if (direction > 0) {
5265 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
5266 if (selt == Selection::noSel) {
5267 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
5269 break;
5272 } while (!cs.GetVisible(lineDoc));
5275 int Editor::StartEndDisplayLine(int pos, bool start) {
5276 RefreshStyleData();
5277 int line = pdoc->LineFromPosition(pos);
5278 AutoSurface surface(this);
5279 AutoLineLayout ll(llc, RetrieveLineLayout(line));
5280 int posRet = INVALID_POSITION;
5281 if (surface && ll) {
5282 unsigned int posLineStart = pdoc->LineStart(line);
5283 LayoutLine(line, surface, vs, ll, wrapWidth);
5284 int posInLine = pos - posLineStart;
5285 if (posInLine <= ll->maxLineLength) {
5286 for (int subLine = 0; subLine < ll->lines; subLine++) {
5287 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
5288 if (start) {
5289 posRet = ll->LineStart(subLine) + posLineStart;
5290 } else {
5291 if (subLine == ll->lines - 1)
5292 posRet = ll->LineStart(subLine + 1) + posLineStart;
5293 else
5294 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
5300 if (posRet == INVALID_POSITION) {
5301 return pos;
5302 } else {
5303 return posRet;
5307 int Editor::KeyCommand(unsigned int iMessage) {
5308 switch (iMessage) {
5309 case SCI_LINEDOWN:
5310 CursorUpOrDown(1);
5311 break;
5312 case SCI_LINEDOWNEXTEND:
5313 CursorUpOrDown(1, Selection::selStream);
5314 break;
5315 case SCI_LINEDOWNRECTEXTEND:
5316 CursorUpOrDown(1, Selection::selRectangle);
5317 break;
5318 case SCI_PARADOWN:
5319 ParaUpOrDown(1);
5320 break;
5321 case SCI_PARADOWNEXTEND:
5322 ParaUpOrDown(1, Selection::selStream);
5323 break;
5324 case SCI_LINESCROLLDOWN:
5325 ScrollTo(topLine + 1);
5326 MoveCaretInsideView(false);
5327 break;
5328 case SCI_LINEUP:
5329 CursorUpOrDown(-1);
5330 break;
5331 case SCI_LINEUPEXTEND:
5332 CursorUpOrDown(-1, Selection::selStream);
5333 break;
5334 case SCI_LINEUPRECTEXTEND:
5335 CursorUpOrDown(-1, Selection::selRectangle);
5336 break;
5337 case SCI_PARAUP:
5338 ParaUpOrDown(-1);
5339 break;
5340 case SCI_PARAUPEXTEND:
5341 ParaUpOrDown(-1, Selection::selStream);
5342 break;
5343 case SCI_LINESCROLLUP:
5344 ScrollTo(topLine - 1);
5345 MoveCaretInsideView(false);
5346 break;
5347 case SCI_CHARLEFT:
5348 if (SelectionEmpty() || sel.MoveExtends()) {
5349 if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5350 SelectionPosition spCaret = sel.RangeMain().caret;
5351 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5352 MovePositionTo(spCaret);
5353 } else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
5354 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1));
5355 } else {
5356 MovePositionTo(MovePositionSoVisible(
5357 SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
5359 } else {
5360 MovePositionTo(sel.LimitsForRectangularElseMain().start);
5362 SetLastXChosen();
5363 break;
5364 case SCI_CHARLEFTEXTEND:
5365 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5366 SelectionPosition spCaret = sel.RangeMain().caret;
5367 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5368 MovePositionTo(spCaret, Selection::selStream);
5369 } else {
5370 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
5372 SetLastXChosen();
5373 break;
5374 case SCI_CHARLEFTRECTEXTEND:
5375 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5376 SelectionPosition spCaret = sel.RangeMain().caret;
5377 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5378 MovePositionTo(spCaret, Selection::selRectangle);
5379 } else {
5380 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
5382 SetLastXChosen();
5383 break;
5384 case SCI_CHARRIGHT:
5385 if (SelectionEmpty() || sel.MoveExtends()) {
5386 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5387 SelectionPosition spCaret = sel.RangeMain().caret;
5388 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5389 MovePositionTo(spCaret);
5390 } else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
5391 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1));
5392 } else {
5393 MovePositionTo(MovePositionSoVisible(
5394 SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
5396 } else {
5397 MovePositionTo(sel.LimitsForRectangularElseMain().end);
5399 SetLastXChosen();
5400 break;
5401 case SCI_CHARRIGHTEXTEND:
5402 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5403 SelectionPosition spCaret = sel.RangeMain().caret;
5404 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5405 MovePositionTo(spCaret, Selection::selStream);
5406 } else {
5407 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
5409 SetLastXChosen();
5410 break;
5411 case SCI_CHARRIGHTRECTEXTEND:
5412 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5413 SelectionPosition spCaret = sel.RangeMain().caret;
5414 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5415 MovePositionTo(spCaret, Selection::selRectangle);
5416 } else {
5417 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
5419 SetLastXChosen();
5420 break;
5421 case SCI_WORDLEFT:
5422 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
5423 SetLastXChosen();
5424 break;
5425 case SCI_WORDLEFTEXTEND:
5426 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
5427 SetLastXChosen();
5428 break;
5429 case SCI_WORDRIGHT:
5430 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
5431 SetLastXChosen();
5432 break;
5433 case SCI_WORDRIGHTEXTEND:
5434 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
5435 SetLastXChosen();
5436 break;
5438 case SCI_WORDLEFTEND:
5439 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
5440 SetLastXChosen();
5441 break;
5442 case SCI_WORDLEFTENDEXTEND:
5443 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
5444 SetLastXChosen();
5445 break;
5446 case SCI_WORDRIGHTEND:
5447 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
5448 SetLastXChosen();
5449 break;
5450 case SCI_WORDRIGHTENDEXTEND:
5451 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
5452 SetLastXChosen();
5453 break;
5455 case SCI_HOME:
5456 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5457 SetLastXChosen();
5458 break;
5459 case SCI_HOMEEXTEND:
5460 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
5461 SetLastXChosen();
5462 break;
5463 case SCI_HOMERECTEXTEND:
5464 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
5465 SetLastXChosen();
5466 break;
5467 case SCI_LINEEND:
5468 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
5469 SetLastXChosen();
5470 break;
5471 case SCI_LINEENDEXTEND:
5472 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
5473 SetLastXChosen();
5474 break;
5475 case SCI_LINEENDRECTEXTEND:
5476 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
5477 SetLastXChosen();
5478 break;
5479 case SCI_HOMEWRAP: {
5480 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5481 if (sel.RangeMain().caret <= homePos)
5482 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5483 MovePositionTo(homePos);
5484 SetLastXChosen();
5486 break;
5487 case SCI_HOMEWRAPEXTEND: {
5488 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5489 if (sel.RangeMain().caret <= homePos)
5490 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5491 MovePositionTo(homePos, Selection::selStream);
5492 SetLastXChosen();
5494 break;
5495 case SCI_LINEENDWRAP: {
5496 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5497 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5498 if (endPos > realEndPos // if moved past visible EOLs
5499 || sel.RangeMain().caret >= endPos) // if at end of display line already
5500 endPos = realEndPos;
5501 MovePositionTo(endPos);
5502 SetLastXChosen();
5504 break;
5505 case SCI_LINEENDWRAPEXTEND: {
5506 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5507 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5508 if (endPos > realEndPos // if moved past visible EOLs
5509 || sel.RangeMain().caret >= endPos) // if at end of display line already
5510 endPos = realEndPos;
5511 MovePositionTo(endPos, Selection::selStream);
5512 SetLastXChosen();
5514 break;
5515 case SCI_DOCUMENTSTART:
5516 MovePositionTo(0);
5517 SetLastXChosen();
5518 break;
5519 case SCI_DOCUMENTSTARTEXTEND:
5520 MovePositionTo(0, Selection::selStream);
5521 SetLastXChosen();
5522 break;
5523 case SCI_DOCUMENTEND:
5524 MovePositionTo(pdoc->Length());
5525 SetLastXChosen();
5526 break;
5527 case SCI_DOCUMENTENDEXTEND:
5528 MovePositionTo(pdoc->Length(), Selection::selStream);
5529 SetLastXChosen();
5530 break;
5531 case SCI_STUTTEREDPAGEUP:
5532 PageMove(-1, Selection::noSel, true);
5533 break;
5534 case SCI_STUTTEREDPAGEUPEXTEND:
5535 PageMove(-1, Selection::selStream, true);
5536 break;
5537 case SCI_STUTTEREDPAGEDOWN:
5538 PageMove(1, Selection::noSel, true);
5539 break;
5540 case SCI_STUTTEREDPAGEDOWNEXTEND:
5541 PageMove(1, Selection::selStream, true);
5542 break;
5543 case SCI_PAGEUP:
5544 PageMove(-1);
5545 break;
5546 case SCI_PAGEUPEXTEND:
5547 PageMove(-1, Selection::selStream);
5548 break;
5549 case SCI_PAGEUPRECTEXTEND:
5550 PageMove(-1, Selection::selRectangle);
5551 break;
5552 case SCI_PAGEDOWN:
5553 PageMove(1);
5554 break;
5555 case SCI_PAGEDOWNEXTEND:
5556 PageMove(1, Selection::selStream);
5557 break;
5558 case SCI_PAGEDOWNRECTEXTEND:
5559 PageMove(1, Selection::selRectangle);
5560 break;
5561 case SCI_EDITTOGGLEOVERTYPE:
5562 inOverstrike = !inOverstrike;
5563 DropCaret();
5564 ShowCaretAtCurrentPosition();
5565 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
5566 NotifyUpdateUI();
5567 break;
5568 case SCI_CANCEL: // Cancel any modes - handled in subclass
5569 // Also unselect text
5570 CancelModes();
5571 break;
5572 case SCI_DELETEBACK:
5573 DelCharBack(true);
5574 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5575 SetLastXChosen();
5577 EnsureCaretVisible();
5578 break;
5579 case SCI_DELETEBACKNOTLINE:
5580 DelCharBack(false);
5581 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5582 SetLastXChosen();
5584 EnsureCaretVisible();
5585 break;
5586 case SCI_TAB:
5587 Indent(true);
5588 if (caretSticky == SC_CARETSTICKY_OFF) {
5589 SetLastXChosen();
5591 EnsureCaretVisible();
5592 ShowCaretAtCurrentPosition(); // Avoid blinking
5593 break;
5594 case SCI_BACKTAB:
5595 Indent(false);
5596 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5597 SetLastXChosen();
5599 EnsureCaretVisible();
5600 ShowCaretAtCurrentPosition(); // Avoid blinking
5601 break;
5602 case SCI_NEWLINE:
5603 NewLine();
5604 break;
5605 case SCI_FORMFEED:
5606 AddChar('\f');
5607 break;
5608 case SCI_VCHOME:
5609 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
5610 SetLastXChosen();
5611 break;
5612 case SCI_VCHOMEEXTEND:
5613 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
5614 SetLastXChosen();
5615 break;
5616 case SCI_VCHOMERECTEXTEND:
5617 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
5618 SetLastXChosen();
5619 break;
5620 case SCI_VCHOMEWRAP: {
5621 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5622 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5623 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5624 homePos = viewLineStart;
5626 MovePositionTo(homePos);
5627 SetLastXChosen();
5629 break;
5630 case SCI_VCHOMEWRAPEXTEND: {
5631 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5632 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5633 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5634 homePos = viewLineStart;
5636 MovePositionTo(homePos, Selection::selStream);
5637 SetLastXChosen();
5639 break;
5640 case SCI_ZOOMIN:
5641 if (vs.zoomLevel < 20) {
5642 vs.zoomLevel++;
5643 InvalidateStyleRedraw();
5644 NotifyZoom();
5646 break;
5647 case SCI_ZOOMOUT:
5648 if (vs.zoomLevel > -10) {
5649 vs.zoomLevel--;
5650 InvalidateStyleRedraw();
5651 NotifyZoom();
5653 break;
5654 case SCI_DELWORDLEFT: {
5655 int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
5656 pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
5657 sel.RangeMain().ClearVirtualSpace();
5658 SetLastXChosen();
5660 break;
5661 case SCI_DELWORDRIGHT: {
5662 UndoGroup ug(pdoc);
5663 sel.RangeMain().caret = SelectionPosition(
5664 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5665 sel.RangeMain().anchor = sel.RangeMain().caret;
5666 int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
5667 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5669 break;
5670 case SCI_DELWORDRIGHTEND: {
5671 UndoGroup ug(pdoc);
5672 sel.RangeMain().caret = SelectionPosition(
5673 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5674 int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
5675 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5677 break;
5678 case SCI_DELLINELEFT: {
5679 int line = pdoc->LineFromPosition(sel.MainCaret());
5680 int start = pdoc->LineStart(line);
5681 pdoc->DeleteChars(start, sel.MainCaret() - start);
5682 sel.RangeMain().ClearVirtualSpace();
5683 SetLastXChosen();
5685 break;
5686 case SCI_DELLINERIGHT: {
5687 int line = pdoc->LineFromPosition(sel.MainCaret());
5688 int end = pdoc->LineEnd(line);
5689 pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
5691 break;
5692 case SCI_LINECOPY: {
5693 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5694 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5695 CopyRangeToClipboard(pdoc->LineStart(lineStart),
5696 pdoc->LineStart(lineEnd + 1));
5698 break;
5699 case SCI_LINECUT: {
5700 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5701 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5702 int start = pdoc->LineStart(lineStart);
5703 int end = pdoc->LineStart(lineEnd + 1);
5704 SetSelection(start, end);
5705 Cut();
5706 SetLastXChosen();
5708 break;
5709 case SCI_LINEDELETE: {
5710 int line = pdoc->LineFromPosition(sel.MainCaret());
5711 int start = pdoc->LineStart(line);
5712 int end = pdoc->LineStart(line + 1);
5713 pdoc->DeleteChars(start, end - start);
5715 break;
5716 case SCI_LINETRANSPOSE:
5717 LineTranspose();
5718 break;
5719 case SCI_LINEDUPLICATE:
5720 Duplicate(true);
5721 break;
5722 case SCI_SELECTIONDUPLICATE:
5723 Duplicate(false);
5724 break;
5725 case SCI_LOWERCASE:
5726 ChangeCaseOfSelection(cmLower);
5727 break;
5728 case SCI_UPPERCASE:
5729 ChangeCaseOfSelection(cmUpper);
5730 break;
5731 case SCI_WORDPARTLEFT:
5732 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
5733 SetLastXChosen();
5734 break;
5735 case SCI_WORDPARTLEFTEXTEND:
5736 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
5737 SetLastXChosen();
5738 break;
5739 case SCI_WORDPARTRIGHT:
5740 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
5741 SetLastXChosen();
5742 break;
5743 case SCI_WORDPARTRIGHTEXTEND:
5744 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
5745 SetLastXChosen();
5746 break;
5747 case SCI_HOMEDISPLAY:
5748 MovePositionTo(MovePositionSoVisible(
5749 StartEndDisplayLine(sel.MainCaret(), true), -1));
5750 SetLastXChosen();
5751 break;
5752 case SCI_VCHOMEDISPLAY: {
5753 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5754 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5755 if (viewLineStart > homePos)
5756 homePos = viewLineStart;
5758 MovePositionTo(homePos);
5759 SetLastXChosen();
5761 break;
5762 case SCI_HOMEDISPLAYEXTEND:
5763 MovePositionTo(MovePositionSoVisible(
5764 StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
5765 SetLastXChosen();
5766 break;
5767 case SCI_VCHOMEDISPLAYEXTEND: {
5768 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5769 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5770 if (viewLineStart > homePos)
5771 homePos = viewLineStart;
5773 MovePositionTo(homePos, Selection::selStream);
5774 SetLastXChosen();
5776 break;
5777 case SCI_LINEENDDISPLAY:
5778 MovePositionTo(MovePositionSoVisible(
5779 StartEndDisplayLine(sel.MainCaret(), false), 1));
5780 SetLastXChosen();
5781 break;
5782 case SCI_LINEENDDISPLAYEXTEND:
5783 MovePositionTo(MovePositionSoVisible(
5784 StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
5785 SetLastXChosen();
5786 break;
5787 case SCI_SCROLLTOSTART:
5788 ScrollTo(0);
5789 break;
5790 case SCI_SCROLLTOEND:
5791 ScrollTo(MaxScrollPos());
5792 break;
5794 return 0;
5797 int Editor::KeyDefault(int, int) {
5798 return 0;
5801 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
5802 DwellEnd(false);
5803 int msg = kmap.Find(key, modifiers);
5804 if (msg) {
5805 if (consumed)
5806 *consumed = true;
5807 return static_cast<int>(WndProc(msg, 0, 0));
5808 } else {
5809 if (consumed)
5810 *consumed = false;
5811 return KeyDefault(key, modifiers);
5815 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
5816 return KeyDownWithModifiers(key, ModifierFlags(shift, ctrl, alt), consumed);
5819 void Editor::Indent(bool forwards) {
5820 UndoGroup ug(pdoc);
5821 for (size_t r=0; r<sel.Count(); r++) {
5822 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
5823 int caretPosition = sel.Range(r).caret.Position();
5824 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
5825 if (lineOfAnchor == lineCurrentPos) {
5826 if (forwards) {
5827 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
5828 caretPosition = sel.Range(r).caret.Position();
5829 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
5830 pdoc->tabIndents) {
5831 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5832 int indentationStep = pdoc->IndentSize();
5833 const int posSelect = pdoc->SetLineIndentation(
5834 lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
5835 sel.Range(r) = SelectionRange(posSelect);
5836 } else {
5837 if (pdoc->useTabs) {
5838 const int lengthInserted = pdoc->InsertString(caretPosition, "\t", 1);
5839 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
5840 } else {
5841 int numSpaces = (pdoc->tabInChars) -
5842 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
5843 if (numSpaces < 1)
5844 numSpaces = pdoc->tabInChars;
5845 const std::string spaceText(numSpaces, ' ');
5846 const int lengthInserted = pdoc->InsertString(caretPosition, spaceText.c_str(),
5847 static_cast<int>(spaceText.length()));
5848 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
5851 } else {
5852 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
5853 pdoc->tabIndents) {
5854 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5855 int indentationStep = pdoc->IndentSize();
5856 const int posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
5857 sel.Range(r) = SelectionRange(posSelect);
5858 } else {
5859 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
5860 pdoc->tabInChars;
5861 if (newColumn < 0)
5862 newColumn = 0;
5863 int newPos = caretPosition;
5864 while (pdoc->GetColumn(newPos) > newColumn)
5865 newPos--;
5866 sel.Range(r) = SelectionRange(newPos);
5869 } else { // Multiline
5870 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
5871 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
5872 // Multiple lines selected so indent / dedent
5873 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
5874 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
5875 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
5876 lineBottomSel--; // If not selecting any characters on a line, do not indent
5877 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
5878 if (lineOfAnchor < lineCurrentPos) {
5879 if (currentPosPosOnLine == 0)
5880 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5881 else
5882 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
5883 } else {
5884 if (anchorPosOnLine == 0)
5885 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5886 else
5887 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
5891 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
5894 class CaseFolderASCII : public CaseFolderTable {
5895 public:
5896 CaseFolderASCII() {
5897 StandardASCII();
5899 ~CaseFolderASCII() {
5904 CaseFolder *Editor::CaseFolderForEncoding() {
5905 // Simple default that only maps ASCII upper case to lower case.
5906 return new CaseFolderASCII();
5910 * Search of a text in the document, in the given range.
5911 * @return The position of the found text, -1 if not found.
5913 long Editor::FindText(
5914 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5915 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5916 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
5918 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
5919 int lengthFound = istrlen(ft->lpstrText);
5920 if (!pdoc->HasCaseFolder())
5921 pdoc->SetCaseFolder(CaseFolderForEncoding());
5922 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
5923 (wParam & SCFIND_MATCHCASE) != 0,
5924 (wParam & SCFIND_WHOLEWORD) != 0,
5925 (wParam & SCFIND_WORDSTART) != 0,
5926 (wParam & SCFIND_REGEXP) != 0,
5927 static_cast<int>(wParam),
5928 &lengthFound);
5929 if (pos != -1) {
5930 ft->chrgText.cpMin = pos;
5931 ft->chrgText.cpMax = pos + lengthFound;
5933 return pos;
5937 * Relocatable search support : Searches relative to current selection
5938 * point and sets the selection to the found text range with
5939 * each search.
5942 * Anchor following searches at current selection start: This allows
5943 * multiple incremental interactive searches to be macro recorded
5944 * while still setting the selection to found text so the find/select
5945 * operation is self-contained.
5947 void Editor::SearchAnchor() {
5948 searchAnchor = SelectionStart().Position();
5952 * Find text from current search anchor: Must call @c SearchAnchor first.
5953 * Used for next text and previous text requests.
5954 * @return The position of the found text, -1 if not found.
5956 long Editor::SearchText(
5957 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
5958 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5959 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5960 sptr_t lParam) { ///< The text to search for.
5962 const char *txt = reinterpret_cast<char *>(lParam);
5963 int pos;
5964 int lengthFound = istrlen(txt);
5965 if (!pdoc->HasCaseFolder())
5966 pdoc->SetCaseFolder(CaseFolderForEncoding());
5967 if (iMessage == SCI_SEARCHNEXT) {
5968 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
5969 (wParam & SCFIND_MATCHCASE) != 0,
5970 (wParam & SCFIND_WHOLEWORD) != 0,
5971 (wParam & SCFIND_WORDSTART) != 0,
5972 (wParam & SCFIND_REGEXP) != 0,
5973 static_cast<int>(wParam),
5974 &lengthFound);
5975 } else {
5976 pos = pdoc->FindText(searchAnchor, 0, txt,
5977 (wParam & SCFIND_MATCHCASE) != 0,
5978 (wParam & SCFIND_WHOLEWORD) != 0,
5979 (wParam & SCFIND_WORDSTART) != 0,
5980 (wParam & SCFIND_REGEXP) != 0,
5981 static_cast<int>(wParam),
5982 &lengthFound);
5984 if (pos != -1) {
5985 SetSelection(pos, pos + lengthFound);
5988 return pos;
5991 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
5992 std::string ret(s);
5993 for (size_t i=0; i<ret.size(); i++) {
5994 switch (caseMapping) {
5995 case cmUpper:
5996 if (ret[i] >= 'a' && ret[i] <= 'z')
5997 ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
5998 break;
5999 case cmLower:
6000 if (ret[i] >= 'A' && ret[i] <= 'Z')
6001 ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
6002 break;
6005 return ret;
6009 * Search for text in the target range of the document.
6010 * @return The position of the found text, -1 if not found.
6012 long Editor::SearchInTarget(const char *text, int length) {
6013 int lengthFound = length;
6015 if (!pdoc->HasCaseFolder())
6016 pdoc->SetCaseFolder(CaseFolderForEncoding());
6017 int pos = pdoc->FindText(targetStart, targetEnd, text,
6018 (searchFlags & SCFIND_MATCHCASE) != 0,
6019 (searchFlags & SCFIND_WHOLEWORD) != 0,
6020 (searchFlags & SCFIND_WORDSTART) != 0,
6021 (searchFlags & SCFIND_REGEXP) != 0,
6022 searchFlags,
6023 &lengthFound);
6024 if (pos != -1) {
6025 targetStart = pos;
6026 targetEnd = pos + lengthFound;
6028 return pos;
6031 void Editor::GoToLine(int lineNo) {
6032 if (lineNo > pdoc->LinesTotal())
6033 lineNo = pdoc->LinesTotal();
6034 if (lineNo < 0)
6035 lineNo = 0;
6036 SetEmptySelection(pdoc->LineStart(lineNo));
6037 ShowCaretAtCurrentPosition();
6038 EnsureCaretVisible();
6041 static bool Close(Point pt1, Point pt2) {
6042 if (abs(pt1.x - pt2.x) > 3)
6043 return false;
6044 if (abs(pt1.y - pt2.y) > 3)
6045 return false;
6046 return true;
6049 std::string Editor::RangeText(int start, int end) const {
6050 if (start < end) {
6051 int len = end - start;
6052 std::string ret(len, '\0');
6053 for (int i = 0; i < len; i++) {
6054 ret[i] = pdoc->CharAt(start + i);
6056 return ret;
6058 return std::string();
6061 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
6062 if (sel.Empty()) {
6063 if (allowLineCopy) {
6064 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
6065 int start = pdoc->LineStart(currentLine);
6066 int end = pdoc->LineEnd(currentLine);
6068 std::string text = RangeText(start, end);
6069 if (pdoc->eolMode != SC_EOL_LF)
6070 text.push_back('\r');
6071 if (pdoc->eolMode != SC_EOL_CR)
6072 text.push_back('\n');
6073 ss->Copy(text, pdoc->dbcsCodePage,
6074 vs.styles[STYLE_DEFAULT].characterSet, false, true);
6076 } else {
6077 std::string text;
6078 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
6079 if (sel.selType == Selection::selRectangle)
6080 std::sort(rangesInOrder.begin(), rangesInOrder.end());
6081 for (size_t r=0; r<rangesInOrder.size(); r++) {
6082 SelectionRange current = rangesInOrder[r];
6083 text.append(RangeText(current.Start().Position(), current.End().Position()));
6084 if (sel.selType == Selection::selRectangle) {
6085 if (pdoc->eolMode != SC_EOL_LF)
6086 text.push_back('\r');
6087 if (pdoc->eolMode != SC_EOL_CR)
6088 text.push_back('\n');
6091 ss->Copy(text, pdoc->dbcsCodePage,
6092 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
6096 void Editor::CopyRangeToClipboard(int start, int end) {
6097 start = pdoc->ClampPositionIntoDocument(start);
6098 end = pdoc->ClampPositionIntoDocument(end);
6099 SelectionText selectedText;
6100 std::string text = RangeText(start, end);
6101 selectedText.Copy(text,
6102 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
6103 CopyToClipboard(selectedText);
6106 void Editor::CopyText(int length, const char *text) {
6107 SelectionText selectedText;
6108 selectedText.Copy(std::string(text, length),
6109 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
6110 CopyToClipboard(selectedText);
6113 void Editor::SetDragPosition(SelectionPosition newPos) {
6114 if (newPos.Position() >= 0) {
6115 newPos = MovePositionOutsideChar(newPos, 1);
6116 posDrop = newPos;
6118 if (!(posDrag == newPos)) {
6119 caret.on = true;
6120 SetTicking(true);
6121 InvalidateCaret();
6122 posDrag = newPos;
6123 InvalidateCaret();
6127 void Editor::DisplayCursor(Window::Cursor c) {
6128 if (cursorMode == SC_CURSORNORMAL)
6129 wMain.SetCursor(c);
6130 else
6131 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
6134 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
6135 int xMove = static_cast<int>(ptStart.x - ptNow.x);
6136 int yMove = static_cast<int>(ptStart.y - ptNow.y);
6137 int distanceSquared = xMove * xMove + yMove * yMove;
6138 return distanceSquared > 16;
6141 void Editor::StartDrag() {
6142 // Always handled by subclasses
6143 //SetMouseCapture(true);
6144 //DisplayCursor(Window::cursorArrow);
6147 void Editor::DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular) {
6148 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
6149 if (inDragDrop == ddDragging)
6150 dropWentOutside = false;
6152 bool positionWasInSelection = PositionInSelection(position.Position());
6154 bool positionOnEdgeOfSelection =
6155 (position == SelectionStart()) || (position == SelectionEnd());
6157 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
6158 (positionOnEdgeOfSelection && !moving)) {
6160 SelectionPosition selStart = SelectionStart();
6161 SelectionPosition selEnd = SelectionEnd();
6163 UndoGroup ug(pdoc);
6165 SelectionPosition positionAfterDeletion = position;
6166 if ((inDragDrop == ddDragging) && moving) {
6167 // Remove dragged out text
6168 if (rectangular || sel.selType == Selection::selLines) {
6169 for (size_t r=0; r<sel.Count(); r++) {
6170 if (position >= sel.Range(r).Start()) {
6171 if (position > sel.Range(r).End()) {
6172 positionAfterDeletion.Add(-sel.Range(r).Length());
6173 } else {
6174 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
6178 } else {
6179 if (position > selStart) {
6180 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
6183 ClearSelection();
6185 position = positionAfterDeletion;
6187 std::string convertedText = Document::TransformLineEnds(value, lengthValue, pdoc->eolMode);
6189 if (rectangular) {
6190 PasteRectangular(position, convertedText.c_str(), static_cast<int>(convertedText.length()));
6191 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
6192 SetEmptySelection(position);
6193 } else {
6194 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
6195 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
6196 const int lengthInserted = pdoc->InsertString(
6197 position.Position(), convertedText.c_str(), static_cast<int>(convertedText.length()));
6198 if (lengthInserted > 0) {
6199 SelectionPosition posAfterInsertion = position;
6200 posAfterInsertion.Add(lengthInserted);
6201 SetSelection(posAfterInsertion, position);
6204 } else if (inDragDrop == ddDragging) {
6205 SetEmptySelection(position);
6209 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
6210 DropAt(position, value, strlen(value), moving, rectangular);
6214 * @return true if given position is inside the selection,
6216 bool Editor::PositionInSelection(int pos) {
6217 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
6218 for (size_t r=0; r<sel.Count(); r++) {
6219 if (sel.Range(r).Contains(pos))
6220 return true;
6222 return false;
6225 bool Editor::PointInSelection(Point pt) {
6226 SelectionPosition pos = SPositionFromLocation(pt, false, true);
6227 Point ptPos = LocationFromPosition(pos);
6228 for (size_t r=0; r<sel.Count(); r++) {
6229 SelectionRange range = sel.Range(r);
6230 if (range.Contains(pos)) {
6231 bool hit = true;
6232 if (pos == range.Start()) {
6233 // see if just before selection
6234 if (pt.x < ptPos.x) {
6235 hit = false;
6238 if (pos == range.End()) {
6239 // see if just after selection
6240 if (pt.x > ptPos.x) {
6241 hit = false;
6244 if (hit)
6245 return true;
6248 return false;
6251 bool Editor::PointInSelMargin(Point pt) {
6252 // Really means: "Point in a margin"
6253 if (vs.fixedColumnWidth > 0) { // There is a margin
6254 PRectangle rcSelMargin = GetClientRectangle();
6255 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart - vs.leftMarginWidth);
6256 rcSelMargin.left = static_cast<XYPOSITION>(vs.textStart - vs.fixedColumnWidth);
6257 return rcSelMargin.Contains(pt);
6258 } else {
6259 return false;
6263 Window::Cursor Editor::GetMarginCursor(Point pt) const {
6264 int x = 0;
6265 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
6266 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
6267 return static_cast<Window::Cursor>(vs.ms[margin].cursor);
6268 x += vs.ms[margin].width;
6270 return Window::cursorReverseArrow;
6273 void Editor::TrimAndSetSelection(int currentPos_, int anchor_) {
6274 sel.TrimSelection(SelectionRange(currentPos_, anchor_));
6275 SetSelection(currentPos_, anchor_);
6278 void Editor::LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine) {
6279 int selCurrentPos, selAnchorPos;
6280 if (wholeLine) {
6281 int lineCurrent_ = pdoc->LineFromPosition(lineCurrentPos_);
6282 int lineAnchor_ = pdoc->LineFromPosition(lineAnchorPos_);
6283 if (lineAnchorPos_ < lineCurrentPos_) {
6284 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
6285 selAnchorPos = pdoc->LineStart(lineAnchor_);
6286 } else if (lineAnchorPos_ > lineCurrentPos_) {
6287 selCurrentPos = pdoc->LineStart(lineCurrent_);
6288 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
6289 } else { // Same line, select it
6290 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
6291 selAnchorPos = pdoc->LineStart(lineAnchor_);
6293 } else {
6294 if (lineAnchorPos_ < lineCurrentPos_) {
6295 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
6296 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6297 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6298 } else if (lineAnchorPos_ > lineCurrentPos_) {
6299 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
6300 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6301 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
6302 } else { // Same line, select it
6303 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6304 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6305 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6308 TrimAndSetSelection(selCurrentPos, selAnchorPos);
6311 void Editor::WordSelection(int pos) {
6312 if (pos < wordSelectAnchorStartPos) {
6313 // Extend backward to the word containing pos.
6314 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
6315 // This ensures that a series of empty lines isn't counted as a single "word".
6316 if (!pdoc->IsLineEndPosition(pos))
6317 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
6318 TrimAndSetSelection(pos, wordSelectAnchorEndPos);
6319 } else if (pos > wordSelectAnchorEndPos) {
6320 // Extend forward to the word containing the character to the left of pos.
6321 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
6322 // This ensures that a series of empty lines isn't counted as a single "word".
6323 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
6324 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
6325 TrimAndSetSelection(pos, wordSelectAnchorStartPos);
6326 } else {
6327 // Select only the anchored word
6328 if (pos >= originalAnchorPos)
6329 TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
6330 else
6331 TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
6335 void Editor::DwellEnd(bool mouseMoved) {
6336 if (mouseMoved)
6337 ticksToDwell = dwellDelay;
6338 else
6339 ticksToDwell = SC_TIME_FOREVER;
6340 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
6341 dwelling = false;
6342 NotifyDwelling(ptMouseLast, dwelling);
6346 void Editor::MouseLeave() {
6347 SetHotSpotRange(NULL);
6348 if (!HaveMouseCapture()) {
6349 ptMouseLast = Point(-1,-1);
6350 DwellEnd(true);
6354 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
6355 return (!rectangular && ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0))
6356 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
6359 void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) {
6360 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
6361 ptMouseLast = pt;
6362 const bool ctrl = (modifiers & SCI_CTRL) != 0;
6363 const bool shift = (modifiers & SCI_SHIFT) != 0;
6364 const bool alt = (modifiers & SCI_ALT) != 0;
6365 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
6366 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6367 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
6368 newCharPos = MovePositionOutsideChar(newCharPos, -1);
6369 inDragDrop = ddNone;
6370 sel.SetMoveExtends(false);
6372 if (NotifyMarginClick(pt, modifiers))
6373 return;
6375 NotifyIndicatorClick(true, newPos.Position(), modifiers);
6377 bool inSelMargin = PointInSelMargin(pt);
6378 // In margin ctrl+(double)click should always select everything
6379 if (ctrl && inSelMargin) {
6380 SelectAll();
6381 lastClickTime = curTime;
6382 lastClick = pt;
6383 return;
6385 if (shift && !inSelMargin) {
6386 SetSelection(newPos);
6388 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
6389 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
6390 SetMouseCapture(true);
6391 if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))
6392 SetEmptySelection(newPos.Position());
6393 bool doubleClick = false;
6394 // Stop mouse button bounce changing selection type
6395 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
6396 if (inSelMargin) {
6397 // Inside margin selection type should be either selSubLine or selWholeLine.
6398 if (selectionType == selSubLine) {
6399 // If it is selSubLine, we're inside a *double* click and word wrap is enabled,
6400 // so we switch to selWholeLine in order to select whole line.
6401 selectionType = selWholeLine;
6402 } else if (selectionType != selSubLine && selectionType != selWholeLine) {
6403 // If it is neither, reset selection type to line selection.
6404 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6406 } else {
6407 if (selectionType == selChar) {
6408 selectionType = selWord;
6409 doubleClick = true;
6410 } else if (selectionType == selWord) {
6411 // Since we ended up here, we're inside a *triple* click, which should always select
6412 // whole line irregardless of word wrap being enabled or not.
6413 selectionType = selWholeLine;
6414 } else {
6415 selectionType = selChar;
6416 originalAnchorPos = sel.MainCaret();
6421 if (selectionType == selWord) {
6422 int charPos = originalAnchorPos;
6423 if (sel.MainCaret() == originalAnchorPos) {
6424 charPos = PositionFromLocation(pt, false, true);
6425 charPos = MovePositionOutsideChar(charPos, -1);
6428 int startWord, endWord;
6429 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
6430 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
6431 endWord = pdoc->ExtendWordSelect(charPos, 1);
6432 } else {
6433 // Selecting backwards, or anchor beyond last character on line. In these cases,
6434 // we select the word containing the character to the *left* of the anchor.
6435 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
6436 startWord = pdoc->ExtendWordSelect(charPos, -1);
6437 endWord = pdoc->ExtendWordSelect(startWord, 1);
6438 } else {
6439 // Anchor at start of line; select nothing to begin with.
6440 startWord = charPos;
6441 endWord = charPos;
6445 wordSelectAnchorStartPos = startWord;
6446 wordSelectAnchorEndPos = endWord;
6447 wordSelectInitialCaretPos = sel.MainCaret();
6448 WordSelection(wordSelectInitialCaretPos);
6449 } else if (selectionType == selSubLine || selectionType == selWholeLine) {
6450 lineAnchorPos = newPos.Position();
6451 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6452 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
6453 } else {
6454 SetEmptySelection(sel.MainCaret());
6456 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
6457 if (doubleClick) {
6458 NotifyDoubleClick(pt, modifiers);
6459 if (PositionIsHotspot(newCharPos.Position()))
6460 NotifyHotSpotDoubleClicked(newCharPos.Position(), modifiers);
6462 } else { // Single click
6463 if (inSelMargin) {
6464 sel.selType = Selection::selStream;
6465 if (!shift) {
6466 // Single click in margin: select whole line or only subline if word wrap is enabled
6467 lineAnchorPos = newPos.Position();
6468 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6469 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6470 } else {
6471 // Single shift+click in margin: select from line anchor to clicked line
6472 if (sel.MainAnchor() > sel.MainCaret())
6473 lineAnchorPos = sel.MainAnchor() - 1;
6474 else
6475 lineAnchorPos = sel.MainAnchor();
6476 // Reset selection type if there is an empty selection.
6477 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
6478 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
6479 // This ensures that we continue selecting in the same selection mode.
6480 if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
6481 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6482 LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
6485 SetDragPosition(SelectionPosition(invalidPosition));
6486 SetMouseCapture(true);
6487 } else {
6488 if (PointIsHotspot(pt)) {
6489 NotifyHotSpotClicked(newCharPos.Position(), modifiers);
6490 hotSpotClickPos = newCharPos.Position();
6492 if (!shift) {
6493 if (PointInSelection(pt) && !SelectionEmpty())
6494 inDragDrop = ddInitial;
6495 else
6496 inDragDrop = ddNone;
6498 SetMouseCapture(true);
6499 if (inDragDrop != ddInitial) {
6500 SetDragPosition(SelectionPosition(invalidPosition));
6501 if (!shift) {
6502 if (ctrl && multipleSelection) {
6503 SelectionRange range(newPos);
6504 sel.TentativeSelection(range);
6505 InvalidateSelection(range, true);
6506 } else {
6507 InvalidateSelection(SelectionRange(newPos), true);
6508 if (sel.Count() > 1)
6509 Redraw();
6510 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
6511 sel.Clear();
6512 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6513 SetSelection(newPos, newPos);
6516 SelectionPosition anchorCurrent = newPos;
6517 if (shift)
6518 anchorCurrent = sel.IsRectangular() ?
6519 sel.Rectangular().anchor : sel.RangeMain().anchor;
6520 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6521 selectionType = selChar;
6522 originalAnchorPos = sel.MainCaret();
6523 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
6524 SetRectangularRange();
6528 lastClickTime = curTime;
6529 lastClick = pt;
6530 lastXChosen = static_cast<int>(pt.x) + xOffset;
6531 ShowCaretAtCurrentPosition();
6534 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
6535 return ButtonDownWithModifiers(pt, curTime, ModifierFlags(shift, ctrl, alt));
6538 bool Editor::PositionIsHotspot(int position) const {
6539 return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
6542 bool Editor::PointIsHotspot(Point pt) {
6543 int pos = PositionFromLocation(pt, true, true);
6544 if (pos == INVALID_POSITION)
6545 return false;
6546 return PositionIsHotspot(pos);
6549 void Editor::SetHotSpotRange(Point *pt) {
6550 if (pt) {
6551 int pos = PositionFromLocation(*pt, false, true);
6553 // If we don't limit this to word characters then the
6554 // range can encompass more than the run range and then
6555 // the underline will not be drawn properly.
6556 int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
6557 int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
6559 // Only invalidate the range if the hotspot range has changed...
6560 if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
6561 if (hsStart != -1) {
6562 InvalidateRange(hsStart, hsEnd);
6564 hsStart = hsStart_;
6565 hsEnd = hsEnd_;
6566 InvalidateRange(hsStart, hsEnd);
6568 } else {
6569 if (hsStart != -1) {
6570 int hsStart_ = hsStart;
6571 int hsEnd_ = hsEnd;
6572 hsStart = -1;
6573 hsEnd = -1;
6574 InvalidateRange(hsStart_, hsEnd_);
6575 } else {
6576 hsStart = -1;
6577 hsEnd = -1;
6582 void Editor::GetHotSpotRange(int &hsStart_, int &hsEnd_) const {
6583 hsStart_ = hsStart;
6584 hsEnd_ = hsEnd;
6587 void Editor::ButtonMoveWithModifiers(Point pt, int modifiers) {
6588 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
6589 DwellEnd(true);
6592 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
6593 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6594 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
6596 if (inDragDrop == ddInitial) {
6597 if (DragThreshold(ptMouseLast, pt)) {
6598 SetMouseCapture(false);
6599 SetDragPosition(movePos);
6600 CopySelectionRange(&drag);
6601 StartDrag();
6603 return;
6606 ptMouseLast = pt;
6607 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
6608 if (HaveMouseCapture()) {
6610 // Slow down autoscrolling/selection
6611 autoScrollTimer.ticksToWait -= timer.tickSize;
6612 if (autoScrollTimer.ticksToWait > 0)
6613 return;
6614 autoScrollTimer.ticksToWait = autoScrollDelay;
6616 // Adjust selection
6617 if (posDrag.IsValid()) {
6618 SetDragPosition(movePos);
6619 } else {
6620 if (selectionType == selChar) {
6621 if (sel.selType == Selection::selStream && (modifiers & SCI_ALT) && mouseSelectionRectangularSwitch) {
6622 sel.selType = Selection::selRectangle;
6624 if (sel.IsRectangular()) {
6625 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
6626 SetSelection(movePos, sel.RangeMain().anchor);
6627 } else if (sel.Count() > 1) {
6628 InvalidateSelection(sel.RangeMain(), false);
6629 SelectionRange range(movePos, sel.RangeMain().anchor);
6630 sel.TentativeSelection(range);
6631 InvalidateSelection(range, true);
6632 } else {
6633 SetSelection(movePos, sel.RangeMain().anchor);
6635 } else if (selectionType == selWord) {
6636 // Continue selecting by word
6637 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
6638 // No need to do anything. Previously this case was lumped
6639 // in with "Moved forward", but that can be harmful in this
6640 // case: a handler for the NotifyDoubleClick re-adjusts
6641 // the selection for a fancier definition of "word" (for
6642 // example, in Perl it is useful to include the leading
6643 // '$', '%' or '@' on variables for word selection). In this
6644 // the ButtonMove() called via Tick() for auto-scrolling
6645 // could result in the fancier word selection adjustment
6646 // being unmade.
6647 } else {
6648 wordSelectInitialCaretPos = -1;
6649 WordSelection(movePos.Position());
6651 } else {
6652 // Continue selecting by line
6653 LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
6657 // Autoscroll
6658 PRectangle rcClient = GetClientRectangle();
6659 Point ptOrigin = GetVisibleOriginInMain();
6660 rcClient.Move(0, -ptOrigin.y);
6661 int lineMove = DisplayFromPosition(movePos.Position());
6662 if (pt.y > rcClient.bottom) {
6663 ScrollTo(lineMove - LinesOnScreen() + 1);
6664 Redraw();
6665 } else if (pt.y < rcClient.top) {
6666 ScrollTo(lineMove);
6667 Redraw();
6669 EnsureCaretVisible(false, false, true);
6671 if (hsStart != -1 && !PointIsHotspot(pt))
6672 SetHotSpotRange(NULL);
6674 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,true) != hotSpotClickPos) {
6675 if (inDragDrop == ddNone) {
6676 DisplayCursor(Window::cursorText);
6678 hotSpotClickPos = INVALID_POSITION;
6681 } else {
6682 if (vs.fixedColumnWidth > 0) { // There is a margin
6683 if (PointInSelMargin(pt)) {
6684 DisplayCursor(GetMarginCursor(pt));
6685 SetHotSpotRange(NULL);
6686 return; // No need to test for selection
6689 // Display regular (drag) cursor over selection
6690 if (PointInSelection(pt) && !SelectionEmpty()) {
6691 DisplayCursor(Window::cursorArrow);
6692 } else if (PointIsHotspot(pt)) {
6693 DisplayCursor(Window::cursorHand);
6694 SetHotSpotRange(&pt);
6695 } else {
6696 DisplayCursor(Window::cursorText);
6697 SetHotSpotRange(NULL);
6702 void Editor::ButtonMove(Point pt) {
6703 ButtonMoveWithModifiers(pt, 0);
6706 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
6707 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
6708 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
6709 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6710 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6711 if (inDragDrop == ddInitial) {
6712 inDragDrop = ddNone;
6713 SetEmptySelection(newPos);
6714 selectionType = selChar;
6715 originalAnchorPos = sel.MainCaret();
6717 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
6718 hotSpotClickPos = INVALID_POSITION;
6719 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
6720 newCharPos = MovePositionOutsideChar(newCharPos, -1);
6721 NotifyHotSpotReleaseClick(newCharPos.Position(), ctrl ? SCI_CTRL : 0);
6723 if (HaveMouseCapture()) {
6724 if (PointInSelMargin(pt)) {
6725 DisplayCursor(GetMarginCursor(pt));
6726 } else {
6727 DisplayCursor(Window::cursorText);
6728 SetHotSpotRange(NULL);
6730 ptMouseLast = pt;
6731 SetMouseCapture(false);
6732 NotifyIndicatorClick(false, newPos.Position(), 0);
6733 if (inDragDrop == ddDragging) {
6734 SelectionPosition selStart = SelectionStart();
6735 SelectionPosition selEnd = SelectionEnd();
6736 if (selStart < selEnd) {
6737 if (drag.Length()) {
6738 const int length = static_cast<int>(drag.Length());
6739 if (ctrl) {
6740 const int lengthInserted = pdoc->InsertString(
6741 newPos.Position(), drag.Data(), length);
6742 if (lengthInserted > 0) {
6743 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
6745 } else if (newPos < selStart) {
6746 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
6747 const int lengthInserted = pdoc->InsertString(
6748 newPos.Position(), drag.Data(), length);
6749 if (lengthInserted > 0) {
6750 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
6752 } else if (newPos > selEnd) {
6753 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
6754 newPos.Add(-static_cast<int>(drag.Length()));
6755 const int lengthInserted = pdoc->InsertString(
6756 newPos.Position(), drag.Data(), length);
6757 if (lengthInserted > 0) {
6758 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
6760 } else {
6761 SetEmptySelection(newPos.Position());
6763 drag.Clear();
6765 selectionType = selChar;
6767 } else {
6768 if (selectionType == selChar) {
6769 if (sel.Count() > 1) {
6770 sel.RangeMain() =
6771 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
6772 InvalidateSelection(sel.RangeMain(), true);
6773 } else {
6774 SetSelection(newPos, sel.RangeMain().anchor);
6777 sel.CommitTentative();
6779 SetRectangularRange();
6780 lastClickTime = curTime;
6781 lastClick = pt;
6782 lastXChosen = static_cast<int>(pt.x) + xOffset;
6783 if (sel.selType == Selection::selStream) {
6784 SetLastXChosen();
6786 inDragDrop = ddNone;
6787 EnsureCaretVisible(false);
6791 // Called frequently to perform background UI including
6792 // caret blinking and automatic scrolling.
6793 void Editor::Tick() {
6794 if (HaveMouseCapture()) {
6795 // Auto scroll
6796 ButtonMove(ptMouseLast);
6798 if (caret.period > 0) {
6799 timer.ticksToWait -= timer.tickSize;
6800 if (timer.ticksToWait <= 0) {
6801 caret.on = !caret.on;
6802 timer.ticksToWait = caret.period;
6803 if (caret.active) {
6804 InvalidateCaret();
6808 if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) {
6809 scrollWidth = lineWidthMaxSeen;
6810 SetScrollBars();
6812 if ((dwellDelay < SC_TIME_FOREVER) &&
6813 (ticksToDwell > 0) &&
6814 (!HaveMouseCapture()) &&
6815 (ptMouseLast.y >= 0)) {
6816 ticksToDwell -= timer.tickSize;
6817 if (ticksToDwell <= 0) {
6818 dwelling = true;
6819 NotifyDwelling(ptMouseLast, dwelling);
6824 bool Editor::Idle() {
6826 bool idleDone;
6828 bool wrappingDone = !Wrapping();
6830 if (!wrappingDone) {
6831 // Wrap lines during idle.
6832 WrapLines(wsIdle);
6833 // No more wrapping
6834 if (!wrapPending.NeedsWrap())
6835 wrappingDone = true;
6838 // Add more idle things to do here, but make sure idleDone is
6839 // set correctly before the function returns. returning
6840 // false will stop calling this idle funtion until SetIdle() is
6841 // called again.
6843 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
6845 return !idleDone;
6848 void Editor::SetFocusState(bool focusState) {
6849 hasFocus = focusState;
6850 NotifyFocus(hasFocus);
6851 if (hasFocus) {
6852 ShowCaretAtCurrentPosition();
6853 } else {
6854 CancelModes();
6855 DropCaret();
6859 int Editor::PositionAfterArea(PRectangle rcArea) const {
6860 // The start of the document line after the display line after the area
6861 // This often means that the line after a modification is restyled which helps
6862 // detect multiline comment additions and heals single line comments
6863 int lineAfter = TopLineOfMain() + static_cast<int>(rcArea.bottom - 1) / vs.lineHeight + 1;
6864 if (lineAfter < cs.LinesDisplayed())
6865 return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
6866 else
6867 return pdoc->Length();
6870 // Style to a position within the view. If this causes a change at end of last line then
6871 // affects later lines so style all the viewed text.
6872 void Editor::StyleToPositionInView(Position pos) {
6873 int endWindow = PositionAfterArea(GetClientDrawingRectangle());
6874 if (pos > endWindow)
6875 pos = endWindow;
6876 int styleAtEnd = pdoc->StyleAt(pos-1);
6877 pdoc->EnsureStyledTo(pos);
6878 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleAt(pos-1))) {
6879 // Style at end of line changed so is multi-line change like starting a comment
6880 // so require rest of window to be styled.
6881 DiscardOverdraw(); // Prepared bitmaps may be invalid
6882 // DiscardOverdraw may have truncated client drawing area so recalculate endWindow
6883 endWindow = PositionAfterArea(GetClientDrawingRectangle());
6884 pdoc->EnsureStyledTo(endWindow);
6888 void Editor::IdleWork() {
6889 // Style the line after the modification as this allows modifications that change just the
6890 // line of the modification to heal instead of propagating to the rest of the window.
6891 if (workNeeded.items & WorkNeeded::workStyle)
6892 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2));
6894 NotifyUpdateUI();
6895 workNeeded.Reset();
6898 void Editor::QueueIdleWork(WorkNeeded::workItems items, int upTo) {
6899 workNeeded.Need(items, upTo);
6902 bool Editor::PaintContains(PRectangle rc) {
6903 if (rc.Empty()) {
6904 return true;
6905 } else {
6906 return rcPaint.Contains(rc);
6910 bool Editor::PaintContainsMargin() {
6911 if (wMargin.GetID()) {
6912 // With separate margin view, paint of text view
6913 // never contains margin.
6914 return false;
6916 PRectangle rcSelMargin = GetClientRectangle();
6917 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart);
6918 return PaintContains(rcSelMargin);
6921 void Editor::CheckForChangeOutsidePaint(Range r) {
6922 if (paintState == painting && !paintingAllText) {
6923 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
6924 if (!r.Valid())
6925 return;
6927 PRectangle rcRange = RectangleFromRange(r);
6928 PRectangle rcText = GetTextRectangle();
6929 if (rcRange.top < rcText.top) {
6930 rcRange.top = rcText.top;
6932 if (rcRange.bottom > rcText.bottom) {
6933 rcRange.bottom = rcText.bottom;
6936 if (!PaintContains(rcRange)) {
6937 AbandonPaint();
6938 paintAbandonedByStyling = true;
6943 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
6944 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
6945 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
6946 CheckForChangeOutsidePaint(Range(braces[0]));
6947 CheckForChangeOutsidePaint(Range(pos0));
6948 braces[0] = pos0;
6950 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
6951 CheckForChangeOutsidePaint(Range(braces[1]));
6952 CheckForChangeOutsidePaint(Range(pos1));
6953 braces[1] = pos1;
6955 bracesMatchStyle = matchStyle;
6956 if (paintState == notPainting) {
6957 Redraw();
6962 void Editor::SetAnnotationHeights(int start, int end) {
6963 if (vs.annotationVisible) {
6964 bool changedHeight = false;
6965 for (int line=start; line<end && line<pdoc->LinesTotal(); line++) {
6966 int linesWrapped = 1;
6967 if (Wrapping()) {
6968 AutoSurface surface(this);
6969 AutoLineLayout ll(llc, RetrieveLineLayout(line));
6970 if (surface && ll) {
6971 LayoutLine(line, surface, vs, ll, wrapWidth);
6972 linesWrapped = ll->lines;
6975 if (cs.SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
6976 changedHeight = true;
6978 if (changedHeight) {
6979 Redraw();
6984 void Editor::SetDocPointer(Document *document) {
6985 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
6986 pdoc->RemoveWatcher(this, 0);
6987 pdoc->Release();
6988 if (document == NULL) {
6989 pdoc = new Document();
6990 } else {
6991 pdoc = document;
6993 pdoc->AddRef();
6995 // Ensure all positions within document
6996 sel.Clear();
6997 targetStart = 0;
6998 targetEnd = 0;
7000 braces[0] = invalidPosition;
7001 braces[1] = invalidPosition;
7003 vs.ReleaseAllExtendedStyles();
7005 SetRepresentations();
7007 // Reset the contraction state to fully shown.
7008 cs.Clear();
7009 cs.InsertLines(0, pdoc->LinesTotal() - 1);
7010 SetAnnotationHeights(0, pdoc->LinesTotal());
7011 llc.Deallocate();
7012 NeedWrapping();
7014 pdoc->AddWatcher(this, 0);
7015 SetScrollBars();
7016 Redraw();
7019 void Editor::SetAnnotationVisible(int visible) {
7020 if (vs.annotationVisible != visible) {
7021 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
7022 vs.annotationVisible = visible;
7023 if (changedFromOrToHidden) {
7024 int dir = vs.annotationVisible ? 1 : -1;
7025 for (int line=0; line<pdoc->LinesTotal(); line++) {
7026 int annotationLines = pdoc->AnnotationLines(line);
7027 if (annotationLines > 0) {
7028 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
7032 Redraw();
7037 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
7039 int Editor::ExpandLine(int line) {
7040 int lineMaxSubord = pdoc->GetLastChild(line);
7041 line++;
7042 while (line <= lineMaxSubord) {
7043 cs.SetVisible(line, line, true);
7044 int level = pdoc->GetLevel(line);
7045 if (level & SC_FOLDLEVELHEADERFLAG) {
7046 if (cs.GetExpanded(line)) {
7047 line = ExpandLine(line);
7048 } else {
7049 line = pdoc->GetLastChild(line);
7052 line++;
7054 return lineMaxSubord;
7057 void Editor::SetFoldExpanded(int lineDoc, bool expanded) {
7058 if (cs.SetExpanded(lineDoc, expanded)) {
7059 RedrawSelMargin();
7063 void Editor::FoldLine(int line, int action) {
7064 if (line >= 0) {
7065 if (action == SC_FOLDACTION_TOGGLE) {
7066 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
7067 line = pdoc->GetFoldParent(line);
7068 if (line < 0)
7069 return;
7071 action = (cs.GetExpanded(line)) ? SC_FOLDACTION_CONTRACT : SC_FOLDACTION_EXPAND;
7074 if (action == SC_FOLDACTION_CONTRACT) {
7075 int lineMaxSubord = pdoc->GetLastChild(line);
7076 if (lineMaxSubord > line) {
7077 cs.SetExpanded(line, 0);
7078 cs.SetVisible(line + 1, lineMaxSubord, false);
7080 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
7081 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
7082 // This does not re-expand the fold
7083 EnsureCaretVisible();
7087 } else {
7088 if (!(cs.GetVisible(line))) {
7089 EnsureLineVisible(line, false);
7090 GoToLine(line);
7092 cs.SetExpanded(line, 1);
7093 ExpandLine(line);
7096 SetScrollBars();
7097 Redraw();
7101 void Editor::FoldExpand(int line, int action, int level) {
7102 bool expanding = action == SC_FOLDACTION_EXPAND;
7103 if (action == SC_FOLDACTION_TOGGLE) {
7104 expanding = !cs.GetExpanded(line);
7106 SetFoldExpanded(line, expanding);
7107 if (expanding && (cs.HiddenLines() == 0))
7108 // Nothing to do
7109 return;
7110 int lineMaxSubord = pdoc->GetLastChild(line, level & SC_FOLDLEVELNUMBERMASK);
7111 line++;
7112 cs.SetVisible(line, lineMaxSubord, expanding);
7113 while (line <= lineMaxSubord) {
7114 int levelLine = pdoc->GetLevel(line);
7115 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
7116 SetFoldExpanded(line, expanding);
7118 line++;
7120 SetScrollBars();
7121 Redraw();
7124 int Editor::ContractedFoldNext(int lineStart) const {
7125 for (int line = lineStart; line<pdoc->LinesTotal();) {
7126 if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
7127 return line;
7128 line = cs.ContractedNext(line+1);
7129 if (line < 0)
7130 return -1;
7133 return -1;
7137 * Recurse up from this line to find any folds that prevent this line from being visible
7138 * and unfold them all.
7140 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
7142 // In case in need of wrapping to ensure DisplayFromDoc works.
7143 if (lineDoc >= wrapPending.start)
7144 WrapLines(wsAll);
7146 if (!cs.GetVisible(lineDoc)) {
7147 // Back up to find a non-blank line
7148 int lookLine = lineDoc;
7149 int lookLineLevel = pdoc->GetLevel(lookLine);
7150 while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
7151 lookLineLevel = pdoc->GetLevel(--lookLine);
7153 int lineParent = pdoc->GetFoldParent(lookLine);
7154 if (lineParent < 0) {
7155 // Backed up to a top level line, so try to find parent of initial line
7156 lineParent = pdoc->GetFoldParent(lineDoc);
7158 if (lineParent >= 0) {
7159 if (lineDoc != lineParent)
7160 EnsureLineVisible(lineParent, enforcePolicy);
7161 if (!cs.GetExpanded(lineParent)) {
7162 cs.SetExpanded(lineParent, 1);
7163 ExpandLine(lineParent);
7166 SetScrollBars();
7167 Redraw();
7169 if (enforcePolicy) {
7170 int lineDisplay = cs.DisplayFromDoc(lineDoc);
7171 if (visiblePolicy & VISIBLE_SLOP) {
7172 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
7173 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
7174 SetVerticalScrollPos();
7175 Redraw();
7176 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
7177 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
7178 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
7179 SetVerticalScrollPos();
7180 Redraw();
7182 } else {
7183 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
7184 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
7185 SetVerticalScrollPos();
7186 Redraw();
7192 void Editor::FoldAll(int action) {
7193 pdoc->EnsureStyledTo(pdoc->Length());
7194 int maxLine = pdoc->LinesTotal();
7195 bool expanding = action == SC_FOLDACTION_EXPAND;
7196 if (action == SC_FOLDACTION_TOGGLE) {
7197 // Discover current state
7198 for (int lineSeek = 0; lineSeek < maxLine; lineSeek++) {
7199 if (pdoc->GetLevel(lineSeek) & SC_FOLDLEVELHEADERFLAG) {
7200 expanding = !cs.GetExpanded(lineSeek);
7201 break;
7205 if (expanding) {
7206 cs.SetVisible(0, maxLine-1, true);
7207 for (int line = 0; line < maxLine; line++) {
7208 int levelLine = pdoc->GetLevel(line);
7209 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
7210 SetFoldExpanded(line, true);
7213 } else {
7214 for (int line = 0; line < maxLine; line++) {
7215 int level = pdoc->GetLevel(line);
7216 if ((level & SC_FOLDLEVELHEADERFLAG) &&
7217 (SC_FOLDLEVELBASE == (level & SC_FOLDLEVELNUMBERMASK))) {
7218 SetFoldExpanded(line, false);
7219 int lineMaxSubord = pdoc->GetLastChild(line, -1);
7220 if (lineMaxSubord > line) {
7221 cs.SetVisible(line + 1, lineMaxSubord, false);
7226 SetScrollBars();
7227 Redraw();
7230 void Editor::FoldChanged(int line, int levelNow, int levelPrev) {
7231 if (levelNow & SC_FOLDLEVELHEADERFLAG) {
7232 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
7233 // Adding a fold point.
7234 if (cs.SetExpanded(line, true)) {
7235 RedrawSelMargin();
7237 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
7239 } else if (levelPrev & SC_FOLDLEVELHEADERFLAG) {
7240 if (!cs.GetExpanded(line)) {
7241 // Removing the fold from one that has been contracted so should expand
7242 // otherwise lines are left invisible with no way to make them visible
7243 if (cs.SetExpanded(line, true)) {
7244 RedrawSelMargin();
7246 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
7249 if (!(levelNow & SC_FOLDLEVELWHITEFLAG) &&
7250 ((levelPrev & SC_FOLDLEVELNUMBERMASK) > (levelNow & SC_FOLDLEVELNUMBERMASK))) {
7251 if (cs.HiddenLines()) {
7252 // See if should still be hidden
7253 int parentLine = pdoc->GetFoldParent(line);
7254 if ((parentLine < 0) || (cs.GetExpanded(parentLine) && cs.GetVisible(parentLine))) {
7255 cs.SetVisible(line, line, true);
7256 SetScrollBars();
7257 Redraw();
7263 void Editor::NeedShown(int pos, int len) {
7264 if (foldAutomatic & SC_AUTOMATICFOLD_SHOW) {
7265 int lineStart = pdoc->LineFromPosition(pos);
7266 int lineEnd = pdoc->LineFromPosition(pos+len);
7267 for (int line = lineStart; line <= lineEnd; line++) {
7268 EnsureLineVisible(line, false);
7270 } else {
7271 NotifyNeedShown(pos, len);
7275 int Editor::GetTag(char *tagValue, int tagNumber) {
7276 const char *text = 0;
7277 int length = 0;
7278 if ((tagNumber >= 1) && (tagNumber <= 9)) {
7279 char name[3] = "\\?";
7280 name[1] = static_cast<char>(tagNumber + '0');
7281 length = 2;
7282 text = pdoc->SubstituteByPosition(name, &length);
7284 if (tagValue) {
7285 if (text)
7286 memcpy(tagValue, text, length + 1);
7287 else
7288 *tagValue = '\0';
7290 return length;
7293 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
7294 UndoGroup ug(pdoc);
7295 if (length == -1)
7296 length = istrlen(text);
7297 if (replacePatterns) {
7298 text = pdoc->SubstituteByPosition(text, &length);
7299 if (!text) {
7300 return 0;
7303 if (targetStart != targetEnd)
7304 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
7305 targetEnd = targetStart;
7306 const int lengthInserted = pdoc->InsertString(targetStart, text, length);
7307 targetEnd = targetStart + lengthInserted;
7308 return length;
7311 bool Editor::IsUnicodeMode() const {
7312 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
7315 int Editor::CodePage() const {
7316 if (pdoc)
7317 return pdoc->dbcsCodePage;
7318 else
7319 return 0;
7322 int Editor::WrapCount(int line) {
7323 AutoSurface surface(this);
7324 AutoLineLayout ll(llc, RetrieveLineLayout(line));
7326 if (surface && ll) {
7327 LayoutLine(line, surface, vs, ll, wrapWidth);
7328 return ll->lines;
7329 } else {
7330 return 1;
7334 void Editor::AddStyledText(char *buffer, int appendLength) {
7335 // The buffer consists of alternating character bytes and style bytes
7336 int textLength = appendLength / 2;
7337 std::string text(textLength, '\0');
7338 int i;
7339 for (i = 0; i < textLength; i++) {
7340 text[i] = buffer[i*2];
7342 const int lengthInserted = pdoc->InsertString(CurrentPosition(), text.c_str(), textLength);
7343 for (i = 0; i < textLength; i++) {
7344 text[i] = buffer[i*2+1];
7346 pdoc->StartStyling(CurrentPosition(), static_cast<unsigned char>(0xff));
7347 pdoc->SetStyles(textLength, text.c_str());
7348 SetEmptySelection(sel.MainCaret() + lengthInserted);
7351 static bool ValidMargin(uptr_t wParam) {
7352 return wParam <= SC_MAX_MARGIN;
7355 static char *CharPtrFromSPtr(sptr_t lParam) {
7356 return reinterpret_cast<char *>(lParam);
7359 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7360 vs.EnsureStyle(wParam);
7361 switch (iMessage) {
7362 case SCI_STYLESETFORE:
7363 vs.styles[wParam].fore = ColourDesired(static_cast<long>(lParam));
7364 break;
7365 case SCI_STYLESETBACK:
7366 vs.styles[wParam].back = ColourDesired(static_cast<long>(lParam));
7367 break;
7368 case SCI_STYLESETBOLD:
7369 vs.styles[wParam].weight = lParam != 0 ? SC_WEIGHT_BOLD : SC_WEIGHT_NORMAL;
7370 break;
7371 case SCI_STYLESETWEIGHT:
7372 vs.styles[wParam].weight = static_cast<int>(lParam);
7373 break;
7374 case SCI_STYLESETITALIC:
7375 vs.styles[wParam].italic = lParam != 0;
7376 break;
7377 case SCI_STYLESETEOLFILLED:
7378 vs.styles[wParam].eolFilled = lParam != 0;
7379 break;
7380 case SCI_STYLESETSIZE:
7381 vs.styles[wParam].size = static_cast<int>(lParam * SC_FONT_SIZE_MULTIPLIER);
7382 break;
7383 case SCI_STYLESETSIZEFRACTIONAL:
7384 vs.styles[wParam].size = static_cast<int>(lParam);
7385 break;
7386 case SCI_STYLESETFONT:
7387 if (lParam != 0) {
7388 vs.SetStyleFontName(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7390 break;
7391 case SCI_STYLESETUNDERLINE:
7392 vs.styles[wParam].underline = lParam != 0;
7393 break;
7394 case SCI_STYLESETCASE:
7395 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
7396 break;
7397 case SCI_STYLESETCHARACTERSET:
7398 vs.styles[wParam].characterSet = static_cast<int>(lParam);
7399 pdoc->SetCaseFolder(NULL);
7400 break;
7401 case SCI_STYLESETVISIBLE:
7402 vs.styles[wParam].visible = lParam != 0;
7403 break;
7404 case SCI_STYLESETCHANGEABLE:
7405 vs.styles[wParam].changeable = lParam != 0;
7406 break;
7407 case SCI_STYLESETHOTSPOT:
7408 vs.styles[wParam].hotspot = lParam != 0;
7409 break;
7411 InvalidateStyleRedraw();
7414 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7415 vs.EnsureStyle(wParam);
7416 switch (iMessage) {
7417 case SCI_STYLEGETFORE:
7418 return vs.styles[wParam].fore.AsLong();
7419 case SCI_STYLEGETBACK:
7420 return vs.styles[wParam].back.AsLong();
7421 case SCI_STYLEGETBOLD:
7422 return vs.styles[wParam].weight > SC_WEIGHT_NORMAL;
7423 case SCI_STYLEGETWEIGHT:
7424 return vs.styles[wParam].weight;
7425 case SCI_STYLEGETITALIC:
7426 return vs.styles[wParam].italic ? 1 : 0;
7427 case SCI_STYLEGETEOLFILLED:
7428 return vs.styles[wParam].eolFilled ? 1 : 0;
7429 case SCI_STYLEGETSIZE:
7430 return vs.styles[wParam].size / SC_FONT_SIZE_MULTIPLIER;
7431 case SCI_STYLEGETSIZEFRACTIONAL:
7432 return vs.styles[wParam].size;
7433 case SCI_STYLEGETFONT:
7434 return StringResult(lParam, vs.styles[wParam].fontName);
7435 case SCI_STYLEGETUNDERLINE:
7436 return vs.styles[wParam].underline ? 1 : 0;
7437 case SCI_STYLEGETCASE:
7438 return static_cast<int>(vs.styles[wParam].caseForce);
7439 case SCI_STYLEGETCHARACTERSET:
7440 return vs.styles[wParam].characterSet;
7441 case SCI_STYLEGETVISIBLE:
7442 return vs.styles[wParam].visible ? 1 : 0;
7443 case SCI_STYLEGETCHANGEABLE:
7444 return vs.styles[wParam].changeable ? 1 : 0;
7445 case SCI_STYLEGETHOTSPOT:
7446 return vs.styles[wParam].hotspot ? 1 : 0;
7448 return 0;
7451 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
7452 const size_t len = val ? strlen(val) : 0;
7453 if (lParam) {
7454 char *ptr = CharPtrFromSPtr(lParam);
7455 if (val)
7456 memcpy(ptr, val, len+1);
7457 else
7458 *ptr = 0;
7460 return len; // Not including NUL
7463 sptr_t Editor::BytesResult(sptr_t lParam, const unsigned char *val, size_t len) {
7464 // No NUL termination: len is number of valid/displayed bytes
7465 if (lParam) {
7466 char *ptr = CharPtrFromSPtr(lParam);
7467 if (val)
7468 memcpy(ptr, val, len);
7469 else
7470 *ptr = 0;
7472 return val ? len : 0;
7475 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7476 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
7478 // Optional macro recording hook
7479 if (recordingMacro)
7480 NotifyMacroRecord(iMessage, wParam, lParam);
7482 switch (iMessage) {
7484 case SCI_GETTEXT: {
7485 if (lParam == 0)
7486 return pdoc->Length() + 1;
7487 if (wParam == 0)
7488 return 0;
7489 char *ptr = CharPtrFromSPtr(lParam);
7490 unsigned int iChar = 0;
7491 for (; iChar < wParam - 1; iChar++)
7492 ptr[iChar] = pdoc->CharAt(iChar);
7493 ptr[iChar] = '\0';
7494 return iChar;
7497 case SCI_SETTEXT: {
7498 if (lParam == 0)
7499 return 0;
7500 UndoGroup ug(pdoc);
7501 pdoc->DeleteChars(0, pdoc->Length());
7502 SetEmptySelection(0);
7503 const char *text = CharPtrFromSPtr(lParam);
7504 pdoc->InsertString(0, text, istrlen(text));
7505 return 1;
7508 case SCI_GETTEXTLENGTH:
7509 return pdoc->Length();
7511 case SCI_CUT:
7512 Cut();
7513 SetLastXChosen();
7514 break;
7516 case SCI_COPY:
7517 Copy();
7518 break;
7520 case SCI_COPYALLOWLINE:
7521 CopyAllowLine();
7522 break;
7524 case SCI_VERTICALCENTRECARET:
7525 VerticalCentreCaret();
7526 break;
7528 case SCI_MOVESELECTEDLINESUP:
7529 MoveSelectedLinesUp();
7530 break;
7532 case SCI_MOVESELECTEDLINESDOWN:
7533 MoveSelectedLinesDown();
7534 break;
7536 case SCI_COPYRANGE:
7537 CopyRangeToClipboard(static_cast<int>(wParam), static_cast<int>(lParam));
7538 break;
7540 case SCI_COPYTEXT:
7541 CopyText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
7542 break;
7544 case SCI_PASTE:
7545 Paste();
7546 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
7547 SetLastXChosen();
7549 EnsureCaretVisible();
7550 break;
7552 case SCI_CLEAR:
7553 Clear();
7554 SetLastXChosen();
7555 EnsureCaretVisible();
7556 break;
7558 case SCI_UNDO:
7559 Undo();
7560 SetLastXChosen();
7561 break;
7563 case SCI_CANUNDO:
7564 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
7566 case SCI_EMPTYUNDOBUFFER:
7567 pdoc->DeleteUndoHistory();
7568 return 0;
7570 case SCI_GETFIRSTVISIBLELINE:
7571 return topLine;
7573 case SCI_SETFIRSTVISIBLELINE:
7574 ScrollTo(static_cast<int>(wParam));
7575 break;
7577 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
7578 int lineStart = pdoc->LineStart(static_cast<int>(wParam));
7579 int lineEnd = pdoc->LineStart(static_cast<int>(wParam + 1));
7580 if (lParam == 0) {
7581 return lineEnd - lineStart;
7583 char *ptr = CharPtrFromSPtr(lParam);
7584 int iPlace = 0;
7585 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
7586 ptr[iPlace++] = pdoc->CharAt(iChar);
7588 return iPlace;
7591 case SCI_GETLINECOUNT:
7592 if (pdoc->LinesTotal() == 0)
7593 return 1;
7594 else
7595 return pdoc->LinesTotal();
7597 case SCI_GETMODIFY:
7598 return !pdoc->IsSavePoint();
7600 case SCI_SETSEL: {
7601 int nStart = static_cast<int>(wParam);
7602 int nEnd = static_cast<int>(lParam);
7603 if (nEnd < 0)
7604 nEnd = pdoc->Length();
7605 if (nStart < 0)
7606 nStart = nEnd; // Remove selection
7607 InvalidateSelection(SelectionRange(nStart, nEnd));
7608 sel.Clear();
7609 sel.selType = Selection::selStream;
7610 SetSelection(nEnd, nStart);
7611 EnsureCaretVisible();
7613 break;
7615 case SCI_GETSELTEXT: {
7616 SelectionText selectedText;
7617 CopySelectionRange(&selectedText);
7618 if (lParam == 0) {
7619 return selectedText.LengthWithTerminator();
7620 } else {
7621 char *ptr = CharPtrFromSPtr(lParam);
7622 unsigned int iChar = 0;
7623 if (selectedText.Length()) {
7624 for (; iChar < selectedText.LengthWithTerminator(); iChar++)
7625 ptr[iChar] = selectedText.Data()[iChar];
7626 } else {
7627 ptr[0] = '\0';
7629 return iChar;
7633 case SCI_LINEFROMPOSITION:
7634 if (static_cast<int>(wParam) < 0)
7635 return 0;
7636 return pdoc->LineFromPosition(static_cast<int>(wParam));
7638 case SCI_POSITIONFROMLINE:
7639 if (static_cast<int>(wParam) < 0)
7640 wParam = pdoc->LineFromPosition(SelectionStart().Position());
7641 if (wParam == 0)
7642 return 0; // Even if there is no text, there is a first line that starts at 0
7643 if (static_cast<int>(wParam) > pdoc->LinesTotal())
7644 return -1;
7645 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
7646 // return -1;
7647 return pdoc->LineStart(static_cast<int>(wParam));
7649 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
7650 case SCI_LINELENGTH:
7651 if ((static_cast<int>(wParam) < 0) ||
7652 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
7653 return 0;
7654 return pdoc->LineStart(static_cast<int>(wParam) + 1) - pdoc->LineStart(static_cast<int>(wParam));
7656 case SCI_REPLACESEL: {
7657 if (lParam == 0)
7658 return 0;
7659 UndoGroup ug(pdoc);
7660 ClearSelection();
7661 char *replacement = CharPtrFromSPtr(lParam);
7662 const int lengthInserted = pdoc->InsertString(
7663 sel.MainCaret(), replacement, istrlen(replacement));
7664 SetEmptySelection(sel.MainCaret() + lengthInserted);
7665 EnsureCaretVisible();
7667 break;
7669 case SCI_SETTARGETSTART:
7670 targetStart = static_cast<int>(wParam);
7671 break;
7673 case SCI_GETTARGETSTART:
7674 return targetStart;
7676 case SCI_SETTARGETEND:
7677 targetEnd = static_cast<int>(wParam);
7678 break;
7680 case SCI_GETTARGETEND:
7681 return targetEnd;
7683 case SCI_TARGETFROMSELECTION:
7684 if (sel.MainCaret() < sel.MainAnchor()) {
7685 targetStart = sel.MainCaret();
7686 targetEnd = sel.MainAnchor();
7687 } else {
7688 targetStart = sel.MainAnchor();
7689 targetEnd = sel.MainCaret();
7691 break;
7693 case SCI_REPLACETARGET:
7694 PLATFORM_ASSERT(lParam);
7695 return ReplaceTarget(false, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7697 case SCI_REPLACETARGETRE:
7698 PLATFORM_ASSERT(lParam);
7699 return ReplaceTarget(true, CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7701 case SCI_SEARCHINTARGET:
7702 PLATFORM_ASSERT(lParam);
7703 return SearchInTarget(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7705 case SCI_SETSEARCHFLAGS:
7706 searchFlags = static_cast<int>(wParam);
7707 break;
7709 case SCI_GETSEARCHFLAGS:
7710 return searchFlags;
7712 case SCI_GETTAG:
7713 return GetTag(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7715 case SCI_POSITIONBEFORE:
7716 return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) - 1, -1, true);
7718 case SCI_POSITIONAFTER:
7719 return pdoc->MovePositionOutsideChar(static_cast<int>(wParam) + 1, 1, true);
7721 case SCI_POSITIONRELATIVE:
7722 return Platform::Clamp(pdoc->GetRelativePosition(static_cast<int>(wParam), static_cast<int>(lParam)), 0, pdoc->Length());
7724 case SCI_LINESCROLL:
7725 ScrollTo(topLine + static_cast<int>(lParam));
7726 HorizontalScrollTo(xOffset + static_cast<int>(wParam)* static_cast<int>(vs.spaceWidth));
7727 return 1;
7729 case SCI_SETXOFFSET:
7730 xOffset = static_cast<int>(wParam);
7731 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7732 SetHorizontalScrollPos();
7733 Redraw();
7734 break;
7736 case SCI_GETXOFFSET:
7737 return xOffset;
7739 case SCI_CHOOSECARETX:
7740 SetLastXChosen();
7741 break;
7743 case SCI_SCROLLCARET:
7744 EnsureCaretVisible();
7745 break;
7747 case SCI_SETREADONLY:
7748 pdoc->SetReadOnly(wParam != 0);
7749 return 1;
7751 case SCI_GETREADONLY:
7752 return pdoc->IsReadOnly();
7754 case SCI_CANPASTE:
7755 return CanPaste();
7757 case SCI_POINTXFROMPOSITION:
7758 if (lParam < 0) {
7759 return 0;
7760 } else {
7761 Point pt = LocationFromPosition(static_cast<int>(lParam));
7762 // Convert to view-relative
7763 return static_cast<int>(pt.x) - vs.textStart + vs.fixedColumnWidth;
7766 case SCI_POINTYFROMPOSITION:
7767 if (lParam < 0) {
7768 return 0;
7769 } else {
7770 Point pt = LocationFromPosition(static_cast<int>(lParam));
7771 return static_cast<int>(pt.y);
7774 case SCI_FINDTEXT:
7775 return FindText(wParam, lParam);
7777 case SCI_GETTEXTRANGE: {
7778 if (lParam == 0)
7779 return 0;
7780 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7781 int cpMax = tr->chrg.cpMax;
7782 if (cpMax == -1)
7783 cpMax = pdoc->Length();
7784 PLATFORM_ASSERT(cpMax <= pdoc->Length());
7785 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
7786 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
7787 // Spec says copied text is terminated with a NUL
7788 tr->lpstrText[len] = '\0';
7789 return len; // Not including NUL
7792 case SCI_HIDESELECTION:
7793 hideSelection = wParam != 0;
7794 Redraw();
7795 break;
7797 case SCI_FORMATRANGE:
7798 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
7800 case SCI_GETMARGINLEFT:
7801 return vs.leftMarginWidth;
7803 case SCI_GETMARGINRIGHT:
7804 return vs.rightMarginWidth;
7806 case SCI_SETMARGINLEFT:
7807 lastXChosen += static_cast<int>(lParam) - vs.leftMarginWidth;
7808 vs.leftMarginWidth = static_cast<int>(lParam);
7809 InvalidateStyleRedraw();
7810 break;
7812 case SCI_SETMARGINRIGHT:
7813 vs.rightMarginWidth = static_cast<int>(lParam);
7814 InvalidateStyleRedraw();
7815 break;
7817 // Control specific mesages
7819 case SCI_ADDTEXT: {
7820 if (lParam == 0)
7821 return 0;
7822 const int lengthInserted = pdoc->InsertString(
7823 CurrentPosition(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7824 SetEmptySelection(sel.MainCaret() + lengthInserted);
7825 return 0;
7828 case SCI_ADDSTYLEDTEXT:
7829 if (lParam)
7830 AddStyledText(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7831 return 0;
7833 case SCI_INSERTTEXT: {
7834 if (lParam == 0)
7835 return 0;
7836 int insertPos = static_cast<int>(wParam);
7837 if (static_cast<int>(wParam) == -1)
7838 insertPos = CurrentPosition();
7839 int newCurrent = CurrentPosition();
7840 char *sz = CharPtrFromSPtr(lParam);
7841 const int lengthInserted = pdoc->InsertString(insertPos, sz, istrlen(sz));
7842 if (newCurrent > insertPos)
7843 newCurrent += lengthInserted;
7844 SetEmptySelection(newCurrent);
7845 return 0;
7848 case SCI_CHANGEINSERTION:
7849 PLATFORM_ASSERT(lParam);
7850 pdoc->ChangeInsertion(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7851 return 0;
7853 case SCI_APPENDTEXT:
7854 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), static_cast<int>(wParam));
7855 return 0;
7857 case SCI_CLEARALL:
7858 ClearAll();
7859 return 0;
7861 case SCI_DELETERANGE:
7862 pdoc->DeleteChars(static_cast<int>(wParam), static_cast<int>(lParam));
7863 return 0;
7865 case SCI_CLEARDOCUMENTSTYLE:
7866 ClearDocumentStyle();
7867 return 0;
7869 case SCI_SETUNDOCOLLECTION:
7870 pdoc->SetUndoCollection(wParam != 0);
7871 return 0;
7873 case SCI_GETUNDOCOLLECTION:
7874 return pdoc->IsCollectingUndo();
7876 case SCI_BEGINUNDOACTION:
7877 pdoc->BeginUndoAction();
7878 return 0;
7880 case SCI_ENDUNDOACTION:
7881 pdoc->EndUndoAction();
7882 return 0;
7884 case SCI_GETCARETPERIOD:
7885 return caret.period;
7887 case SCI_SETCARETPERIOD:
7888 CaretSetPeriod(static_cast<int>(wParam));
7889 break;
7891 case SCI_GETWORDCHARS:
7892 return pdoc->GetCharsOfClass(CharClassify::ccWord, reinterpret_cast<unsigned char *>(lParam));
7894 case SCI_SETWORDCHARS: {
7895 pdoc->SetDefaultCharClasses(false);
7896 if (lParam == 0)
7897 return 0;
7898 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
7900 break;
7902 case SCI_GETWHITESPACECHARS:
7903 return pdoc->GetCharsOfClass(CharClassify::ccSpace, reinterpret_cast<unsigned char *>(lParam));
7905 case SCI_SETWHITESPACECHARS: {
7906 if (lParam == 0)
7907 return 0;
7908 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
7910 break;
7912 case SCI_GETPUNCTUATIONCHARS:
7913 return pdoc->GetCharsOfClass(CharClassify::ccPunctuation, reinterpret_cast<unsigned char *>(lParam));
7915 case SCI_SETPUNCTUATIONCHARS: {
7916 if (lParam == 0)
7917 return 0;
7918 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccPunctuation);
7920 break;
7922 case SCI_SETCHARSDEFAULT:
7923 pdoc->SetDefaultCharClasses(true);
7924 break;
7926 case SCI_GETLENGTH:
7927 return pdoc->Length();
7929 case SCI_ALLOCATE:
7930 pdoc->Allocate(static_cast<int>(wParam));
7931 break;
7933 case SCI_GETCHARAT:
7934 return pdoc->CharAt(static_cast<int>(wParam));
7936 case SCI_SETCURRENTPOS:
7937 if (sel.IsRectangular()) {
7938 sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
7939 SetRectangularRange();
7940 Redraw();
7941 } else {
7942 SetSelection(static_cast<int>(wParam), sel.MainAnchor());
7944 break;
7946 case SCI_GETCURRENTPOS:
7947 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
7949 case SCI_SETANCHOR:
7950 if (sel.IsRectangular()) {
7951 sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
7952 SetRectangularRange();
7953 Redraw();
7954 } else {
7955 SetSelection(sel.MainCaret(), static_cast<int>(wParam));
7957 break;
7959 case SCI_GETANCHOR:
7960 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
7962 case SCI_SETSELECTIONSTART:
7963 SetSelection(Platform::Maximum(sel.MainCaret(), static_cast<int>(wParam)), static_cast<int>(wParam));
7964 break;
7966 case SCI_GETSELECTIONSTART:
7967 return sel.LimitsForRectangularElseMain().start.Position();
7969 case SCI_SETSELECTIONEND:
7970 SetSelection(static_cast<int>(wParam), Platform::Minimum(sel.MainAnchor(), static_cast<int>(wParam)));
7971 break;
7973 case SCI_GETSELECTIONEND:
7974 return sel.LimitsForRectangularElseMain().end.Position();
7976 case SCI_SETEMPTYSELECTION:
7977 SetEmptySelection(static_cast<int>(wParam));
7978 break;
7980 case SCI_SETPRINTMAGNIFICATION:
7981 printParameters.magnification = static_cast<int>(wParam);
7982 break;
7984 case SCI_GETPRINTMAGNIFICATION:
7985 return printParameters.magnification;
7987 case SCI_SETPRINTCOLOURMODE:
7988 printParameters.colourMode = static_cast<int>(wParam);
7989 break;
7991 case SCI_GETPRINTCOLOURMODE:
7992 return printParameters.colourMode;
7994 case SCI_SETPRINTWRAPMODE:
7995 printParameters.wrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
7996 break;
7998 case SCI_GETPRINTWRAPMODE:
7999 return printParameters.wrapState;
8001 case SCI_GETSTYLEAT:
8002 if (static_cast<int>(wParam) >= pdoc->Length())
8003 return 0;
8004 else
8005 return pdoc->StyleAt(static_cast<int>(wParam));
8007 case SCI_REDO:
8008 Redo();
8009 break;
8011 case SCI_SELECTALL:
8012 SelectAll();
8013 break;
8015 case SCI_SETSAVEPOINT:
8016 pdoc->SetSavePoint();
8017 break;
8019 case SCI_GETSTYLEDTEXT: {
8020 if (lParam == 0)
8021 return 0;
8022 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
8023 int iPlace = 0;
8024 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
8025 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
8026 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
8028 tr->lpstrText[iPlace] = '\0';
8029 tr->lpstrText[iPlace + 1] = '\0';
8030 return iPlace;
8033 case SCI_CANREDO:
8034 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
8036 case SCI_MARKERLINEFROMHANDLE:
8037 return pdoc->LineFromHandle(static_cast<int>(wParam));
8039 case SCI_MARKERDELETEHANDLE:
8040 pdoc->DeleteMarkFromHandle(static_cast<int>(wParam));
8041 break;
8043 case SCI_GETVIEWWS:
8044 return vs.viewWhitespace;
8046 case SCI_SETVIEWWS:
8047 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
8048 Redraw();
8049 break;
8051 case SCI_GETWHITESPACESIZE:
8052 return vs.whitespaceSize;
8054 case SCI_SETWHITESPACESIZE:
8055 vs.whitespaceSize = static_cast<int>(wParam);
8056 Redraw();
8057 break;
8059 case SCI_POSITIONFROMPOINT:
8060 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
8061 false, false);
8063 case SCI_POSITIONFROMPOINTCLOSE:
8064 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
8065 true, false);
8067 case SCI_CHARPOSITIONFROMPOINT:
8068 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
8069 false, true);
8071 case SCI_CHARPOSITIONFROMPOINTCLOSE:
8072 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
8073 true, true);
8075 case SCI_GOTOLINE:
8076 GoToLine(static_cast<int>(wParam));
8077 break;
8079 case SCI_GOTOPOS:
8080 SetEmptySelection(static_cast<int>(wParam));
8081 EnsureCaretVisible();
8082 break;
8084 case SCI_GETCURLINE: {
8085 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
8086 int lineStart = pdoc->LineStart(lineCurrentPos);
8087 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
8088 if (lParam == 0) {
8089 return 1 + lineEnd - lineStart;
8091 PLATFORM_ASSERT(wParam > 0);
8092 char *ptr = CharPtrFromSPtr(lParam);
8093 unsigned int iPlace = 0;
8094 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
8095 ptr[iPlace++] = pdoc->CharAt(iChar);
8097 ptr[iPlace] = '\0';
8098 return sel.MainCaret() - lineStart;
8101 case SCI_GETENDSTYLED:
8102 return pdoc->GetEndStyled();
8104 case SCI_GETEOLMODE:
8105 return pdoc->eolMode;
8107 case SCI_SETEOLMODE:
8108 pdoc->eolMode = static_cast<int>(wParam);
8109 break;
8111 case SCI_SETLINEENDTYPESALLOWED:
8112 if (pdoc->SetLineEndTypesAllowed(static_cast<int>(wParam))) {
8113 cs.Clear();
8114 cs.InsertLines(0, pdoc->LinesTotal() - 1);
8115 SetAnnotationHeights(0, pdoc->LinesTotal());
8116 InvalidateStyleRedraw();
8118 break;
8120 case SCI_GETLINEENDTYPESALLOWED:
8121 return pdoc->GetLineEndTypesAllowed();
8123 case SCI_GETLINEENDTYPESACTIVE:
8124 return pdoc->GetLineEndTypesActive();
8126 case SCI_STARTSTYLING:
8127 pdoc->StartStyling(static_cast<int>(wParam), static_cast<char>(lParam));
8128 break;
8130 case SCI_SETSTYLING:
8131 pdoc->SetStyleFor(static_cast<int>(wParam), static_cast<char>(lParam));
8132 break;
8134 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
8135 if (lParam == 0)
8136 return 0;
8137 pdoc->SetStyles(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
8138 break;
8140 case SCI_SETBUFFEREDDRAW:
8141 bufferedDraw = wParam != 0;
8142 break;
8144 case SCI_GETBUFFEREDDRAW:
8145 return bufferedDraw;
8147 case SCI_GETTWOPHASEDRAW:
8148 return twoPhaseDraw;
8150 case SCI_SETTWOPHASEDRAW:
8151 twoPhaseDraw = wParam != 0;
8152 InvalidateStyleRedraw();
8153 break;
8155 case SCI_SETFONTQUALITY:
8156 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
8157 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
8158 InvalidateStyleRedraw();
8159 break;
8161 case SCI_GETFONTQUALITY:
8162 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
8164 case SCI_SETTABWIDTH:
8165 if (wParam > 0) {
8166 pdoc->tabInChars = static_cast<int>(wParam);
8167 if (pdoc->indentInChars == 0)
8168 pdoc->actualIndentInChars = pdoc->tabInChars;
8170 InvalidateStyleRedraw();
8171 break;
8173 case SCI_GETTABWIDTH:
8174 return pdoc->tabInChars;
8176 case SCI_SETINDENT:
8177 pdoc->indentInChars = static_cast<int>(wParam);
8178 if (pdoc->indentInChars != 0)
8179 pdoc->actualIndentInChars = pdoc->indentInChars;
8180 else
8181 pdoc->actualIndentInChars = pdoc->tabInChars;
8182 InvalidateStyleRedraw();
8183 break;
8185 case SCI_GETINDENT:
8186 return pdoc->indentInChars;
8188 case SCI_SETUSETABS:
8189 pdoc->useTabs = wParam != 0;
8190 InvalidateStyleRedraw();
8191 break;
8193 case SCI_GETUSETABS:
8194 return pdoc->useTabs;
8196 case SCI_SETLINEINDENTATION:
8197 pdoc->SetLineIndentation(static_cast<int>(wParam), static_cast<int>(lParam));
8198 break;
8200 case SCI_GETLINEINDENTATION:
8201 return pdoc->GetLineIndentation(static_cast<int>(wParam));
8203 case SCI_GETLINEINDENTPOSITION:
8204 return pdoc->GetLineIndentPosition(static_cast<int>(wParam));
8206 case SCI_SETTABINDENTS:
8207 pdoc->tabIndents = wParam != 0;
8208 break;
8210 case SCI_GETTABINDENTS:
8211 return pdoc->tabIndents;
8213 case SCI_SETBACKSPACEUNINDENTS:
8214 pdoc->backspaceUnindents = wParam != 0;
8215 break;
8217 case SCI_GETBACKSPACEUNINDENTS:
8218 return pdoc->backspaceUnindents;
8220 case SCI_SETMOUSEDWELLTIME:
8221 dwellDelay = static_cast<int>(wParam);
8222 ticksToDwell = dwellDelay;
8223 break;
8225 case SCI_GETMOUSEDWELLTIME:
8226 return dwellDelay;
8228 case SCI_WORDSTARTPOSITION:
8229 return pdoc->ExtendWordSelect(static_cast<int>(wParam), -1, lParam != 0);
8231 case SCI_WORDENDPOSITION:
8232 return pdoc->ExtendWordSelect(static_cast<int>(wParam), 1, lParam != 0);
8234 case SCI_SETWRAPMODE:
8235 if (vs.SetWrapState(static_cast<int>(wParam))) {
8236 xOffset = 0;
8237 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
8238 InvalidateStyleRedraw();
8239 ReconfigureScrollBars();
8241 break;
8243 case SCI_GETWRAPMODE:
8244 return vs.wrapState;
8246 case SCI_SETWRAPVISUALFLAGS:
8247 if (vs.SetWrapVisualFlags(static_cast<int>(wParam))) {
8248 InvalidateStyleRedraw();
8249 ReconfigureScrollBars();
8251 break;
8253 case SCI_GETWRAPVISUALFLAGS:
8254 return vs.wrapVisualFlags;
8256 case SCI_SETWRAPVISUALFLAGSLOCATION:
8257 if (vs.SetWrapVisualFlagsLocation(static_cast<int>(wParam))) {
8258 InvalidateStyleRedraw();
8260 break;
8262 case SCI_GETWRAPVISUALFLAGSLOCATION:
8263 return vs.wrapVisualFlagsLocation;
8265 case SCI_SETWRAPSTARTINDENT:
8266 if (vs.SetWrapVisualStartIndent(static_cast<int>(wParam))) {
8267 InvalidateStyleRedraw();
8268 ReconfigureScrollBars();
8270 break;
8272 case SCI_GETWRAPSTARTINDENT:
8273 return vs.wrapVisualStartIndent;
8275 case SCI_SETWRAPINDENTMODE:
8276 if (vs.SetWrapIndentMode(static_cast<int>(wParam))) {
8277 InvalidateStyleRedraw();
8278 ReconfigureScrollBars();
8280 break;
8282 case SCI_GETWRAPINDENTMODE:
8283 return vs.wrapIndentMode;
8285 case SCI_SETLAYOUTCACHE:
8286 llc.SetLevel(static_cast<int>(wParam));
8287 break;
8289 case SCI_GETLAYOUTCACHE:
8290 return llc.GetLevel();
8292 case SCI_SETPOSITIONCACHE:
8293 posCache.SetSize(wParam);
8294 break;
8296 case SCI_GETPOSITIONCACHE:
8297 return posCache.GetSize();
8299 case SCI_SETSCROLLWIDTH:
8300 PLATFORM_ASSERT(wParam > 0);
8301 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
8302 lineWidthMaxSeen = 0;
8303 scrollWidth = static_cast<int>(wParam);
8304 SetScrollBars();
8306 break;
8308 case SCI_GETSCROLLWIDTH:
8309 return scrollWidth;
8311 case SCI_SETSCROLLWIDTHTRACKING:
8312 trackLineWidth = wParam != 0;
8313 break;
8315 case SCI_GETSCROLLWIDTHTRACKING:
8316 return trackLineWidth;
8318 case SCI_LINESJOIN:
8319 LinesJoin();
8320 break;
8322 case SCI_LINESSPLIT:
8323 LinesSplit(static_cast<int>(wParam));
8324 break;
8326 case SCI_TEXTWIDTH:
8327 PLATFORM_ASSERT(wParam < vs.styles.size());
8328 PLATFORM_ASSERT(lParam);
8329 return TextWidth(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
8331 case SCI_TEXTHEIGHT:
8332 return vs.lineHeight;
8334 case SCI_SETENDATLASTLINE:
8335 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
8336 if (endAtLastLine != (wParam != 0)) {
8337 endAtLastLine = wParam != 0;
8338 SetScrollBars();
8340 break;
8342 case SCI_GETENDATLASTLINE:
8343 return endAtLastLine;
8345 case SCI_SETCARETSTICKY:
8346 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
8347 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
8348 caretSticky = static_cast<int>(wParam);
8350 break;
8352 case SCI_GETCARETSTICKY:
8353 return caretSticky;
8355 case SCI_TOGGLECARETSTICKY:
8356 caretSticky = !caretSticky;
8357 break;
8359 case SCI_GETCOLUMN:
8360 return pdoc->GetColumn(static_cast<int>(wParam));
8362 case SCI_FINDCOLUMN:
8363 return pdoc->FindColumn(static_cast<int>(wParam), static_cast<int>(lParam));
8365 case SCI_SETHSCROLLBAR :
8366 if (horizontalScrollBarVisible != (wParam != 0)) {
8367 horizontalScrollBarVisible = wParam != 0;
8368 SetScrollBars();
8369 ReconfigureScrollBars();
8371 break;
8373 case SCI_GETHSCROLLBAR:
8374 return horizontalScrollBarVisible;
8376 case SCI_SETVSCROLLBAR:
8377 if (verticalScrollBarVisible != (wParam != 0)) {
8378 verticalScrollBarVisible = wParam != 0;
8379 SetScrollBars();
8380 ReconfigureScrollBars();
8381 if (verticalScrollBarVisible)
8382 SetVerticalScrollPos();
8384 break;
8386 case SCI_GETVSCROLLBAR:
8387 return verticalScrollBarVisible;
8389 case SCI_SETINDENTATIONGUIDES:
8390 vs.viewIndentationGuides = IndentView(wParam);
8391 Redraw();
8392 break;
8394 case SCI_GETINDENTATIONGUIDES:
8395 return vs.viewIndentationGuides;
8397 case SCI_SETHIGHLIGHTGUIDE:
8398 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
8399 highlightGuideColumn = static_cast<int>(wParam);
8400 Redraw();
8402 break;
8404 case SCI_GETHIGHLIGHTGUIDE:
8405 return highlightGuideColumn;
8407 case SCI_GETLINEENDPOSITION:
8408 return pdoc->LineEnd(static_cast<int>(wParam));
8410 case SCI_SETCODEPAGE:
8411 if (ValidCodePage(static_cast<int>(wParam))) {
8412 if (pdoc->SetDBCSCodePage(static_cast<int>(wParam))) {
8413 cs.Clear();
8414 cs.InsertLines(0, pdoc->LinesTotal() - 1);
8415 SetAnnotationHeights(0, pdoc->LinesTotal());
8416 InvalidateStyleRedraw();
8417 SetRepresentations();
8420 break;
8422 case SCI_GETCODEPAGE:
8423 return pdoc->dbcsCodePage;
8425 #ifdef INCLUDE_DEPRECATED_FEATURES
8426 case SCI_SETUSEPALETTE:
8427 InvalidateStyleRedraw();
8428 break;
8430 case SCI_GETUSEPALETTE:
8431 return 0;
8432 #endif
8434 // Marker definition and setting
8435 case SCI_MARKERDEFINE:
8436 if (wParam <= MARKER_MAX) {
8437 vs.markers[wParam].markType = static_cast<int>(lParam);
8438 vs.CalcLargestMarkerHeight();
8440 InvalidateStyleData();
8441 RedrawSelMargin();
8442 break;
8444 case SCI_MARKERSYMBOLDEFINED:
8445 if (wParam <= MARKER_MAX)
8446 return vs.markers[wParam].markType;
8447 else
8448 return 0;
8450 case SCI_MARKERSETFORE:
8451 if (wParam <= MARKER_MAX)
8452 vs.markers[wParam].fore = ColourDesired(static_cast<long>(lParam));
8453 InvalidateStyleData();
8454 RedrawSelMargin();
8455 break;
8456 case SCI_MARKERSETBACKSELECTED:
8457 if (wParam <= MARKER_MAX)
8458 vs.markers[wParam].backSelected = ColourDesired(static_cast<long>(lParam));
8459 InvalidateStyleData();
8460 RedrawSelMargin();
8461 break;
8462 case SCI_MARKERENABLEHIGHLIGHT:
8463 highlightDelimiter.isEnabled = wParam == 1;
8464 RedrawSelMargin();
8465 break;
8466 case SCI_MARKERSETBACK:
8467 if (wParam <= MARKER_MAX)
8468 vs.markers[wParam].back = ColourDesired(static_cast<long>(lParam));
8469 InvalidateStyleData();
8470 RedrawSelMargin();
8471 break;
8472 case SCI_MARKERSETALPHA:
8473 if (wParam <= MARKER_MAX)
8474 vs.markers[wParam].alpha = static_cast<int>(lParam);
8475 InvalidateStyleRedraw();
8476 break;
8477 case SCI_MARKERADD: {
8478 int markerID = pdoc->AddMark(static_cast<int>(wParam), static_cast<int>(lParam));
8479 return markerID;
8481 case SCI_MARKERADDSET:
8482 if (lParam != 0)
8483 pdoc->AddMarkSet(static_cast<int>(wParam), static_cast<int>(lParam));
8484 break;
8486 case SCI_MARKERDELETE:
8487 pdoc->DeleteMark(static_cast<int>(wParam), static_cast<int>(lParam));
8488 break;
8490 case SCI_MARKERDELETEALL:
8491 pdoc->DeleteAllMarks(static_cast<int>(wParam));
8492 break;
8494 case SCI_MARKERGET:
8495 return pdoc->GetMark(static_cast<int>(wParam));
8497 case SCI_MARKERNEXT:
8498 return pdoc->MarkerNext(static_cast<int>(wParam), static_cast<int>(lParam));
8500 case SCI_MARKERPREVIOUS: {
8501 for (int iLine = static_cast<int>(wParam); iLine >= 0; iLine--) {
8502 if ((pdoc->GetMark(iLine) & lParam) != 0)
8503 return iLine;
8506 return -1;
8508 case SCI_MARKERDEFINEPIXMAP:
8509 if (wParam <= MARKER_MAX) {
8510 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
8511 vs.CalcLargestMarkerHeight();
8513 InvalidateStyleData();
8514 RedrawSelMargin();
8515 break;
8517 case SCI_RGBAIMAGESETWIDTH:
8518 sizeRGBAImage.x = static_cast<XYPOSITION>(wParam);
8519 break;
8521 case SCI_RGBAIMAGESETHEIGHT:
8522 sizeRGBAImage.y = static_cast<XYPOSITION>(wParam);
8523 break;
8525 case SCI_RGBAIMAGESETSCALE:
8526 scaleRGBAImage = static_cast<float>(wParam);
8527 break;
8529 case SCI_MARKERDEFINERGBAIMAGE:
8530 if (wParam <= MARKER_MAX) {
8531 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0f, reinterpret_cast<unsigned char *>(lParam));
8532 vs.CalcLargestMarkerHeight();
8534 InvalidateStyleData();
8535 RedrawSelMargin();
8536 break;
8538 case SCI_SETMARGINTYPEN:
8539 if (ValidMargin(wParam)) {
8540 vs.ms[wParam].style = static_cast<int>(lParam);
8541 InvalidateStyleRedraw();
8543 break;
8545 case SCI_GETMARGINTYPEN:
8546 if (ValidMargin(wParam))
8547 return vs.ms[wParam].style;
8548 else
8549 return 0;
8551 case SCI_SETMARGINWIDTHN:
8552 if (ValidMargin(wParam)) {
8553 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
8554 if (vs.ms[wParam].width != lParam) {
8555 lastXChosen += static_cast<int>(lParam) - vs.ms[wParam].width;
8556 vs.ms[wParam].width = static_cast<int>(lParam);
8557 InvalidateStyleRedraw();
8560 break;
8562 case SCI_GETMARGINWIDTHN:
8563 if (ValidMargin(wParam))
8564 return vs.ms[wParam].width;
8565 else
8566 return 0;
8568 case SCI_SETMARGINMASKN:
8569 if (ValidMargin(wParam)) {
8570 vs.ms[wParam].mask = static_cast<int>(lParam);
8571 InvalidateStyleRedraw();
8573 break;
8575 case SCI_GETMARGINMASKN:
8576 if (ValidMargin(wParam))
8577 return vs.ms[wParam].mask;
8578 else
8579 return 0;
8581 case SCI_SETMARGINSENSITIVEN:
8582 if (ValidMargin(wParam)) {
8583 vs.ms[wParam].sensitive = lParam != 0;
8584 InvalidateStyleRedraw();
8586 break;
8588 case SCI_GETMARGINSENSITIVEN:
8589 if (ValidMargin(wParam))
8590 return vs.ms[wParam].sensitive ? 1 : 0;
8591 else
8592 return 0;
8594 case SCI_SETMARGINCURSORN:
8595 if (ValidMargin(wParam))
8596 vs.ms[wParam].cursor = static_cast<int>(lParam);
8597 break;
8599 case SCI_GETMARGINCURSORN:
8600 if (ValidMargin(wParam))
8601 return vs.ms[wParam].cursor;
8602 else
8603 return 0;
8605 case SCI_STYLECLEARALL:
8606 vs.ClearStyles();
8607 InvalidateStyleRedraw();
8608 break;
8610 case SCI_STYLESETFORE:
8611 case SCI_STYLESETBACK:
8612 case SCI_STYLESETBOLD:
8613 case SCI_STYLESETWEIGHT:
8614 case SCI_STYLESETITALIC:
8615 case SCI_STYLESETEOLFILLED:
8616 case SCI_STYLESETSIZE:
8617 case SCI_STYLESETSIZEFRACTIONAL:
8618 case SCI_STYLESETFONT:
8619 case SCI_STYLESETUNDERLINE:
8620 case SCI_STYLESETCASE:
8621 case SCI_STYLESETCHARACTERSET:
8622 case SCI_STYLESETVISIBLE:
8623 case SCI_STYLESETCHANGEABLE:
8624 case SCI_STYLESETHOTSPOT:
8625 StyleSetMessage(iMessage, wParam, lParam);
8626 break;
8628 case SCI_STYLEGETFORE:
8629 case SCI_STYLEGETBACK:
8630 case SCI_STYLEGETBOLD:
8631 case SCI_STYLEGETWEIGHT:
8632 case SCI_STYLEGETITALIC:
8633 case SCI_STYLEGETEOLFILLED:
8634 case SCI_STYLEGETSIZE:
8635 case SCI_STYLEGETSIZEFRACTIONAL:
8636 case SCI_STYLEGETFONT:
8637 case SCI_STYLEGETUNDERLINE:
8638 case SCI_STYLEGETCASE:
8639 case SCI_STYLEGETCHARACTERSET:
8640 case SCI_STYLEGETVISIBLE:
8641 case SCI_STYLEGETCHANGEABLE:
8642 case SCI_STYLEGETHOTSPOT:
8643 return StyleGetMessage(iMessage, wParam, lParam);
8645 case SCI_STYLERESETDEFAULT:
8646 vs.ResetDefaultStyle();
8647 InvalidateStyleRedraw();
8648 break;
8649 case SCI_SETSTYLEBITS:
8650 vs.EnsureStyle((1 << wParam) - 1);
8651 pdoc->SetStylingBits(static_cast<int>(wParam));
8652 break;
8654 case SCI_GETSTYLEBITS:
8655 return pdoc->stylingBits;
8657 case SCI_SETLINESTATE:
8658 return pdoc->SetLineState(static_cast<int>(wParam), static_cast<int>(lParam));
8660 case SCI_GETLINESTATE:
8661 return pdoc->GetLineState(static_cast<int>(wParam));
8663 case SCI_GETMAXLINESTATE:
8664 return pdoc->GetMaxLineState();
8666 case SCI_GETCARETLINEVISIBLE:
8667 return vs.showCaretLineBackground;
8668 case SCI_SETCARETLINEVISIBLE:
8669 vs.showCaretLineBackground = wParam != 0;
8670 InvalidateStyleRedraw();
8671 break;
8672 case SCI_GETCARETLINEVISIBLEALWAYS:
8673 return vs.alwaysShowCaretLineBackground;
8674 case SCI_SETCARETLINEVISIBLEALWAYS:
8675 vs.alwaysShowCaretLineBackground = wParam != 0;
8676 InvalidateStyleRedraw();
8677 break;
8679 case SCI_GETCARETLINEBACK:
8680 return vs.caretLineBackground.AsLong();
8681 case SCI_SETCARETLINEBACK:
8682 vs.caretLineBackground = static_cast<int>(wParam);
8683 InvalidateStyleRedraw();
8684 break;
8685 case SCI_GETCARETLINEBACKALPHA:
8686 return vs.caretLineAlpha;
8687 case SCI_SETCARETLINEBACKALPHA:
8688 vs.caretLineAlpha = static_cast<int>(wParam);
8689 InvalidateStyleRedraw();
8690 break;
8692 // Folding messages
8694 case SCI_VISIBLEFROMDOCLINE:
8695 return cs.DisplayFromDoc(static_cast<int>(wParam));
8697 case SCI_DOCLINEFROMVISIBLE:
8698 return cs.DocFromDisplay(static_cast<int>(wParam));
8700 case SCI_WRAPCOUNT:
8701 return WrapCount(static_cast<int>(wParam));
8703 case SCI_SETFOLDLEVEL: {
8704 int prev = pdoc->SetLevel(static_cast<int>(wParam), static_cast<int>(lParam));
8705 if (prev != static_cast<int>(lParam))
8706 RedrawSelMargin();
8707 return prev;
8710 case SCI_GETFOLDLEVEL:
8711 return pdoc->GetLevel(static_cast<int>(wParam));
8713 case SCI_GETLASTCHILD:
8714 return pdoc->GetLastChild(static_cast<int>(wParam), static_cast<int>(lParam));
8716 case SCI_GETFOLDPARENT:
8717 return pdoc->GetFoldParent(static_cast<int>(wParam));
8719 case SCI_SHOWLINES:
8720 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), true);
8721 SetScrollBars();
8722 Redraw();
8723 break;
8725 case SCI_HIDELINES:
8726 if (wParam > 0)
8727 cs.SetVisible(static_cast<int>(wParam), static_cast<int>(lParam), false);
8728 SetScrollBars();
8729 Redraw();
8730 break;
8732 case SCI_GETLINEVISIBLE:
8733 return cs.GetVisible(static_cast<int>(wParam));
8735 case SCI_GETALLLINESVISIBLE:
8736 return cs.HiddenLines() ? 0 : 1;
8738 case SCI_SETFOLDEXPANDED:
8739 SetFoldExpanded(static_cast<int>(wParam), lParam != 0);
8740 break;
8742 case SCI_GETFOLDEXPANDED:
8743 return cs.GetExpanded(static_cast<int>(wParam));
8745 case SCI_SETAUTOMATICFOLD:
8746 foldAutomatic = static_cast<int>(wParam);
8747 break;
8749 case SCI_GETAUTOMATICFOLD:
8750 return foldAutomatic;
8752 case SCI_SETFOLDFLAGS:
8753 foldFlags = static_cast<int>(wParam);
8754 Redraw();
8755 break;
8757 case SCI_TOGGLEFOLD:
8758 FoldLine(static_cast<int>(wParam), SC_FOLDACTION_TOGGLE);
8759 break;
8761 case SCI_FOLDLINE:
8762 FoldLine(static_cast<int>(wParam), static_cast<int>(lParam));
8763 break;
8765 case SCI_FOLDCHILDREN:
8766 FoldExpand(static_cast<int>(wParam), static_cast<int>(lParam), pdoc->GetLevel(static_cast<int>(wParam)));
8767 break;
8769 case SCI_FOLDALL:
8770 FoldAll(static_cast<int>(wParam));
8771 break;
8773 case SCI_EXPANDCHILDREN:
8774 FoldExpand(static_cast<int>(wParam), SC_FOLDACTION_EXPAND, static_cast<int>(lParam));
8775 break;
8777 case SCI_CONTRACTEDFOLDNEXT:
8778 return ContractedFoldNext(static_cast<int>(wParam));
8780 case SCI_ENSUREVISIBLE:
8781 EnsureLineVisible(static_cast<int>(wParam), false);
8782 break;
8784 case SCI_ENSUREVISIBLEENFORCEPOLICY:
8785 EnsureLineVisible(static_cast<int>(wParam), true);
8786 break;
8788 case SCI_SCROLLRANGE:
8789 ScrollRange(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
8790 break;
8792 case SCI_SEARCHANCHOR:
8793 SearchAnchor();
8794 break;
8796 case SCI_SEARCHNEXT:
8797 case SCI_SEARCHPREV:
8798 return SearchText(iMessage, wParam, lParam);
8800 case SCI_SETXCARETPOLICY:
8801 caretXPolicy = static_cast<int>(wParam);
8802 caretXSlop = static_cast<int>(lParam);
8803 break;
8805 case SCI_SETYCARETPOLICY:
8806 caretYPolicy = static_cast<int>(wParam);
8807 caretYSlop = static_cast<int>(lParam);
8808 break;
8810 case SCI_SETVISIBLEPOLICY:
8811 visiblePolicy = static_cast<int>(wParam);
8812 visibleSlop = static_cast<int>(lParam);
8813 break;
8815 case SCI_LINESONSCREEN:
8816 return LinesOnScreen();
8818 case SCI_SETSELFORE:
8819 vs.selColours.fore = ColourOptional(wParam, lParam);
8820 vs.selAdditionalForeground = ColourDesired(static_cast<long>(lParam));
8821 InvalidateStyleRedraw();
8822 break;
8824 case SCI_SETSELBACK:
8825 vs.selColours.back = ColourOptional(wParam, lParam);
8826 vs.selAdditionalBackground = ColourDesired(static_cast<long>(lParam));
8827 InvalidateStyleRedraw();
8828 break;
8830 case SCI_SETSELALPHA:
8831 vs.selAlpha = static_cast<int>(wParam);
8832 vs.selAdditionalAlpha = static_cast<int>(wParam);
8833 InvalidateStyleRedraw();
8834 break;
8836 case SCI_GETSELALPHA:
8837 return vs.selAlpha;
8839 case SCI_GETSELEOLFILLED:
8840 return vs.selEOLFilled;
8842 case SCI_SETSELEOLFILLED:
8843 vs.selEOLFilled = wParam != 0;
8844 InvalidateStyleRedraw();
8845 break;
8847 case SCI_SETWHITESPACEFORE:
8848 vs.whitespaceColours.fore = ColourOptional(wParam, lParam);
8849 InvalidateStyleRedraw();
8850 break;
8852 case SCI_SETWHITESPACEBACK:
8853 vs.whitespaceColours.back = ColourOptional(wParam, lParam);
8854 InvalidateStyleRedraw();
8855 break;
8857 case SCI_SETCARETFORE:
8858 vs.caretcolour = ColourDesired(static_cast<long>(wParam));
8859 InvalidateStyleRedraw();
8860 break;
8862 case SCI_GETCARETFORE:
8863 return vs.caretcolour.AsLong();
8865 case SCI_SETCARETSTYLE:
8866 if (wParam <= CARETSTYLE_BLOCK)
8867 vs.caretStyle = static_cast<int>(wParam);
8868 else
8869 /* Default to the line caret */
8870 vs.caretStyle = CARETSTYLE_LINE;
8871 InvalidateStyleRedraw();
8872 break;
8874 case SCI_GETCARETSTYLE:
8875 return vs.caretStyle;
8877 case SCI_SETCARETWIDTH:
8878 if (static_cast<int>(wParam) <= 0)
8879 vs.caretWidth = 0;
8880 else if (wParam >= 3)
8881 vs.caretWidth = 3;
8882 else
8883 vs.caretWidth = static_cast<int>(wParam);
8884 InvalidateStyleRedraw();
8885 break;
8887 case SCI_GETCARETWIDTH:
8888 return vs.caretWidth;
8890 case SCI_ASSIGNCMDKEY:
8891 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
8892 Platform::HighShortFromLong(static_cast<long>(wParam)), static_cast<unsigned int>(lParam));
8893 break;
8895 case SCI_CLEARCMDKEY:
8896 kmap.AssignCmdKey(Platform::LowShortFromLong(static_cast<long>(wParam)),
8897 Platform::HighShortFromLong(static_cast<long>(wParam)), SCI_NULL);
8898 break;
8900 case SCI_CLEARALLCMDKEYS:
8901 kmap.Clear();
8902 break;
8904 case SCI_INDICSETSTYLE:
8905 if (wParam <= INDIC_MAX) {
8906 vs.indicators[wParam].style = static_cast<int>(lParam);
8907 InvalidateStyleRedraw();
8909 break;
8911 case SCI_INDICGETSTYLE:
8912 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
8914 case SCI_INDICSETFORE:
8915 if (wParam <= INDIC_MAX) {
8916 vs.indicators[wParam].fore = ColourDesired(static_cast<long>(lParam));
8917 InvalidateStyleRedraw();
8919 break;
8921 case SCI_INDICGETFORE:
8922 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.AsLong() : 0;
8924 case SCI_INDICSETUNDER:
8925 if (wParam <= INDIC_MAX) {
8926 vs.indicators[wParam].under = lParam != 0;
8927 InvalidateStyleRedraw();
8929 break;
8931 case SCI_INDICGETUNDER:
8932 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
8934 case SCI_INDICSETALPHA:
8935 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8936 vs.indicators[wParam].fillAlpha = static_cast<int>(lParam);
8937 InvalidateStyleRedraw();
8939 break;
8941 case SCI_INDICGETALPHA:
8942 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
8944 case SCI_INDICSETOUTLINEALPHA:
8945 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8946 vs.indicators[wParam].outlineAlpha = static_cast<int>(lParam);
8947 InvalidateStyleRedraw();
8949 break;
8951 case SCI_INDICGETOUTLINEALPHA:
8952 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
8954 case SCI_SETINDICATORCURRENT:
8955 pdoc->decorations.SetCurrentIndicator(static_cast<int>(wParam));
8956 break;
8957 case SCI_GETINDICATORCURRENT:
8958 return pdoc->decorations.GetCurrentIndicator();
8959 case SCI_SETINDICATORVALUE:
8960 pdoc->decorations.SetCurrentValue(static_cast<int>(wParam));
8961 break;
8962 case SCI_GETINDICATORVALUE:
8963 return pdoc->decorations.GetCurrentValue();
8965 case SCI_INDICATORFILLRANGE:
8966 pdoc->DecorationFillRange(static_cast<int>(wParam), pdoc->decorations.GetCurrentValue(), static_cast<int>(lParam));
8967 break;
8969 case SCI_INDICATORCLEARRANGE:
8970 pdoc->DecorationFillRange(static_cast<int>(wParam), 0, static_cast<int>(lParam));
8971 break;
8973 case SCI_INDICATORALLONFOR:
8974 return pdoc->decorations.AllOnFor(static_cast<int>(wParam));
8976 case SCI_INDICATORVALUEAT:
8977 return pdoc->decorations.ValueAt(static_cast<int>(wParam), static_cast<int>(lParam));
8979 case SCI_INDICATORSTART:
8980 return pdoc->decorations.Start(static_cast<int>(wParam), static_cast<int>(lParam));
8982 case SCI_INDICATOREND:
8983 return pdoc->decorations.End(static_cast<int>(wParam), static_cast<int>(lParam));
8985 case SCI_LINEDOWN:
8986 case SCI_LINEDOWNEXTEND:
8987 case SCI_PARADOWN:
8988 case SCI_PARADOWNEXTEND:
8989 case SCI_LINEUP:
8990 case SCI_LINEUPEXTEND:
8991 case SCI_PARAUP:
8992 case SCI_PARAUPEXTEND:
8993 case SCI_CHARLEFT:
8994 case SCI_CHARLEFTEXTEND:
8995 case SCI_CHARRIGHT:
8996 case SCI_CHARRIGHTEXTEND:
8997 case SCI_WORDLEFT:
8998 case SCI_WORDLEFTEXTEND:
8999 case SCI_WORDRIGHT:
9000 case SCI_WORDRIGHTEXTEND:
9001 case SCI_WORDLEFTEND:
9002 case SCI_WORDLEFTENDEXTEND:
9003 case SCI_WORDRIGHTEND:
9004 case SCI_WORDRIGHTENDEXTEND:
9005 case SCI_HOME:
9006 case SCI_HOMEEXTEND:
9007 case SCI_LINEEND:
9008 case SCI_LINEENDEXTEND:
9009 case SCI_HOMEWRAP:
9010 case SCI_HOMEWRAPEXTEND:
9011 case SCI_LINEENDWRAP:
9012 case SCI_LINEENDWRAPEXTEND:
9013 case SCI_DOCUMENTSTART:
9014 case SCI_DOCUMENTSTARTEXTEND:
9015 case SCI_DOCUMENTEND:
9016 case SCI_DOCUMENTENDEXTEND:
9017 case SCI_SCROLLTOSTART:
9018 case SCI_SCROLLTOEND:
9020 case SCI_STUTTEREDPAGEUP:
9021 case SCI_STUTTEREDPAGEUPEXTEND:
9022 case SCI_STUTTEREDPAGEDOWN:
9023 case SCI_STUTTEREDPAGEDOWNEXTEND:
9025 case SCI_PAGEUP:
9026 case SCI_PAGEUPEXTEND:
9027 case SCI_PAGEDOWN:
9028 case SCI_PAGEDOWNEXTEND:
9029 case SCI_EDITTOGGLEOVERTYPE:
9030 case SCI_CANCEL:
9031 case SCI_DELETEBACK:
9032 case SCI_TAB:
9033 case SCI_BACKTAB:
9034 case SCI_NEWLINE:
9035 case SCI_FORMFEED:
9036 case SCI_VCHOME:
9037 case SCI_VCHOMEEXTEND:
9038 case SCI_VCHOMEWRAP:
9039 case SCI_VCHOMEWRAPEXTEND:
9040 case SCI_VCHOMEDISPLAY:
9041 case SCI_VCHOMEDISPLAYEXTEND:
9042 case SCI_ZOOMIN:
9043 case SCI_ZOOMOUT:
9044 case SCI_DELWORDLEFT:
9045 case SCI_DELWORDRIGHT:
9046 case SCI_DELWORDRIGHTEND:
9047 case SCI_DELLINELEFT:
9048 case SCI_DELLINERIGHT:
9049 case SCI_LINECOPY:
9050 case SCI_LINECUT:
9051 case SCI_LINEDELETE:
9052 case SCI_LINETRANSPOSE:
9053 case SCI_LINEDUPLICATE:
9054 case SCI_LOWERCASE:
9055 case SCI_UPPERCASE:
9056 case SCI_LINESCROLLDOWN:
9057 case SCI_LINESCROLLUP:
9058 case SCI_WORDPARTLEFT:
9059 case SCI_WORDPARTLEFTEXTEND:
9060 case SCI_WORDPARTRIGHT:
9061 case SCI_WORDPARTRIGHTEXTEND:
9062 case SCI_DELETEBACKNOTLINE:
9063 case SCI_HOMEDISPLAY:
9064 case SCI_HOMEDISPLAYEXTEND:
9065 case SCI_LINEENDDISPLAY:
9066 case SCI_LINEENDDISPLAYEXTEND:
9067 case SCI_LINEDOWNRECTEXTEND:
9068 case SCI_LINEUPRECTEXTEND:
9069 case SCI_CHARLEFTRECTEXTEND:
9070 case SCI_CHARRIGHTRECTEXTEND:
9071 case SCI_HOMERECTEXTEND:
9072 case SCI_VCHOMERECTEXTEND:
9073 case SCI_LINEENDRECTEXTEND:
9074 case SCI_PAGEUPRECTEXTEND:
9075 case SCI_PAGEDOWNRECTEXTEND:
9076 case SCI_SELECTIONDUPLICATE:
9077 return KeyCommand(iMessage);
9079 case SCI_BRACEHIGHLIGHT:
9080 SetBraceHighlight(static_cast<int>(wParam), static_cast<int>(lParam), STYLE_BRACELIGHT);
9081 break;
9083 case SCI_BRACEHIGHLIGHTINDICATOR:
9084 if (lParam >= 0 && lParam <= INDIC_MAX) {
9085 vs.braceHighlightIndicatorSet = wParam != 0;
9086 vs.braceHighlightIndicator = static_cast<int>(lParam);
9088 break;
9090 case SCI_BRACEBADLIGHT:
9091 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
9092 break;
9094 case SCI_BRACEBADLIGHTINDICATOR:
9095 if (lParam >= 0 && lParam <= INDIC_MAX) {
9096 vs.braceBadLightIndicatorSet = wParam != 0;
9097 vs.braceBadLightIndicator = static_cast<int>(lParam);
9099 break;
9101 case SCI_BRACEMATCH:
9102 // wParam is position of char to find brace for,
9103 // lParam is maximum amount of text to restyle to find it
9104 return pdoc->BraceMatch(static_cast<int>(wParam), static_cast<int>(lParam));
9106 case SCI_GETVIEWEOL:
9107 return vs.viewEOL;
9109 case SCI_SETVIEWEOL:
9110 vs.viewEOL = wParam != 0;
9111 InvalidateStyleRedraw();
9112 break;
9114 case SCI_SETZOOM:
9115 vs.zoomLevel = static_cast<int>(wParam);
9116 InvalidateStyleRedraw();
9117 NotifyZoom();
9118 break;
9120 case SCI_GETZOOM:
9121 return vs.zoomLevel;
9123 case SCI_GETEDGECOLUMN:
9124 return vs.theEdge;
9126 case SCI_SETEDGECOLUMN:
9127 vs.theEdge = static_cast<int>(wParam);
9128 InvalidateStyleRedraw();
9129 break;
9131 case SCI_GETEDGEMODE:
9132 return vs.edgeState;
9134 case SCI_SETEDGEMODE:
9135 vs.edgeState = static_cast<int>(wParam);
9136 InvalidateStyleRedraw();
9137 break;
9139 case SCI_GETEDGECOLOUR:
9140 return vs.edgecolour.AsLong();
9142 case SCI_SETEDGECOLOUR:
9143 vs.edgecolour = ColourDesired(static_cast<long>(wParam));
9144 InvalidateStyleRedraw();
9145 break;
9147 case SCI_GETDOCPOINTER:
9148 return reinterpret_cast<sptr_t>(pdoc);
9150 case SCI_SETDOCPOINTER:
9151 CancelModes();
9152 SetDocPointer(reinterpret_cast<Document *>(lParam));
9153 return 0;
9155 case SCI_CREATEDOCUMENT: {
9156 Document *doc = new Document();
9157 doc->AddRef();
9158 return reinterpret_cast<sptr_t>(doc);
9161 case SCI_ADDREFDOCUMENT:
9162 (reinterpret_cast<Document *>(lParam))->AddRef();
9163 break;
9165 case SCI_RELEASEDOCUMENT:
9166 (reinterpret_cast<Document *>(lParam))->Release();
9167 break;
9169 case SCI_CREATELOADER: {
9170 Document *doc = new Document();
9171 doc->AddRef();
9172 doc->Allocate(static_cast<int>(wParam));
9173 doc->SetUndoCollection(false);
9174 return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
9177 case SCI_SETMODEVENTMASK:
9178 modEventMask = static_cast<int>(wParam);
9179 return 0;
9181 case SCI_GETMODEVENTMASK:
9182 return modEventMask;
9184 case SCI_CONVERTEOLS:
9185 pdoc->ConvertLineEnds(static_cast<int>(wParam));
9186 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
9187 return 0;
9189 case SCI_SETLENGTHFORENCODE:
9190 lengthForEncode = static_cast<int>(wParam);
9191 return 0;
9193 case SCI_SELECTIONISRECTANGLE:
9194 return sel.selType == Selection::selRectangle ? 1 : 0;
9196 case SCI_SETSELECTIONMODE: {
9197 switch (wParam) {
9198 case SC_SEL_STREAM:
9199 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
9200 sel.selType = Selection::selStream;
9201 break;
9202 case SC_SEL_RECTANGLE:
9203 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
9204 sel.selType = Selection::selRectangle;
9205 break;
9206 case SC_SEL_LINES:
9207 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
9208 sel.selType = Selection::selLines;
9209 break;
9210 case SC_SEL_THIN:
9211 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
9212 sel.selType = Selection::selThin;
9213 break;
9214 default:
9215 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
9216 sel.selType = Selection::selStream;
9218 InvalidateSelection(sel.RangeMain(), true);
9219 break;
9221 case SCI_GETSELECTIONMODE:
9222 switch (sel.selType) {
9223 case Selection::selStream:
9224 return SC_SEL_STREAM;
9225 case Selection::selRectangle:
9226 return SC_SEL_RECTANGLE;
9227 case Selection::selLines:
9228 return SC_SEL_LINES;
9229 case Selection::selThin:
9230 return SC_SEL_THIN;
9231 default: // ?!
9232 return SC_SEL_STREAM;
9234 case SCI_GETLINESELSTARTPOSITION:
9235 case SCI_GETLINESELENDPOSITION: {
9236 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(static_cast<int>(wParam))),
9237 SelectionPosition(pdoc->LineEnd(static_cast<int>(wParam))));
9238 for (size_t r=0; r<sel.Count(); r++) {
9239 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
9240 if (portion.start.IsValid()) {
9241 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
9244 return INVALID_POSITION;
9247 case SCI_SETOVERTYPE:
9248 inOverstrike = wParam != 0;
9249 break;
9251 case SCI_GETOVERTYPE:
9252 return inOverstrike ? 1 : 0;
9254 case SCI_SETFOCUS:
9255 SetFocusState(wParam != 0);
9256 break;
9258 case SCI_GETFOCUS:
9259 return hasFocus;
9261 case SCI_SETSTATUS:
9262 errorStatus = static_cast<int>(wParam);
9263 break;
9265 case SCI_GETSTATUS:
9266 return errorStatus;
9268 case SCI_SETMOUSEDOWNCAPTURES:
9269 mouseDownCaptures = wParam != 0;
9270 break;
9272 case SCI_GETMOUSEDOWNCAPTURES:
9273 return mouseDownCaptures;
9275 case SCI_SETCURSOR:
9276 cursorMode = static_cast<int>(wParam);
9277 DisplayCursor(Window::cursorText);
9278 break;
9280 case SCI_GETCURSOR:
9281 return cursorMode;
9283 case SCI_SETCONTROLCHARSYMBOL:
9284 vs.controlCharSymbol = static_cast<int>(wParam);
9285 InvalidateStyleRedraw();
9286 break;
9288 case SCI_GETCONTROLCHARSYMBOL:
9289 return vs.controlCharSymbol;
9291 case SCI_SETREPRESENTATION:
9292 reprs.SetRepresentation(reinterpret_cast<const char *>(wParam), CharPtrFromSPtr(lParam));
9293 break;
9295 case SCI_GETREPRESENTATION: {
9296 Representation *repr = reprs.RepresentationFromCharacter(
9297 reinterpret_cast<const char *>(wParam), UTF8MaxBytes);
9298 if (repr) {
9299 return StringResult(lParam, repr->stringRep.c_str());
9301 return 0;
9304 case SCI_CLEARREPRESENTATION:
9305 reprs.ClearRepresentation(reinterpret_cast<const char *>(wParam));
9306 break;
9308 case SCI_STARTRECORD:
9309 recordingMacro = true;
9310 return 0;
9312 case SCI_STOPRECORD:
9313 recordingMacro = false;
9314 return 0;
9316 case SCI_MOVECARETINSIDEVIEW:
9317 MoveCaretInsideView();
9318 break;
9320 case SCI_SETFOLDMARGINCOLOUR:
9321 vs.foldmarginColour = ColourOptional(wParam, lParam);
9322 InvalidateStyleRedraw();
9323 break;
9325 case SCI_SETFOLDMARGINHICOLOUR:
9326 vs.foldmarginHighlightColour = ColourOptional(wParam, lParam);
9327 InvalidateStyleRedraw();
9328 break;
9330 case SCI_SETHOTSPOTACTIVEFORE:
9331 vs.hotspotColours.fore = ColourOptional(wParam, lParam);
9332 InvalidateStyleRedraw();
9333 break;
9335 case SCI_GETHOTSPOTACTIVEFORE:
9336 return vs.hotspotColours.fore.AsLong();
9338 case SCI_SETHOTSPOTACTIVEBACK:
9339 vs.hotspotColours.back = ColourOptional(wParam, lParam);
9340 InvalidateStyleRedraw();
9341 break;
9343 case SCI_GETHOTSPOTACTIVEBACK:
9344 return vs.hotspotColours.back.AsLong();
9346 case SCI_SETHOTSPOTACTIVEUNDERLINE:
9347 vs.hotspotUnderline = wParam != 0;
9348 InvalidateStyleRedraw();
9349 break;
9351 case SCI_GETHOTSPOTACTIVEUNDERLINE:
9352 return vs.hotspotUnderline ? 1 : 0;
9354 case SCI_SETHOTSPOTSINGLELINE:
9355 vs.hotspotSingleLine = wParam != 0;
9356 InvalidateStyleRedraw();
9357 break;
9359 case SCI_GETHOTSPOTSINGLELINE:
9360 return vs.hotspotSingleLine ? 1 : 0;
9362 case SCI_SETPASTECONVERTENDINGS:
9363 convertPastes = wParam != 0;
9364 break;
9366 case SCI_GETPASTECONVERTENDINGS:
9367 return convertPastes ? 1 : 0;
9369 case SCI_GETCHARACTERPOINTER:
9370 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
9372 case SCI_GETRANGEPOINTER:
9373 return reinterpret_cast<sptr_t>(pdoc->RangePointer(static_cast<int>(wParam), static_cast<int>(lParam)));
9375 case SCI_GETGAPPOSITION:
9376 return pdoc->GapPosition();
9378 case SCI_SETEXTRAASCENT:
9379 vs.extraAscent = static_cast<int>(wParam);
9380 InvalidateStyleRedraw();
9381 break;
9383 case SCI_GETEXTRAASCENT:
9384 return vs.extraAscent;
9386 case SCI_SETEXTRADESCENT:
9387 vs.extraDescent = static_cast<int>(wParam);
9388 InvalidateStyleRedraw();
9389 break;
9391 case SCI_GETEXTRADESCENT:
9392 return vs.extraDescent;
9394 case SCI_MARGINSETSTYLEOFFSET:
9395 vs.marginStyleOffset = static_cast<int>(wParam);
9396 InvalidateStyleRedraw();
9397 break;
9399 case SCI_MARGINGETSTYLEOFFSET:
9400 return vs.marginStyleOffset;
9402 case SCI_SETMARGINOPTIONS:
9403 marginOptions = static_cast<int>(wParam);
9404 break;
9406 case SCI_GETMARGINOPTIONS:
9407 return marginOptions;
9409 case SCI_MARGINSETTEXT:
9410 pdoc->MarginSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
9411 break;
9413 case SCI_MARGINGETTEXT: {
9414 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
9415 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
9418 case SCI_MARGINSETSTYLE:
9419 pdoc->MarginSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
9420 break;
9422 case SCI_MARGINGETSTYLE: {
9423 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
9424 return st.style;
9427 case SCI_MARGINSETSTYLES:
9428 pdoc->MarginSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
9429 break;
9431 case SCI_MARGINGETSTYLES: {
9432 const StyledText st = pdoc->MarginStyledText(static_cast<int>(wParam));
9433 return BytesResult(lParam, st.styles, st.length);
9436 case SCI_MARGINTEXTCLEARALL:
9437 pdoc->MarginClearAll();
9438 break;
9440 case SCI_ANNOTATIONSETTEXT:
9441 pdoc->AnnotationSetText(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
9442 break;
9444 case SCI_ANNOTATIONGETTEXT: {
9445 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
9446 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
9449 case SCI_ANNOTATIONGETSTYLE: {
9450 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
9451 return st.style;
9454 case SCI_ANNOTATIONSETSTYLE:
9455 pdoc->AnnotationSetStyle(static_cast<int>(wParam), static_cast<int>(lParam));
9456 break;
9458 case SCI_ANNOTATIONSETSTYLES:
9459 pdoc->AnnotationSetStyles(static_cast<int>(wParam), reinterpret_cast<const unsigned char *>(lParam));
9460 break;
9462 case SCI_ANNOTATIONGETSTYLES: {
9463 const StyledText st = pdoc->AnnotationStyledText(static_cast<int>(wParam));
9464 return BytesResult(lParam, st.styles, st.length);
9467 case SCI_ANNOTATIONGETLINES:
9468 return pdoc->AnnotationLines(static_cast<int>(wParam));
9470 case SCI_ANNOTATIONCLEARALL:
9471 pdoc->AnnotationClearAll();
9472 break;
9474 case SCI_ANNOTATIONSETVISIBLE:
9475 SetAnnotationVisible(static_cast<int>(wParam));
9476 break;
9478 case SCI_ANNOTATIONGETVISIBLE:
9479 return vs.annotationVisible;
9481 case SCI_ANNOTATIONSETSTYLEOFFSET:
9482 vs.annotationStyleOffset = static_cast<int>(wParam);
9483 InvalidateStyleRedraw();
9484 break;
9486 case SCI_ANNOTATIONGETSTYLEOFFSET:
9487 return vs.annotationStyleOffset;
9489 case SCI_RELEASEALLEXTENDEDSTYLES:
9490 vs.ReleaseAllExtendedStyles();
9491 break;
9493 case SCI_ALLOCATEEXTENDEDSTYLES:
9494 return vs.AllocateExtendedStyles(static_cast<int>(wParam));
9496 case SCI_ADDUNDOACTION:
9497 pdoc->AddUndoAction(static_cast<int>(wParam), lParam & UNDO_MAY_COALESCE);
9498 break;
9500 case SCI_SETMOUSESELECTIONRECTANGULARSWITCH:
9501 mouseSelectionRectangularSwitch = wParam != 0;
9502 break;
9504 case SCI_GETMOUSESELECTIONRECTANGULARSWITCH:
9505 return mouseSelectionRectangularSwitch;
9507 case SCI_SETMULTIPLESELECTION:
9508 multipleSelection = wParam != 0;
9509 InvalidateCaret();
9510 break;
9512 case SCI_GETMULTIPLESELECTION:
9513 return multipleSelection;
9515 case SCI_SETADDITIONALSELECTIONTYPING:
9516 additionalSelectionTyping = wParam != 0;
9517 InvalidateCaret();
9518 break;
9520 case SCI_GETADDITIONALSELECTIONTYPING:
9521 return additionalSelectionTyping;
9523 case SCI_SETMULTIPASTE:
9524 multiPasteMode = static_cast<int>(wParam);
9525 break;
9527 case SCI_GETMULTIPASTE:
9528 return multiPasteMode;
9530 case SCI_SETADDITIONALCARETSBLINK:
9531 additionalCaretsBlink = wParam != 0;
9532 InvalidateCaret();
9533 break;
9535 case SCI_GETADDITIONALCARETSBLINK:
9536 return additionalCaretsBlink;
9538 case SCI_SETADDITIONALCARETSVISIBLE:
9539 additionalCaretsVisible = wParam != 0;
9540 InvalidateCaret();
9541 break;
9543 case SCI_GETADDITIONALCARETSVISIBLE:
9544 return additionalCaretsVisible;
9546 case SCI_GETSELECTIONS:
9547 return sel.Count();
9549 case SCI_GETSELECTIONEMPTY:
9550 return sel.Empty();
9552 case SCI_CLEARSELECTIONS:
9553 sel.Clear();
9554 Redraw();
9555 break;
9557 case SCI_SETSELECTION:
9558 sel.SetSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
9559 Redraw();
9560 break;
9562 case SCI_ADDSELECTION:
9563 sel.AddSelection(SelectionRange(static_cast<int>(wParam), static_cast<int>(lParam)));
9564 Redraw();
9565 break;
9567 case SCI_DROPSELECTIONN:
9568 sel.DropSelection(static_cast<int>(wParam));
9569 Redraw();
9570 break;
9572 case SCI_SETMAINSELECTION:
9573 sel.SetMain(static_cast<int>(wParam));
9574 Redraw();
9575 break;
9577 case SCI_GETMAINSELECTION:
9578 return sel.Main();
9580 case SCI_SETSELECTIONNCARET:
9581 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
9582 Redraw();
9583 break;
9585 case SCI_GETSELECTIONNCARET:
9586 return sel.Range(wParam).caret.Position();
9588 case SCI_SETSELECTIONNANCHOR:
9589 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
9590 Redraw();
9591 break;
9592 case SCI_GETSELECTIONNANCHOR:
9593 return sel.Range(wParam).anchor.Position();
9595 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
9596 sel.Range(wParam).caret.SetVirtualSpace(static_cast<int>(lParam));
9597 Redraw();
9598 break;
9600 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
9601 return sel.Range(wParam).caret.VirtualSpace();
9603 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
9604 sel.Range(wParam).anchor.SetVirtualSpace(static_cast<int>(lParam));
9605 Redraw();
9606 break;
9608 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
9609 return sel.Range(wParam).anchor.VirtualSpace();
9611 case SCI_SETSELECTIONNSTART:
9612 sel.Range(wParam).anchor.SetPosition(static_cast<int>(lParam));
9613 Redraw();
9614 break;
9616 case SCI_GETSELECTIONNSTART:
9617 return sel.Range(wParam).Start().Position();
9619 case SCI_SETSELECTIONNEND:
9620 sel.Range(wParam).caret.SetPosition(static_cast<int>(lParam));
9621 Redraw();
9622 break;
9624 case SCI_GETSELECTIONNEND:
9625 return sel.Range(wParam).End().Position();
9627 case SCI_SETRECTANGULARSELECTIONCARET:
9628 if (!sel.IsRectangular())
9629 sel.Clear();
9630 sel.selType = Selection::selRectangle;
9631 sel.Rectangular().caret.SetPosition(static_cast<int>(wParam));
9632 SetRectangularRange();
9633 Redraw();
9634 break;
9636 case SCI_GETRECTANGULARSELECTIONCARET:
9637 return sel.Rectangular().caret.Position();
9639 case SCI_SETRECTANGULARSELECTIONANCHOR:
9640 if (!sel.IsRectangular())
9641 sel.Clear();
9642 sel.selType = Selection::selRectangle;
9643 sel.Rectangular().anchor.SetPosition(static_cast<int>(wParam));
9644 SetRectangularRange();
9645 Redraw();
9646 break;
9648 case SCI_GETRECTANGULARSELECTIONANCHOR:
9649 return sel.Rectangular().anchor.Position();
9651 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9652 if (!sel.IsRectangular())
9653 sel.Clear();
9654 sel.selType = Selection::selRectangle;
9655 sel.Rectangular().caret.SetVirtualSpace(static_cast<int>(wParam));
9656 SetRectangularRange();
9657 Redraw();
9658 break;
9660 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9661 return sel.Rectangular().caret.VirtualSpace();
9663 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9664 if (!sel.IsRectangular())
9665 sel.Clear();
9666 sel.selType = Selection::selRectangle;
9667 sel.Rectangular().anchor.SetVirtualSpace(static_cast<int>(wParam));
9668 SetRectangularRange();
9669 Redraw();
9670 break;
9672 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9673 return sel.Rectangular().anchor.VirtualSpace();
9675 case SCI_SETVIRTUALSPACEOPTIONS:
9676 virtualSpaceOptions = static_cast<int>(wParam);
9677 break;
9679 case SCI_GETVIRTUALSPACEOPTIONS:
9680 return virtualSpaceOptions;
9682 case SCI_SETADDITIONALSELFORE:
9683 vs.selAdditionalForeground = ColourDesired(static_cast<long>(wParam));
9684 InvalidateStyleRedraw();
9685 break;
9687 case SCI_SETADDITIONALSELBACK:
9688 vs.selAdditionalBackground = ColourDesired(static_cast<long>(wParam));
9689 InvalidateStyleRedraw();
9690 break;
9692 case SCI_SETADDITIONALSELALPHA:
9693 vs.selAdditionalAlpha = static_cast<int>(wParam);
9694 InvalidateStyleRedraw();
9695 break;
9697 case SCI_GETADDITIONALSELALPHA:
9698 return vs.selAdditionalAlpha;
9700 case SCI_SETADDITIONALCARETFORE:
9701 vs.additionalCaretColour = ColourDesired(static_cast<long>(wParam));
9702 InvalidateStyleRedraw();
9703 break;
9705 case SCI_GETADDITIONALCARETFORE:
9706 return vs.additionalCaretColour.AsLong();
9708 case SCI_ROTATESELECTION:
9709 sel.RotateMain();
9710 InvalidateSelection(sel.RangeMain(), true);
9711 break;
9713 case SCI_SWAPMAINANCHORCARET:
9714 InvalidateSelection(sel.RangeMain());
9715 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
9716 break;
9718 case SCI_CHANGELEXERSTATE:
9719 pdoc->ChangeLexerState(static_cast<int>(wParam), static_cast<int>(lParam));
9720 break;
9722 case SCI_SETIDENTIFIER:
9723 SetCtrlID(static_cast<int>(wParam));
9724 break;
9726 case SCI_GETIDENTIFIER:
9727 return GetCtrlID();
9729 case SCI_SETTECHNOLOGY:
9730 // No action by default
9731 break;
9733 case SCI_GETTECHNOLOGY:
9734 return technology;
9736 case SCI_COUNTCHARACTERS:
9737 return pdoc->CountCharacters(static_cast<int>(wParam), static_cast<int>(lParam));
9739 default:
9740 return DefWndProc(iMessage, wParam, lParam);
9742 //Platform::DebugPrintf("end wnd proc\n");
9743 return 0l;