Update Scintilla to version 3.4.1
[geany-mirror.git] / scintilla / src / Editor.cxx
blob1337a0e052cb8f7324e24b150ca6692b7d32cd3b
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 int RoundXYPosition(XYPOSITION xyPos) {
92 return int(xyPos+0.5);
95 static inline bool IsControlCharacter(int ch) {
96 // iscntrl returns true for lots of chars > 127 which are displayable
97 return ch >= 0 && ch < ' ';
100 static inline bool IsAllSpacesOrTabs(char *s, unsigned int len) {
101 for (unsigned int i = 0; i < len; i++) {
102 // This is safe because IsSpaceOrTab() will return false for null terminators
103 if (!IsSpaceOrTab(s[i]))
104 return false;
106 return true;
109 PrintParameters::PrintParameters() {
110 magnification = 0;
111 colourMode = SC_PRINT_NORMAL;
112 wrapState = eWrapWord;
115 Editor::Editor() {
116 ctrlID = 0;
118 stylesValid = false;
119 technology = SC_TECHNOLOGY_DEFAULT;
120 scaleRGBAImage = 100;
122 cursorMode = SC_CURSORNORMAL;
124 hasFocus = false;
125 hideSelection = false;
126 inOverstrike = false;
127 drawOverstrikeCaret = true;
128 errorStatus = 0;
129 mouseDownCaptures = true;
131 bufferedDraw = true;
132 twoPhaseDraw = true;
134 lastClickTime = 0;
135 dwellDelay = SC_TIME_FOREVER;
136 ticksToDwell = SC_TIME_FOREVER;
137 dwelling = false;
138 ptMouseLast.x = 0;
139 ptMouseLast.y = 0;
140 inDragDrop = ddNone;
141 dropWentOutside = false;
142 posDrag = SelectionPosition(invalidPosition);
143 posDrop = SelectionPosition(invalidPosition);
144 hotSpotClickPos = INVALID_POSITION;
145 selectionType = selChar;
147 lastXChosen = 0;
148 lineAnchorPos = 0;
149 originalAnchorPos = 0;
150 wordSelectAnchorStartPos = 0;
151 wordSelectAnchorEndPos = 0;
152 wordSelectInitialCaretPos = -1;
154 primarySelection = true;
156 caretXPolicy = CARET_SLOP | CARET_EVEN;
157 caretXSlop = 50;
159 caretYPolicy = CARET_EVEN;
160 caretYSlop = 0;
162 visiblePolicy = 0;
163 visibleSlop = 0;
165 searchAnchor = 0;
167 xOffset = 0;
168 xCaretMargin = 50;
169 horizontalScrollBarVisible = true;
170 scrollWidth = 2000;
171 trackLineWidth = false;
172 lineWidthMaxSeen = 0;
173 verticalScrollBarVisible = true;
174 endAtLastLine = true;
175 caretSticky = SC_CARETSTICKY_OFF;
176 marginOptions = SC_MARGINOPTION_NONE;
177 mouseSelectionRectangularSwitch = false;
178 multipleSelection = false;
179 additionalSelectionTyping = false;
180 multiPasteMode = SC_MULTIPASTE_ONCE;
181 additionalCaretsBlink = true;
182 additionalCaretsVisible = true;
183 virtualSpaceOptions = SCVS_NONE;
185 pixmapLine = 0;
186 pixmapSelMargin = 0;
187 pixmapSelPattern = 0;
188 pixmapSelPatternOffset1 = 0;
189 pixmapIndentGuide = 0;
190 pixmapIndentGuideHighlight = 0;
192 targetStart = 0;
193 targetEnd = 0;
194 searchFlags = 0;
196 topLine = 0;
197 posTopLine = 0;
199 lengthForEncode = -1;
201 needUpdateUI = 0;
202 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
203 braces[0] = invalidPosition;
204 braces[1] = invalidPosition;
205 bracesMatchStyle = STYLE_BRACEBAD;
206 highlightGuideColumn = 0;
208 paintState = notPainting;
209 paintAbandonedByStyling = false;
210 paintingAllText = false;
211 willRedrawAll = false;
213 modEventMask = SC_MODEVENTMASKALL;
215 pdoc = new Document();
216 pdoc->AddRef();
217 pdoc->AddWatcher(this, 0);
219 recordingMacro = false;
220 foldFlags = 0;
221 foldAutomatic = 0;
223 wrapWidth = LineLayout::wrapWidthInfinite;
225 convertPastes = true;
227 hsStart = -1;
228 hsEnd = -1;
230 llc.SetLevel(LineLayoutCache::llcCaret);
231 posCache.SetSize(0x400);
233 SetRepresentations();
236 Editor::~Editor() {
237 pdoc->RemoveWatcher(this, 0);
238 pdoc->Release();
239 pdoc = 0;
240 DropGraphics(true);
243 void Editor::Finalise() {
244 SetIdle(false);
245 CancelModes();
248 void Editor::SetRepresentations() {
249 reprs.Clear();
251 // C0 control set
252 const char *reps[] = {
253 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
254 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
255 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
256 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
258 for (size_t j=0; j < ELEMENTS(reps); j++) {
259 char c[2] = { static_cast<char>(j), 0 };
260 reprs.SetRepresentation(c, reps[j]);
263 // C1 control set
264 // As well as Unicode mode, ISO-8859-1 should use these
265 if (IsUnicodeMode()) {
266 const char *repsC1[] = {
267 "PAD", "HOP", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
268 "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
269 "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
270 "SOS", "SGCI", "SCI", "CSI", "ST", "OSC", "PM", "APC"
272 for (size_t j=0; j < ELEMENTS(repsC1); j++) {
273 char c1[3] = { '\xc2', static_cast<char>(0x80+j), 0 };
274 reprs.SetRepresentation(c1, repsC1[j]);
276 reprs.SetRepresentation("\xe2\x80\xa8", "LS");
277 reprs.SetRepresentation("\xe2\x80\xa9", "PS");
280 // UTF-8 invalid bytes
281 if (IsUnicodeMode()) {
282 for (int k=0x80; k < 0x100; k++) {
283 char hiByte[2] = { static_cast<char>(k), 0 };
284 char hexits[4];
285 sprintf(hexits, "x%2X", k);
286 reprs.SetRepresentation(hiByte, hexits);
291 void Editor::DropGraphics(bool freeObjects) {
292 if (freeObjects) {
293 delete pixmapLine;
294 pixmapLine = 0;
295 delete pixmapSelMargin;
296 pixmapSelMargin = 0;
297 delete pixmapSelPattern;
298 pixmapSelPattern = 0;
299 delete pixmapSelPatternOffset1;
300 pixmapSelPatternOffset1 = 0;
301 delete pixmapIndentGuide;
302 pixmapIndentGuide = 0;
303 delete pixmapIndentGuideHighlight;
304 pixmapIndentGuideHighlight = 0;
305 } else {
306 if (pixmapLine)
307 pixmapLine->Release();
308 if (pixmapSelMargin)
309 pixmapSelMargin->Release();
310 if (pixmapSelPattern)
311 pixmapSelPattern->Release();
312 if (pixmapSelPatternOffset1)
313 pixmapSelPatternOffset1->Release();
314 if (pixmapIndentGuide)
315 pixmapIndentGuide->Release();
316 if (pixmapIndentGuideHighlight)
317 pixmapIndentGuideHighlight->Release();
321 void Editor::AllocateGraphics() {
322 if (!pixmapLine)
323 pixmapLine = Surface::Allocate(technology);
324 if (!pixmapSelMargin)
325 pixmapSelMargin = Surface::Allocate(technology);
326 if (!pixmapSelPattern)
327 pixmapSelPattern = Surface::Allocate(technology);
328 if (!pixmapSelPatternOffset1)
329 pixmapSelPatternOffset1 = Surface::Allocate(technology);
330 if (!pixmapIndentGuide)
331 pixmapIndentGuide = Surface::Allocate(technology);
332 if (!pixmapIndentGuideHighlight)
333 pixmapIndentGuideHighlight = Surface::Allocate(technology);
336 void Editor::InvalidateStyleData() {
337 stylesValid = false;
338 vs.technology = technology;
339 DropGraphics(false);
340 AllocateGraphics();
341 llc.Invalidate(LineLayout::llInvalid);
342 posCache.Clear();
345 void Editor::InvalidateStyleRedraw() {
346 NeedWrapping();
347 InvalidateStyleData();
348 Redraw();
351 void Editor::RefreshStyleData() {
352 if (!stylesValid) {
353 stylesValid = true;
354 AutoSurface surface(this);
355 if (surface) {
356 vs.Refresh(*surface, pdoc->tabInChars);
358 SetScrollBars();
359 SetRectangularRange();
363 Point Editor::GetVisibleOriginInMain() {
364 return Point(0,0);
367 Point Editor::DocumentPointFromView(Point ptView) {
368 Point ptDocument = ptView;
369 if (wMargin.GetID()) {
370 Point ptOrigin = GetVisibleOriginInMain();
371 ptDocument.x += ptOrigin.x;
372 ptDocument.y += ptOrigin.y;
373 } else {
374 ptDocument.x += xOffset;
375 ptDocument.y += topLine * vs.lineHeight;
377 return ptDocument;
380 int Editor::TopLineOfMain() const {
381 if (wMargin.GetID())
382 return 0;
383 else
384 return topLine;
387 PRectangle Editor::GetClientRectangle() {
388 return wMain.GetClientPosition();
391 PRectangle Editor::GetClientDrawingRectangle() {
392 return GetClientRectangle();
395 PRectangle Editor::GetTextRectangle() {
396 PRectangle rc = GetClientRectangle();
397 rc.left += vs.textStart;
398 rc.right -= vs.rightMarginWidth;
399 return rc;
402 int Editor::LinesOnScreen() {
403 PRectangle rcClient = GetClientRectangle();
404 int htClient = rcClient.bottom - rcClient.top;
405 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
406 return htClient / vs.lineHeight;
409 int Editor::LinesToScroll() {
410 int retVal = LinesOnScreen() - 1;
411 if (retVal < 1)
412 return 1;
413 else
414 return retVal;
417 int Editor::MaxScrollPos() {
418 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
419 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
420 int retVal = cs.LinesDisplayed();
421 if (endAtLastLine) {
422 retVal -= LinesOnScreen();
423 } else {
424 retVal--;
426 if (retVal < 0) {
427 return 0;
428 } else {
429 return retVal;
433 const char *ControlCharacterString(unsigned char ch) {
434 const char *reps[] = {
435 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
436 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
437 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
438 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
440 if (ch < ELEMENTS(reps)) {
441 return reps[ch];
442 } else {
443 return "BAD";
448 * Convenience class to ensure LineLayout objects are always disposed.
450 class AutoLineLayout {
451 LineLayoutCache &llc;
452 LineLayout *ll;
453 AutoLineLayout &operator=(const AutoLineLayout &);
454 public:
455 AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
456 ~AutoLineLayout() {
457 llc.Dispose(ll);
458 ll = 0;
460 LineLayout *operator->() const {
461 return ll;
463 operator LineLayout *() const {
464 return ll;
466 void Set(LineLayout *ll_) {
467 llc.Dispose(ll);
468 ll = ll_;
472 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
473 if (sp.Position() < 0) {
474 return SelectionPosition(0);
475 } else if (sp.Position() > pdoc->Length()) {
476 return SelectionPosition(pdoc->Length());
477 } else {
478 // If not at end of line then set offset to 0
479 if (!pdoc->IsLineEndPosition(sp.Position()))
480 sp.SetVirtualSpace(0);
481 return sp;
485 Point Editor::LocationFromPosition(SelectionPosition pos) {
486 Point pt;
487 RefreshStyleData();
488 if (pos.Position() == INVALID_POSITION)
489 return pt;
490 const int line = pdoc->LineFromPosition(pos.Position());
491 const int lineVisible = cs.DisplayFromDoc(line);
492 //Platform::DebugPrintf("line=%d\n", line);
493 AutoSurface surface(this);
494 AutoLineLayout ll(llc, RetrieveLineLayout(line));
495 if (surface && ll) {
496 const int posLineStart = pdoc->LineStart(line);
497 LayoutLine(line, surface, vs, ll, wrapWidth);
498 const int posInLine = pos.Position() - posLineStart;
499 pt = ll->PointFromPosition(posInLine, vs.lineHeight);
500 pt.y += (lineVisible - topLine) * vs.lineHeight;
501 pt.x += vs.textStart - xOffset;
503 pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
504 return pt;
507 Point Editor::LocationFromPosition(int pos) {
508 return LocationFromPosition(SelectionPosition(pos));
511 int Editor::XFromPosition(int pos) {
512 Point pt = LocationFromPosition(pos);
513 return pt.x - vs.textStart + xOffset;
516 int Editor::XFromPosition(SelectionPosition sp) {
517 Point pt = LocationFromPosition(sp);
518 return pt.x - vs.textStart + xOffset;
521 int Editor::LineFromLocation(Point pt) const {
522 return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
525 void Editor::SetTopLine(int topLineNew) {
526 if ((topLine != topLineNew) && (topLineNew >= 0)) {
527 topLine = topLineNew;
528 ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
530 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
533 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
534 RefreshStyleData();
535 if (canReturnInvalid) {
536 PRectangle rcClient = GetTextRectangle();
537 // May be in scroll view coordinates so translate back to main view
538 Point ptOrigin = GetVisibleOriginInMain();
539 rcClient.Move(-ptOrigin.x, -ptOrigin.y);
540 if (!rcClient.Contains(pt))
541 return SelectionPosition(INVALID_POSITION);
542 if (pt.x < vs.textStart)
543 return SelectionPosition(INVALID_POSITION);
544 if (pt.y < 0)
545 return SelectionPosition(INVALID_POSITION);
547 pt = DocumentPointFromView(pt);
548 pt.x = pt.x - vs.textStart;
549 int visibleLine = floor(pt.y / vs.lineHeight);
550 if (!canReturnInvalid && (visibleLine < 0))
551 visibleLine = 0;
552 const int lineDoc = cs.DocFromDisplay(visibleLine);
553 if (canReturnInvalid && (lineDoc < 0))
554 return SelectionPosition(INVALID_POSITION);
555 if (lineDoc >= pdoc->LinesTotal())
556 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length());
557 const int posLineStart = pdoc->LineStart(lineDoc);
558 AutoSurface surface(this);
559 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
560 if (surface && ll) {
561 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
562 const int lineStartSet = cs.DisplayFromDoc(lineDoc);
563 const int subLine = visibleLine - lineStartSet;
564 if (subLine < ll->lines) {
565 const Range rangeSubLine = ll->SubLineRange(subLine);
566 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
567 if (subLine > 0) // Wrapped
568 pt.x -= ll->wrapIndent;
569 const int positionInLine = ll->FindPositionFromX(pt.x + subLineStart, rangeSubLine, charPosition);
570 if (positionInLine < rangeSubLine.end) {
571 return SelectionPosition(pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
573 if (virtualSpace) {
574 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
575 const int spaceOffset = (pt.x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) /
576 spaceWidth;
577 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
578 } else if (canReturnInvalid) {
579 if (pt.x < (ll->positions[rangeSubLine.end] - subLineStart)) {
580 return SelectionPosition(pdoc->MovePositionOutsideChar(rangeSubLine.end + posLineStart, 1));
582 } else {
583 return SelectionPosition(rangeSubLine.end + posLineStart);
586 if (!canReturnInvalid)
587 return SelectionPosition(ll->numCharsInLine + posLineStart);
589 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : posLineStart);
592 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
593 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
597 * Find the document position corresponding to an x coordinate on a particular document line.
598 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
599 * This method is used for rectangular selections and does not work on wrapped lines.
601 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
602 RefreshStyleData();
603 if (lineDoc >= pdoc->LinesTotal())
604 return SelectionPosition(pdoc->Length());
605 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
606 AutoSurface surface(this);
607 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
608 if (surface && ll) {
609 const int posLineStart = pdoc->LineStart(lineDoc);
610 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
611 const Range rangeSubLine = ll->SubLineRange(0);
612 const XYPOSITION subLineStart = ll->positions[rangeSubLine.start];
613 const int positionInLine = ll->FindPositionFromX(x + subLineStart, rangeSubLine, false);
614 if (positionInLine < rangeSubLine.end) {
615 return SelectionPosition(pdoc->MovePositionOutsideChar(positionInLine + posLineStart, 1));
617 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
618 const int spaceOffset = (x + subLineStart - ll->positions[rangeSubLine.end] + spaceWidth / 2) / spaceWidth;
619 return SelectionPosition(rangeSubLine.end + posLineStart, spaceOffset);
621 return SelectionPosition(0);
624 int Editor::PositionFromLineX(int lineDoc, int x) {
625 return SPositionFromLineX(lineDoc, x).Position();
629 * If painting then abandon the painting because a wider redraw is needed.
630 * @return true if calling code should stop drawing.
632 bool Editor::AbandonPaint() {
633 if ((paintState == painting) && !paintingAllText) {
634 paintState = paintAbandoned;
636 return paintState == paintAbandoned;
639 void Editor::RedrawRect(PRectangle rc) {
640 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
642 // Clip the redraw rectangle into the client area
643 PRectangle rcClient = GetClientRectangle();
644 if (rc.top < rcClient.top)
645 rc.top = rcClient.top;
646 if (rc.bottom > rcClient.bottom)
647 rc.bottom = rcClient.bottom;
648 if (rc.left < rcClient.left)
649 rc.left = rcClient.left;
650 if (rc.right > rcClient.right)
651 rc.right = rcClient.right;
653 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
654 wMain.InvalidateRectangle(rc);
658 void Editor::DiscardOverdraw() {
659 // Overridden on platforms that may draw outside visible area.
662 void Editor::Redraw() {
663 //Platform::DebugPrintf("Redraw all\n");
664 PRectangle rcClient = GetClientRectangle();
665 wMain.InvalidateRectangle(rcClient);
666 if (wMargin.GetID())
667 wMargin.InvalidateAll();
668 //wMain.InvalidateAll();
671 void Editor::RedrawSelMargin(int line, bool allAfter) {
672 bool abandonDraw = false;
673 if (!wMargin.GetID()) // Margin in main window so may need to abandon and retry
674 abandonDraw = AbandonPaint();
675 if (!abandonDraw) {
676 if (vs.maskInLine) {
677 Redraw();
678 } else {
679 PRectangle rcSelMargin = GetClientRectangle();
680 rcSelMargin.right = rcSelMargin.left + vs.fixedColumnWidth;
681 if (line != -1) {
682 int position = pdoc->LineStart(line);
683 PRectangle rcLine = RectangleFromRange(position, position);
685 // Inflate line rectangle if there are image markers with height larger than line height
686 if (vs.largestMarkerHeight > vs.lineHeight) {
687 int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
688 rcLine.top -= delta;
689 rcLine.bottom += delta;
690 if (rcLine.top < rcSelMargin.top)
691 rcLine.top = rcSelMargin.top;
692 if (rcLine.bottom > rcSelMargin.bottom)
693 rcLine.bottom = rcSelMargin.bottom;
696 rcSelMargin.top = rcLine.top;
697 if (!allAfter)
698 rcSelMargin.bottom = rcLine.bottom;
699 if (rcSelMargin.Empty())
700 return;
702 if (wMargin.GetID()) {
703 Point ptOrigin = GetVisibleOriginInMain();
704 rcSelMargin.Move(-ptOrigin.x, -ptOrigin.y);
705 wMargin.InvalidateRectangle(rcSelMargin);
706 } else {
707 wMain.InvalidateRectangle(rcSelMargin);
713 PRectangle Editor::RectangleFromRange(int start, int end) {
714 int minPos = start;
715 if (minPos > end)
716 minPos = end;
717 int maxPos = start;
718 if (maxPos < end)
719 maxPos = end;
720 int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos));
721 int lineDocMax = pdoc->LineFromPosition(maxPos);
722 int maxLine = cs.DisplayFromDoc(lineDocMax) + cs.GetHeight(lineDocMax) - 1;
723 PRectangle rcClient = GetTextRectangle();
724 PRectangle rc;
725 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
726 rc.left = vs.textStart - leftTextOverlap;
727 rc.top = (minLine - TopLineOfMain()) * vs.lineHeight;
728 if (rc.top < rcClient.top)
729 rc.top = rcClient.top;
730 rc.right = rcClient.right;
731 rc.bottom = (maxLine - TopLineOfMain() + 1) * vs.lineHeight;
733 return rc;
736 void Editor::InvalidateRange(int start, int end) {
737 RedrawRect(RectangleFromRange(start, end));
740 int Editor::CurrentPosition() const {
741 return sel.MainCaret();
744 bool Editor::SelectionEmpty() const {
745 return sel.Empty();
748 SelectionPosition Editor::SelectionStart() {
749 return sel.RangeMain().Start();
752 SelectionPosition Editor::SelectionEnd() {
753 return sel.RangeMain().End();
756 void Editor::SetRectangularRange() {
757 if (sel.IsRectangular()) {
758 int xAnchor = XFromPosition(sel.Rectangular().anchor);
759 int xCaret = XFromPosition(sel.Rectangular().caret);
760 if (sel.selType == Selection::selThin) {
761 xCaret = xAnchor;
763 int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
764 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
765 int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
766 for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
767 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
768 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
769 range.ClearVirtualSpace();
770 if (line == lineAnchorRect)
771 sel.SetSelection(range);
772 else
773 sel.AddSelectionWithoutTrim(range);
778 void Editor::ThinRectangularRange() {
779 if (sel.IsRectangular()) {
780 sel.selType = Selection::selThin;
781 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
782 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
783 } else {
784 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
786 SetRectangularRange();
790 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
791 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
792 invalidateWholeSelection = true;
794 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
795 // +1 for lastAffected ensures caret repainted
796 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
797 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
798 if (invalidateWholeSelection) {
799 for (size_t r=0; r<sel.Count(); r++) {
800 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
801 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
802 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
803 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
806 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
807 InvalidateRange(firstAffected, lastAffected);
810 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
811 currentPos_ = ClampPositionIntoDocument(currentPos_);
812 anchor_ = ClampPositionIntoDocument(anchor_);
813 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
814 /* For Line selection - ensure the anchor and caret are always
815 at the beginning and end of the region lines. */
816 if (sel.selType == Selection::selLines) {
817 if (currentPos_ > anchor_) {
818 anchor_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
819 currentPos_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
820 } else {
821 currentPos_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
822 anchor_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
825 SelectionRange rangeNew(currentPos_, anchor_);
826 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
827 InvalidateSelection(rangeNew);
829 sel.RangeMain() = rangeNew;
830 SetRectangularRange();
831 ClaimSelection();
833 if (highlightDelimiter.NeedsDrawing(currentLine)) {
834 RedrawSelMargin();
836 QueueIdleWork(WorkNeeded::workUpdateUI);
839 void Editor::SetSelection(int currentPos_, int anchor_) {
840 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
843 // Just move the caret on the main selection
844 void Editor::SetSelection(SelectionPosition currentPos_) {
845 currentPos_ = ClampPositionIntoDocument(currentPos_);
846 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
847 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
848 InvalidateSelection(SelectionRange(currentPos_));
850 if (sel.IsRectangular()) {
851 sel.Rectangular() =
852 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
853 SetRectangularRange();
854 } else {
855 sel.RangeMain() =
856 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
858 ClaimSelection();
860 if (highlightDelimiter.NeedsDrawing(currentLine)) {
861 RedrawSelMargin();
863 QueueIdleWork(WorkNeeded::workUpdateUI);
866 void Editor::SetSelection(int currentPos_) {
867 SetSelection(SelectionPosition(currentPos_));
870 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
871 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
872 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
873 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
874 InvalidateSelection(rangeNew);
876 sel.Clear();
877 sel.RangeMain() = rangeNew;
878 SetRectangularRange();
879 ClaimSelection();
881 if (highlightDelimiter.NeedsDrawing(currentLine)) {
882 RedrawSelMargin();
884 QueueIdleWork(WorkNeeded::workUpdateUI);
887 void Editor::SetEmptySelection(int currentPos_) {
888 SetEmptySelection(SelectionPosition(currentPos_));
891 bool Editor::RangeContainsProtected(int start, int end) const {
892 if (vs.ProtectionActive()) {
893 if (start > end) {
894 int t = start;
895 start = end;
896 end = t;
898 int mask = pdoc->stylingBitsMask;
899 for (int pos = start; pos < end; pos++) {
900 if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())
901 return true;
904 return false;
907 bool Editor::SelectionContainsProtected() {
908 for (size_t r=0; r<sel.Count(); r++) {
909 if (RangeContainsProtected(sel.Range(r).Start().Position(),
910 sel.Range(r).End().Position())) {
911 return true;
914 return false;
918 * Asks document to find a good position and then moves out of any invisible positions.
920 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
921 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
924 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
925 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
926 if (posMoved != pos.Position())
927 pos.SetPosition(posMoved);
928 if (vs.ProtectionActive()) {
929 int mask = pdoc->stylingBitsMask;
930 if (moveDir > 0) {
931 if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()) {
932 while ((pos.Position() < pdoc->Length()) &&
933 (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()))
934 pos.Add(1);
936 } else if (moveDir < 0) {
937 if (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()) {
938 while ((pos.Position() > 0) &&
939 (vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()))
940 pos.Add(-1);
944 return pos;
947 int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
948 bool simpleCaret = (sel.Count() == 1) && sel.Empty();
949 SelectionPosition spCaret = sel.Last();
951 int delta = newPos.Position() - sel.MainCaret();
952 newPos = ClampPositionIntoDocument(newPos);
953 newPos = MovePositionOutsideChar(newPos, delta);
954 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
955 // Can't turn into multiple selection so clear additional selections
956 InvalidateSelection(SelectionRange(newPos), true);
957 SelectionRange rangeMain = sel.RangeMain();
958 sel.SetSelection(rangeMain);
960 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
961 // Switching to rectangular
962 InvalidateSelection(sel.RangeMain(), false);
963 SelectionRange rangeMain = sel.RangeMain();
964 sel.Clear();
965 sel.Rectangular() = rangeMain;
967 if (selt != Selection::noSel) {
968 sel.selType = selt;
970 if (selt != Selection::noSel || sel.MoveExtends()) {
971 SetSelection(newPos);
972 } else {
973 SetEmptySelection(newPos);
975 ShowCaretAtCurrentPosition();
977 int currentLine = pdoc->LineFromPosition(newPos.Position());
978 if (ensureVisible) {
979 // In case in need of wrapping to ensure DisplayFromDoc works.
980 if (currentLine >= wrapPending.start)
981 WrapLines(wsAll);
982 XYScrollPosition newXY = XYScrollToMakeVisible(
983 SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret), xysDefault);
984 if (simpleCaret && (newXY.xOffset == xOffset)) {
985 // simple vertical scroll then invalidate
986 ScrollTo(newXY.topLine);
987 InvalidateSelection(SelectionRange(spCaret), true);
988 } else {
989 SetXYScroll(newXY);
993 if (highlightDelimiter.NeedsDrawing(currentLine)) {
994 RedrawSelMargin();
996 return 0;
999 int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
1000 return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
1003 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
1004 pos = ClampPositionIntoDocument(pos);
1005 pos = MovePositionOutsideChar(pos, moveDir);
1006 int lineDoc = pdoc->LineFromPosition(pos.Position());
1007 if (cs.GetVisible(lineDoc)) {
1008 return pos;
1009 } else {
1010 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1011 if (moveDir > 0) {
1012 // lineDisplay is already line before fold as lines in fold use display line of line after fold
1013 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
1014 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
1015 } else {
1016 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
1017 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
1022 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
1023 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
1026 Point Editor::PointMainCaret() {
1027 return LocationFromPosition(sel.Range(sel.Main()).caret);
1031 * Choose the x position that the caret will try to stick to
1032 * as it moves up and down.
1034 void Editor::SetLastXChosen() {
1035 Point pt = PointMainCaret();
1036 lastXChosen = pt.x + xOffset;
1039 void Editor::ScrollTo(int line, bool moveThumb) {
1040 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
1041 if (topLineNew != topLine) {
1042 // Try to optimise small scrolls
1043 #ifndef UNDER_CE
1044 int linesToMove = topLine - topLineNew;
1045 bool performBlit = (abs(linesToMove) <= 10) && (paintState == notPainting);
1046 willRedrawAll = !performBlit;
1047 #endif
1048 SetTopLine(topLineNew);
1049 // Optimize by styling the view as this will invalidate any needed area
1050 // which could abort the initial paint if discovered later.
1051 StyleToPositionInView(PositionAfterArea(GetClientRectangle()));
1052 #ifndef UNDER_CE
1053 // Perform redraw rather than scroll if many lines would be redrawn anyway.
1054 if (performBlit) {
1055 ScrollText(linesToMove);
1056 } else {
1057 Redraw();
1059 willRedrawAll = false;
1060 #else
1061 Redraw();
1062 #endif
1063 if (moveThumb) {
1064 SetVerticalScrollPos();
1069 void Editor::ScrollText(int /* linesToMove */) {
1070 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
1071 Redraw();
1074 void Editor::HorizontalScrollTo(int xPos) {
1075 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
1076 if (xPos < 0)
1077 xPos = 0;
1078 if (!Wrapping() && (xOffset != xPos)) {
1079 xOffset = xPos;
1080 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1081 SetHorizontalScrollPos();
1082 RedrawRect(GetClientRectangle());
1086 void Editor::VerticalCentreCaret() {
1087 int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
1088 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1089 int newTop = lineDisplay - (LinesOnScreen() / 2);
1090 if (topLine != newTop) {
1091 SetTopLine(newTop > 0 ? newTop : 0);
1092 RedrawRect(GetClientRectangle());
1096 // Avoid 64 bit compiler warnings.
1097 // Scintilla does not support text buffers larger than 2**31
1098 static int istrlen(const char *s) {
1099 return static_cast<int>(strlen(s));
1102 void Editor::MoveSelectedLines(int lineDelta) {
1104 // if selection doesn't start at the beginning of the line, set the new start
1105 int selectionStart = SelectionStart().Position();
1106 int startLine = pdoc->LineFromPosition(selectionStart);
1107 int beginningOfStartLine = pdoc->LineStart(startLine);
1108 selectionStart = beginningOfStartLine;
1110 // if selection doesn't end at the beginning of a line greater than that of the start,
1111 // then set it at the beginning of the next one
1112 int selectionEnd = SelectionEnd().Position();
1113 int endLine = pdoc->LineFromPosition(selectionEnd);
1114 int beginningOfEndLine = pdoc->LineStart(endLine);
1115 bool appendEol = false;
1116 if (selectionEnd > beginningOfEndLine
1117 || selectionStart == selectionEnd) {
1118 selectionEnd = pdoc->LineStart(endLine + 1);
1119 appendEol = (selectionEnd == pdoc->Length() && pdoc->LineFromPosition(selectionEnd) == endLine);
1122 // if there's nowhere for the selection to move
1123 // (i.e. at the beginning going up or at the end going down),
1124 // stop it right there!
1125 if ((selectionStart == 0 && lineDelta < 0)
1126 || (selectionEnd == pdoc->Length() && lineDelta > 0)
1127 || selectionStart == selectionEnd) {
1128 return;
1131 UndoGroup ug(pdoc);
1133 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
1134 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
1135 ClearSelection();
1136 selectionEnd = CurrentPosition();
1138 SetSelection(selectionStart, selectionEnd);
1140 SelectionText selectedText;
1141 CopySelectionRange(&selectedText);
1143 int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
1144 Point currentLocation = LocationFromPosition(CurrentPosition());
1145 int currentLine = LineFromLocation(currentLocation);
1147 if (appendEol)
1148 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
1149 ClearSelection();
1151 const char *eol = StringFromEOLMode(pdoc->eolMode);
1152 if (currentLine + lineDelta >= pdoc->LinesTotal())
1153 pdoc->InsertCString(pdoc->Length(), eol);
1154 GoToLine(currentLine + lineDelta);
1156 pdoc->InsertCString(CurrentPosition(), selectedText.Data());
1157 if (appendEol) {
1158 pdoc->InsertCString(CurrentPosition() + selectionLength, eol);
1159 selectionLength += istrlen(eol);
1161 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1164 void Editor::MoveSelectedLinesUp() {
1165 MoveSelectedLines(-1);
1168 void Editor::MoveSelectedLinesDown() {
1169 MoveSelectedLines(1);
1172 void Editor::MoveCaretInsideView(bool ensureVisible) {
1173 PRectangle rcClient = GetTextRectangle();
1174 Point pt = PointMainCaret();
1175 if (pt.y < rcClient.top) {
1176 MovePositionTo(SPositionFromLocation(
1177 Point(lastXChosen - xOffset, rcClient.top),
1178 false, false, UserVirtualSpace()),
1179 Selection::noSel, ensureVisible);
1180 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1181 int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
1182 MovePositionTo(SPositionFromLocation(
1183 Point(lastXChosen - xOffset, rcClient.top + yOfLastLineFullyDisplayed),
1184 false, false, UserVirtualSpace()),
1185 Selection::noSel, ensureVisible);
1189 int Editor::DisplayFromPosition(int pos) {
1190 int lineDoc = pdoc->LineFromPosition(pos);
1191 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1192 AutoSurface surface(this);
1193 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
1194 if (surface && ll) {
1195 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
1196 unsigned int posLineStart = pdoc->LineStart(lineDoc);
1197 int posInLine = pos - posLineStart;
1198 lineDisplay--; // To make up for first increment ahead.
1199 for (int subLine = 0; subLine < ll->lines; subLine++) {
1200 if (posInLine >= ll->LineStart(subLine)) {
1201 lineDisplay++;
1205 return lineDisplay;
1209 * Ensure the caret is reasonably visible in context.
1211 Caret policy in SciTE
1213 If slop is set, we can define a slop value.
1214 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1215 This zone is defined as a number of pixels near the vertical margins,
1216 and as a number of lines near the horizontal margins.
1217 By keeping the caret away from the edges, it is seen within its context,
1218 so it is likely that the identifier that the caret is on can be completely seen,
1219 and that the current line is seen with some of the lines following it which are
1220 often dependent on that line.
1222 If strict is set, the policy is enforced... strictly.
1223 The caret is centred on the display if slop is not set,
1224 and cannot go in the UZ if slop is set.
1226 If jumps is set, the display is moved more energetically
1227 so the caret can move in the same direction longer before the policy is applied again.
1228 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1230 If even is not set, instead of having symmetrical UZs,
1231 the left and bottom UZs are extended up to right and top UZs respectively.
1232 This way, we favour the displaying of useful information: the begining of lines,
1233 where most code reside, and the lines after the caret, eg. the body of a function.
1235 | | | | |
1236 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1237 | | | | | visibility or going into the UZ) display is...
1238 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1239 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1240 0 | 0 | 0 | 1 | Yes | moved by one position
1241 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1242 0 | 0 | 1 | 1 | Yes | centred on the caret
1243 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1244 0 | 1 | - | 1 | No, caret is always centred | -
1245 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1246 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1247 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1248 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1249 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1250 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1251 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1254 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange &range, const XYScrollOptions options) {
1255 PRectangle rcClient = GetTextRectangle();
1256 Point pt = LocationFromPosition(range.caret);
1257 Point ptAnchor = LocationFromPosition(range.anchor);
1258 const Point ptOrigin = GetVisibleOriginInMain();
1259 pt.x += ptOrigin.x;
1260 pt.y += ptOrigin.y;
1261 ptAnchor.x += ptOrigin.x;
1262 ptAnchor.y += ptOrigin.y;
1263 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1265 XYScrollPosition newXY(xOffset, topLine);
1266 if (rcClient.Empty()) {
1267 return newXY;
1270 // Vertical positioning
1271 if ((options & xysVertical) && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1272 const int lineCaret = DisplayFromPosition(range.caret.Position());
1273 const int linesOnScreen = LinesOnScreen();
1274 const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1275 const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
1276 const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
1277 const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
1278 const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
1280 // It should be possible to scroll the window to show the caret,
1281 // but this fails to remove the caret on GTK+
1282 if (bSlop) { // A margin is defined
1283 int yMoveT, yMoveB;
1284 if (bStrict) {
1285 int yMarginT, yMarginB;
1286 if (!(options & xysUseMargin)) {
1287 // In drag mode, avoid moves
1288 // otherwise, a double click will select several lines.
1289 yMarginT = yMarginB = 0;
1290 } else {
1291 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1292 // a maximum of slightly less than half the heigth of the text area.
1293 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1294 if (bEven) {
1295 yMarginB = yMarginT;
1296 } else {
1297 yMarginB = linesOnScreen - yMarginT - 1;
1300 yMoveT = yMarginT;
1301 if (bEven) {
1302 if (bJump) {
1303 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1305 yMoveB = yMoveT;
1306 } else {
1307 yMoveB = linesOnScreen - yMoveT - 1;
1309 if (lineCaret < topLine + yMarginT) {
1310 // Caret goes too high
1311 newXY.topLine = lineCaret - yMoveT;
1312 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1313 // Caret goes too low
1314 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1316 } else { // Not strict
1317 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1318 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1319 if (bEven) {
1320 yMoveB = yMoveT;
1321 } else {
1322 yMoveB = linesOnScreen - yMoveT - 1;
1324 if (lineCaret < topLine) {
1325 // Caret goes too high
1326 newXY.topLine = lineCaret - yMoveT;
1327 } else if (lineCaret > topLine + linesOnScreen - 1) {
1328 // Caret goes too low
1329 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1332 } else { // No slop
1333 if (!bStrict && !bJump) {
1334 // Minimal move
1335 if (lineCaret < topLine) {
1336 // Caret goes too high
1337 newXY.topLine = lineCaret;
1338 } else if (lineCaret > topLine + linesOnScreen - 1) {
1339 // Caret goes too low
1340 if (bEven) {
1341 newXY.topLine = lineCaret - linesOnScreen + 1;
1342 } else {
1343 newXY.topLine = lineCaret;
1346 } else { // Strict or going out of display
1347 if (bEven) {
1348 // Always center caret
1349 newXY.topLine = lineCaret - halfScreen;
1350 } else {
1351 // Always put caret on top of display
1352 newXY.topLine = lineCaret;
1356 if (!(range.caret == range.anchor)) {
1357 const int lineAnchor = DisplayFromPosition(range.anchor.Position());
1358 if (lineAnchor < lineCaret) {
1359 // Shift up to show anchor or as much of range as possible
1360 newXY.topLine = std::min(newXY.topLine, lineAnchor);
1361 newXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());
1362 } else {
1363 // Shift down to show anchor or as much of range as possible
1364 newXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());
1365 newXY.topLine = std::min(newXY.topLine, lineCaret);
1368 newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
1371 // Horizontal positioning
1372 if ((options & xysHorizontal) && !Wrapping()) {
1373 const int halfScreen = Platform::Maximum(rcClient.Width() - 4, 4) / 2;
1374 const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
1375 const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
1376 const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
1377 const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
1379 if (bSlop) { // A margin is defined
1380 int xMoveL, xMoveR;
1381 if (bStrict) {
1382 int xMarginL, xMarginR;
1383 if (!(options & xysUseMargin)) {
1384 // In drag mode, avoid moves unless very near of the margin
1385 // otherwise, a simple click will select text.
1386 xMarginL = xMarginR = 2;
1387 } else {
1388 // xMargin must equal to caretXSlop, with a minimum of 2 and
1389 // a maximum of slightly less than half the width of the text area.
1390 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1391 if (bEven) {
1392 xMarginL = xMarginR;
1393 } else {
1394 xMarginL = rcClient.Width() - xMarginR - 4;
1397 if (bJump && bEven) {
1398 // Jump is used only in even mode
1399 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1400 } else {
1401 xMoveL = xMoveR = 0; // Not used, avoid a warning
1403 if (pt.x < rcClient.left + xMarginL) {
1404 // Caret is on the left of the display
1405 if (bJump && bEven) {
1406 newXY.xOffset -= xMoveL;
1407 } else {
1408 // Move just enough to allow to display the caret
1409 newXY.xOffset -= (rcClient.left + xMarginL) - pt.x;
1411 } else if (pt.x >= rcClient.right - xMarginR) {
1412 // Caret is on the right of the display
1413 if (bJump && bEven) {
1414 newXY.xOffset += xMoveR;
1415 } else {
1416 // Move just enough to allow to display the caret
1417 newXY.xOffset += pt.x - (rcClient.right - xMarginR) + 1;
1420 } else { // Not strict
1421 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1422 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1423 if (bEven) {
1424 xMoveL = xMoveR;
1425 } else {
1426 xMoveL = rcClient.Width() - xMoveR - 4;
1428 if (pt.x < rcClient.left) {
1429 // Caret is on the left of the display
1430 newXY.xOffset -= xMoveL;
1431 } else if (pt.x >= rcClient.right) {
1432 // Caret is on the right of the display
1433 newXY.xOffset += xMoveR;
1436 } else { // No slop
1437 if (bStrict ||
1438 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1439 // Strict or going out of display
1440 if (bEven) {
1441 // Center caret
1442 newXY.xOffset += pt.x - rcClient.left - halfScreen;
1443 } else {
1444 // Put caret on right
1445 newXY.xOffset += pt.x - rcClient.right + 1;
1447 } else {
1448 // Move just enough to allow to display the caret
1449 if (pt.x < rcClient.left) {
1450 // Caret is on the left of the display
1451 if (bEven) {
1452 newXY.xOffset -= rcClient.left - pt.x;
1453 } else {
1454 newXY.xOffset += pt.x - rcClient.right + 1;
1456 } else if (pt.x >= rcClient.right) {
1457 // Caret is on the right of the display
1458 newXY.xOffset += pt.x - rcClient.right + 1;
1462 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1463 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1464 newXY.xOffset = pt.x + xOffset - rcClient.left - 2;
1465 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1466 newXY.xOffset = pt.x + xOffset - rcClient.right + 2;
1467 if (vs.caretStyle == CARETSTYLE_BLOCK) {
1468 // Ensure we can see a good portion of the block caret
1469 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1472 if (!(range.caret == range.anchor)) {
1473 if (ptAnchor.x < pt.x) {
1474 // Shift to left to show anchor or as much of range as possible
1475 int maxOffset = ptAnchor.x + xOffset - rcClient.left - 1;
1476 int minOffset = pt.x + xOffset - rcClient.right + 1;
1477 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1478 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1479 } else {
1480 // Shift to right to show anchor or as much of range as possible
1481 int minOffset = ptAnchor.x + xOffset - rcClient.right + 1;
1482 int maxOffset = pt.x + xOffset - rcClient.left - 1;
1483 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1484 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1487 if (newXY.xOffset < 0) {
1488 newXY.xOffset = 0;
1492 return newXY;
1495 void Editor::SetXYScroll(XYScrollPosition newXY) {
1496 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1497 if (newXY.topLine != topLine) {
1498 SetTopLine(newXY.topLine);
1499 SetVerticalScrollPos();
1501 if (newXY.xOffset != xOffset) {
1502 xOffset = newXY.xOffset;
1503 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1504 if (newXY.xOffset > 0) {
1505 PRectangle rcText = GetTextRectangle();
1506 if (horizontalScrollBarVisible &&
1507 rcText.Width() + xOffset > scrollWidth) {
1508 scrollWidth = xOffset + rcText.Width();
1509 SetScrollBars();
1512 SetHorizontalScrollPos();
1514 Redraw();
1515 UpdateSystemCaret();
1519 void Editor::ScrollRange(SelectionRange range) {
1520 SetXYScroll(XYScrollToMakeVisible(range, xysDefault));
1523 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1524 SetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret),
1525 static_cast<XYScrollOptions>((useMargin?xysUseMargin:0)|(vert?xysVertical:0)|(horiz?xysHorizontal:0))));
1528 void Editor::ShowCaretAtCurrentPosition() {
1529 if (hasFocus) {
1530 caret.active = true;
1531 caret.on = true;
1532 SetTicking(true);
1533 } else {
1534 caret.active = false;
1535 caret.on = false;
1537 InvalidateCaret();
1540 void Editor::DropCaret() {
1541 caret.active = false;
1542 InvalidateCaret();
1545 void Editor::InvalidateCaret() {
1546 if (posDrag.IsValid()) {
1547 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1548 } else {
1549 for (size_t r=0; r<sel.Count(); r++) {
1550 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1553 UpdateSystemCaret();
1556 void Editor::UpdateSystemCaret() {
1559 bool Editor::Wrapping() const {
1560 return vs.wrapState != eWrapNone;
1563 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1564 //Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd);
1565 if (wrapPending.AddRange(docLineStart, docLineEnd)) {
1566 llc.Invalidate(LineLayout::llPositions);
1568 // Wrap lines during idle.
1569 if (Wrapping() && wrapPending.NeedsWrap()) {
1570 SetIdle(true);
1574 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1575 AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
1576 int linesWrapped = 1;
1577 if (ll) {
1578 LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
1579 linesWrapped = ll->lines;
1581 return cs.SetHeight(lineToWrap, linesWrapped +
1582 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1585 // Perform wrapping for a subset of the lines needing wrapping.
1586 // wsAll: wrap all lines which need wrapping in this single call
1587 // wsVisible: wrap currently visible lines
1588 // wsIdle: wrap one page + 100 lines
1589 // Return true if wrapping occurred.
1590 bool Editor::WrapLines(enum wrapScope ws) {
1591 int goodTopLine = topLine;
1592 bool wrapOccurred = false;
1593 if (!Wrapping()) {
1594 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1595 wrapWidth = LineLayout::wrapWidthInfinite;
1596 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1597 cs.SetHeight(lineDoc, 1 +
1598 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1600 wrapOccurred = true;
1602 wrapPending.Reset();
1604 } else if (wrapPending.NeedsWrap()) {
1605 wrapPending.start = std::min(wrapPending.start, pdoc->LinesTotal());
1606 if (!SetIdle(true)) {
1607 // Idle processing not supported so full wrap required.
1608 ws = wsAll;
1610 // Decide where to start wrapping
1611 int lineToWrap = wrapPending.start;
1612 int lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal());
1613 const int lineDocTop = cs.DocFromDisplay(topLine);
1614 const int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1615 if (ws == wsVisible) {
1616 lineToWrap = Platform::Clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal());
1617 // Priority wrap to just after visible area.
1618 // Since wrapping could reduce display lines, treat each
1619 // as taking only one display line.
1620 lineToWrapEnd = lineDocTop;
1621 int lines = LinesOnScreen() + 1;
1622 while ((lineToWrapEnd < cs.LinesInDoc()) && (lines>0)) {
1623 if (cs.GetVisible(lineToWrapEnd))
1624 lines--;
1625 lineToWrapEnd++;
1627 // .. and if the paint window is outside pending wraps
1628 if ((lineToWrap > wrapPending.end) || (lineToWrapEnd < wrapPending.start)) {
1629 // Currently visible text does not need wrapping
1630 return false;
1632 } else if (ws == wsIdle) {
1633 lineToWrapEnd = lineToWrap + LinesOnScreen() + 100;
1635 const int lineEndNeedWrap = std::min(wrapPending.end, pdoc->LinesTotal());
1636 lineToWrapEnd = std::min(lineToWrapEnd, lineEndNeedWrap);
1638 // Ensure all lines being wrapped are styled.
1639 pdoc->EnsureStyledTo(pdoc->LineStart(lineToWrapEnd));
1641 if (lineToWrap < lineToWrapEnd) {
1643 PRectangle rcTextArea = GetClientRectangle();
1644 rcTextArea.left = vs.textStart;
1645 rcTextArea.right -= vs.rightMarginWidth;
1646 wrapWidth = rcTextArea.Width();
1647 RefreshStyleData();
1648 AutoSurface surface(this);
1649 if (surface) {
1650 //Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
1652 while (lineToWrap < lineToWrapEnd) {
1653 if (WrapOneLine(surface, lineToWrap)) {
1654 wrapOccurred = true;
1656 wrapPending.Wrapped(lineToWrap);
1657 lineToWrap++;
1660 goodTopLine = cs.DisplayFromDoc(lineDocTop) + std::min(subLineTop, cs.GetHeight(lineDocTop)-1);
1664 // If wrapping is done, bring it to resting position
1665 if (wrapPending.start >= lineEndNeedWrap) {
1666 wrapPending.Reset();
1670 if (wrapOccurred) {
1671 SetScrollBars();
1672 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1673 SetVerticalScrollPos();
1676 return wrapOccurred;
1679 void Editor::LinesJoin() {
1680 if (!RangeContainsProtected(targetStart, targetEnd)) {
1681 UndoGroup ug(pdoc);
1682 bool prevNonWS = true;
1683 for (int pos = targetStart; pos < targetEnd; pos++) {
1684 if (pdoc->IsPositionInLineEnd(pos)) {
1685 targetEnd -= pdoc->LenChar(pos);
1686 pdoc->DelChar(pos);
1687 if (prevNonWS) {
1688 // Ensure at least one space separating previous lines
1689 pdoc->InsertChar(pos, ' ');
1690 targetEnd++;
1692 } else {
1693 prevNonWS = pdoc->CharAt(pos) != ' ';
1699 const char *Editor::StringFromEOLMode(int eolMode) {
1700 if (eolMode == SC_EOL_CRLF) {
1701 return "\r\n";
1702 } else if (eolMode == SC_EOL_CR) {
1703 return "\r";
1704 } else {
1705 return "\n";
1709 void Editor::LinesSplit(int pixelWidth) {
1710 if (!RangeContainsProtected(targetStart, targetEnd)) {
1711 if (pixelWidth == 0) {
1712 PRectangle rcText = GetTextRectangle();
1713 pixelWidth = rcText.Width();
1715 int lineStart = pdoc->LineFromPosition(targetStart);
1716 int lineEnd = pdoc->LineFromPosition(targetEnd);
1717 const char *eol = StringFromEOLMode(pdoc->eolMode);
1718 UndoGroup ug(pdoc);
1719 for (int line = lineStart; line <= lineEnd; line++) {
1720 AutoSurface surface(this);
1721 AutoLineLayout ll(llc, RetrieveLineLayout(line));
1722 if (surface && ll) {
1723 unsigned int posLineStart = pdoc->LineStart(line);
1724 LayoutLine(line, surface, vs, ll, pixelWidth);
1725 for (int subLine = 1; subLine < ll->lines; subLine++) {
1726 pdoc->InsertCString(
1727 static_cast<int>(posLineStart + (subLine - 1) * strlen(eol) +
1728 ll->LineStart(subLine)),
1729 eol);
1730 targetEnd += static_cast<int>(strlen(eol));
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 += 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 = 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 = 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 = surface->WidthText(vs.styles[style].font,
1805 st.text + start + i, static_cast<int>(end - i + 1));
1806 PRectangle rcSegment = rcText;
1807 rcSegment.left = x;
1808 rcSegment.right = x + width + 1;
1809 surface->DrawTextNoClip(rcSegment, vs.styles[style].font,
1810 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 = 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 = (rcMargin.top + ptOrigin.y) / vs.lineHeight;
1896 int visibleLine = TopLineOfMain() + lineStartPaint;
1897 int yposScreen = lineStartPaint * vs.lineHeight - 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 int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
1924 SC_MARKNUM_FOLDEROPEN);
1925 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 int lineDoc = cs.DocFromDisplay(visibleLine);
1932 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
1933 bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
1934 bool lastSubLine = visibleLine == (cs.DisplayFromDoc(lineDoc + 1) - 1);
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 int level = pdoc->GetLevel(lineDoc);
1945 int levelNext = pdoc->GetLevel(lineDoc + 1);
1946 int levelNum = level & SC_FOLDLEVELNUMBERMASK;
1947 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 int firstFollowupLine = cs.DocFromDisplay(cs.DisplayFromDoc(lineDoc + 1));
1978 int firstFollowupLineLevel = pdoc->GetLevel(firstFollowupLine);
1979 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 = yposScreen;
2035 rcMarker.bottom = 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) {
2042 int lev = pdoc->GetLevel(lineDoc);
2043 sprintf(number, "%c%c %03X %03X",
2044 (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
2045 (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
2046 lev & SC_FOLDLEVELNUMBERMASK,
2047 lev >> 16
2050 PRectangle rcNumber = rcMarker;
2051 // Right justify
2052 XYPOSITION width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
2053 XYPOSITION xpos = rcNumber.right - width - vs.marginNumberPadding;
2054 rcNumber.left = xpos;
2055 surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font,
2056 rcNumber.top + vs.maxAscent, number, istrlen(number),
2057 vs.styles[STYLE_LINENUMBER].fore,
2058 vs.styles[STYLE_LINENUMBER].back);
2059 } else if (vs.wrapVisualFlags & SC_WRAPVISUALFLAG_MARGIN) {
2060 PRectangle rcWrapMarker = rcMarker;
2061 rcWrapMarker.right -= 3;
2062 rcWrapMarker.left = rcWrapMarker.right - vs.styles[STYLE_LINENUMBER].aveCharWidth;
2063 DrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
2065 } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
2066 if (firstSubLine) {
2067 const StyledText stMargin = pdoc->MarginStyledText(lineDoc);
2068 if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
2069 surface->FillRectangle(rcMarker,
2070 vs.styles[stMargin.StyleAt(0)+vs.marginStyleOffset].back);
2071 if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
2072 int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
2073 rcMarker.left = rcMarker.right - width - 3;
2075 DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, rcMarker.top + vs.maxAscent,
2076 stMargin, 0, stMargin.length);
2081 if (marks) {
2082 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
2083 if (marks & 1) {
2084 LineMarker::typeOfFold tFold = LineMarker::undefined;
2085 if ((vs.ms[margin].mask & SC_MASK_FOLDERS) && highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) {
2086 if (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) {
2087 tFold = LineMarker::body;
2088 } else if (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) {
2089 if (firstSubLine) {
2090 tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head;
2091 } else {
2092 if (cs.GetExpanded(lineDoc) || headWithTail) {
2093 tFold = LineMarker::body;
2094 } else {
2095 tFold = LineMarker::undefined;
2098 } else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) {
2099 tFold = LineMarker::tail;
2102 vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font, tFold, vs.ms[margin].style);
2104 marks >>= 1;
2108 visibleLine++;
2109 yposScreen += vs.lineHeight;
2114 PRectangle rcBlankMargin = rcMargin;
2115 rcBlankMargin.left = rcSelMargin.right;
2116 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back);
2118 if (bufferedDraw) {
2119 surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *pixmapSelMargin);
2123 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
2124 int ydiff = (rcTab.bottom - rcTab.top) / 2;
2125 int xhead = rcTab.right - 1 - ydiff;
2126 if (xhead <= rcTab.left) {
2127 ydiff -= rcTab.left - xhead - 1;
2128 xhead = rcTab.left - 1;
2130 if ((rcTab.left + 2) < (rcTab.right - 1))
2131 surface->MoveTo(rcTab.left + 2, ymid);
2132 else
2133 surface->MoveTo(rcTab.right - 1, ymid);
2134 surface->LineTo(rcTab.right - 1, ymid);
2135 surface->LineTo(xhead, ymid - ydiff);
2136 surface->MoveTo(rcTab.right - 1, ymid);
2137 surface->LineTo(xhead, ymid + ydiff);
2140 LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
2141 int posLineStart = pdoc->LineStart(lineNumber);
2142 int posLineEnd = pdoc->LineStart(lineNumber + 1);
2143 PLATFORM_ASSERT(posLineEnd >= posLineStart);
2144 int lineCaret = pdoc->LineFromPosition(sel.MainCaret());
2145 return llc.Retrieve(lineNumber, lineCaret,
2146 posLineEnd - posLineStart, pdoc->GetStyleClock(),
2147 LinesOnScreen() + 1, pdoc->LinesTotal());
2151 * Fill in the LineLayout data for the given line.
2152 * Copy the given @a line and its styles from the document into local arrays.
2153 * Also determine the x position at which each character starts.
2155 void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) {
2156 //void LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width, Document *pdoc, PositionCache &posCache, SpecialRepresentations &reprs) {
2157 if (!ll)
2158 return;
2160 PLATFORM_ASSERT(line < pdoc->LinesTotal());
2161 PLATFORM_ASSERT(ll->chars != NULL);
2162 int posLineStart = pdoc->LineStart(line);
2163 int posLineEnd = pdoc->LineStart(line + 1);
2164 // If the line is very long, limit the treatment to a length that should fit in the viewport
2165 if (posLineEnd > (posLineStart + ll->maxLineLength)) {
2166 posLineEnd = posLineStart + ll->maxLineLength;
2168 if (ll->validity == LineLayout::llCheckTextAndStyle) {
2169 int lineLength = posLineEnd - posLineStart;
2170 if (!vstyle.viewEOL) {
2171 lineLength = pdoc->LineEnd(line) - posLineStart;
2173 if (lineLength == ll->numCharsInLine) {
2174 // See if chars, styles, indicators, are all the same
2175 bool allSame = true;
2176 const int styleMask = pdoc->stylingBitsMask;
2177 // Check base line layout
2178 char styleByte = 0;
2179 int numCharsInLine = 0;
2180 while (numCharsInLine < lineLength) {
2181 int charInDoc = numCharsInLine + posLineStart;
2182 char chDoc = pdoc->CharAt(charInDoc);
2183 styleByte = pdoc->StyleAt(charInDoc);
2184 allSame = allSame &&
2185 (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte & styleMask));
2186 allSame = allSame &&
2187 (ll->indicators[numCharsInLine] == static_cast<char>(styleByte & ~styleMask));
2188 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
2189 allSame = allSame &&
2190 (ll->chars[numCharsInLine] == chDoc);
2191 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
2192 allSame = allSame &&
2193 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
2194 else // Style::caseUpper
2195 allSame = allSame &&
2196 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
2197 numCharsInLine++;
2199 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
2200 if (allSame) {
2201 ll->validity = LineLayout::llPositions;
2202 } else {
2203 ll->validity = LineLayout::llInvalid;
2205 } else {
2206 ll->validity = LineLayout::llInvalid;
2209 if (ll->validity == LineLayout::llInvalid) {
2210 ll->widthLine = LineLayout::wrapWidthInfinite;
2211 ll->lines = 1;
2212 if (vstyle.edgeState == EDGE_BACKGROUND) {
2213 ll->edgeColumn = pdoc->FindColumn(line, vstyle.theEdge);
2214 if (ll->edgeColumn >= posLineStart) {
2215 ll->edgeColumn -= posLineStart;
2217 } else {
2218 ll->edgeColumn = -1;
2221 char styleByte;
2222 const int styleMask = pdoc->stylingBitsMask;
2223 ll->styleBitsSet = 0;
2224 // Fill base line layout
2225 const int lineLength = posLineEnd - posLineStart;
2226 pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
2227 pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
2228 int numCharsBeforeEOL = pdoc->LineEnd(line) - posLineStart;
2229 const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
2230 for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
2231 styleByte = ll->styles[styleInLine];
2232 ll->styleBitsSet |= styleByte;
2233 ll->styles[styleInLine] = static_cast<char>(styleByte & styleMask);
2234 ll->indicators[styleInLine] = static_cast<char>(styleByte & ~styleMask);
2236 styleByte = static_cast<char>(((lineLength > 0) ? ll->styles[lineLength-1] : 0) & styleMask);
2237 if (vstyle.someStylesForceCase) {
2238 for (int charInLine = 0; charInLine<lineLength; charInLine++) {
2239 char chDoc = ll->chars[charInLine];
2240 if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
2241 ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
2242 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
2243 ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
2246 ll->xHighlightGuide = 0;
2247 // Extra element at the end of the line to hold end x position and act as
2248 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
2249 ll->styles[numCharsInLine] = styleByte; // For eolFilled
2250 ll->indicators[numCharsInLine] = 0;
2252 // Layout the line, determining the position of each character,
2253 // with an extra element at the end for the end of the line.
2254 ll->positions[0] = 0;
2255 bool lastSegItalics = false;
2257 BreakFinder bfLayout(ll, 0, numCharsInLine, posLineStart, 0, false, pdoc, &reprs);
2258 while (bfLayout.More()) {
2260 const TextSegment ts = bfLayout.Next();
2262 std::fill(&ll->positions[ts.start+1], &ll->positions[ts.end()+1], 0.0f);
2263 if (vstyle.styles[ll->styles[ts.start]].visible) {
2264 if (ts.representation) {
2265 XYPOSITION representationWidth = vstyle.controlCharWidth;
2266 if (ll->chars[ts.start] == '\t') {
2267 // Tab is a special case of representation, taking a variable amount of space
2268 representationWidth =
2269 ((static_cast<int>((ll->positions[ts.start] + 2) / vstyle.tabWidth) + 1) * vstyle.tabWidth) - ll->positions[ts.start];
2270 } else {
2271 if (representationWidth <= 0.0) {
2272 XYPOSITION positionsRepr[256]; // Should expand when needed
2273 posCache.MeasureWidths(surface, vstyle, STYLE_CONTROLCHAR, ts.representation->stringRep.c_str(),
2274 static_cast<unsigned int>(ts.representation->stringRep.length()), positionsRepr, pdoc);
2275 representationWidth = positionsRepr[ts.representation->stringRep.length()-1] + vstyle.ctrlCharPadding;
2278 for (int ii=0; ii < ts.length; ii++)
2279 ll->positions[ts.start + 1 + ii] = representationWidth;
2280 } else {
2281 if ((ts.length == 1) && (' ' == ll->chars[ts.start])) {
2282 // Over half the segments are single characters and of these about half are space characters.
2283 ll->positions[ts.start + 1] = vstyle.styles[ll->styles[ts.start]].spaceWidth;
2284 } else {
2285 posCache.MeasureWidths(surface, vstyle, ll->styles[ts.start], ll->chars + ts.start,
2286 ts.length, ll->positions + ts.start + 1, pdoc);
2289 lastSegItalics = (!ts.representation) && ((ll->chars[ts.end()-1] != ' ') && vstyle.styles[ll->styles[ts.start]].italic);
2292 for (int posToIncrease = ts.start+1; posToIncrease <= ts.end(); posToIncrease++) {
2293 ll->positions[posToIncrease] += ll->positions[ts.start];
2297 // Small hack to make lines that end with italics not cut off the edge of the last character
2298 if (lastSegItalics) {
2299 ll->positions[numCharsInLine] += vstyle.lastSegItalicsOffset;
2301 ll->numCharsInLine = numCharsInLine;
2302 ll->numCharsBeforeEOL = numCharsBeforeEOL;
2303 ll->validity = LineLayout::llPositions;
2305 // Hard to cope when too narrow, so just assume there is space
2306 if (width < 20) {
2307 width = 20;
2309 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
2310 ll->widthLine = width;
2311 if (width == LineLayout::wrapWidthInfinite) {
2312 ll->lines = 1;
2313 } else if (width > ll->positions[ll->numCharsInLine]) {
2314 // Simple common case where line does not need wrapping.
2315 ll->lines = 1;
2316 } else {
2317 if (vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2318 width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
2320 XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
2321 if (vstyle.wrapIndentMode == SC_WRAPINDENT_INDENT) {
2322 wrapAddIndent = pdoc->IndentSize() * vstyle.spaceWidth;
2323 } else if (vstyle.wrapIndentMode == SC_WRAPINDENT_FIXED) {
2324 wrapAddIndent = vstyle.wrapVisualStartIndent * vstyle.aveCharWidth;
2326 ll->wrapIndent = wrapAddIndent;
2327 if (vstyle.wrapIndentMode != SC_WRAPINDENT_FIXED)
2328 for (int i = 0; i < ll->numCharsInLine; i++) {
2329 if (!IsSpaceOrTab(ll->chars[i])) {
2330 ll->wrapIndent += ll->positions[i]; // Add line indent
2331 break;
2334 // Check for text width minimum
2335 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
2336 ll->wrapIndent = wrapAddIndent;
2337 // Check for wrapIndent minimum
2338 if ((vstyle.wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
2339 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
2340 ll->lines = 0;
2341 // Calculate line start positions based upon width.
2342 int lastGoodBreak = 0;
2343 int lastLineStart = 0;
2344 XYACCUMULATOR startOffset = 0;
2345 int p = 0;
2346 while (p < ll->numCharsInLine) {
2347 if ((ll->positions[p + 1] - startOffset) >= width) {
2348 if (lastGoodBreak == lastLineStart) {
2349 // Try moving to start of last character
2350 if (p > 0) {
2351 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2352 - posLineStart;
2354 if (lastGoodBreak == lastLineStart) {
2355 // Ensure at least one character on line.
2356 lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
2357 - posLineStart;
2360 lastLineStart = lastGoodBreak;
2361 ll->lines++;
2362 ll->SetLineStart(ll->lines, lastGoodBreak);
2363 startOffset = ll->positions[lastGoodBreak];
2364 // take into account the space for start wrap mark and indent
2365 startOffset -= ll->wrapIndent;
2366 p = lastGoodBreak + 1;
2367 continue;
2369 if (p > 0) {
2370 if (vstyle.wrapState == eWrapChar) {
2371 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2372 - posLineStart;
2373 p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
2374 continue;
2375 } else if ((vstyle.wrapState == eWrapWord) && (ll->styles[p] != ll->styles[p - 1])) {
2376 lastGoodBreak = p;
2377 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
2378 lastGoodBreak = p;
2381 p++;
2383 ll->lines++;
2385 ll->validity = LineLayout::llLines;
2389 ColourDesired Editor::SelectionBackground(ViewStyle &vsDraw, bool main) const {
2390 return main ?
2391 (primarySelection ? vsDraw.selColours.back : vsDraw.selBackground2) :
2392 vsDraw.selAdditionalBackground;
2395 ColourDesired Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
2396 ColourDesired background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) const {
2397 if (inSelection == 1) {
2398 if (vsDraw.selColours.back.isSet && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
2399 return SelectionBackground(vsDraw, true);
2401 } else if (inSelection == 2) {
2402 if (vsDraw.selColours.back.isSet && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
2403 return SelectionBackground(vsDraw, false);
2405 } else {
2406 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
2407 (i >= ll->edgeColumn) &&
2408 (i < ll->numCharsBeforeEOL))
2409 return vsDraw.edgecolour;
2410 if (inHotspot && vsDraw.hotspotColours.back.isSet)
2411 return vsDraw.hotspotColours.back;
2413 if (overrideBackground && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
2414 return background;
2415 } else {
2416 return vsDraw.styles[styleMain].back;
2420 void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
2421 Point from(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
2422 PRectangle rcCopyArea(start + 1, rcSegment.top, start + 2, rcSegment.bottom);
2423 surface->Copy(rcCopyArea, from,
2424 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
2427 void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
2428 bool isEndMarker, ColourDesired wrapColour) {
2429 surface->PenColour(wrapColour);
2431 enum { xa = 1 }; // gap before start
2432 int w = rcPlace.right - rcPlace.left - xa - 1;
2434 bool xStraight = isEndMarker; // x-mirrored symbol for start marker
2436 int x0 = xStraight ? rcPlace.left : rcPlace.right - 1;
2437 int y0 = rcPlace.top;
2439 int dy = (rcPlace.bottom - rcPlace.top) / 5;
2440 int y = (rcPlace.bottom - rcPlace.top) / 2 + dy;
2442 struct Relative {
2443 Surface *surface;
2444 int xBase;
2445 int xDir;
2446 int yBase;
2447 int yDir;
2448 void MoveTo(int xRelative, int yRelative) {
2449 surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2451 void LineTo(int xRelative, int yRelative) {
2452 surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2455 Relative rel = {surface, x0, xStraight ? 1 : -1, y0, 1};
2457 // arrow head
2458 rel.MoveTo(xa, y);
2459 rel.LineTo(xa + 2*w / 3, y - dy);
2460 rel.MoveTo(xa, y);
2461 rel.LineTo(xa + 2*w / 3, y + dy);
2463 // arrow body
2464 rel.MoveTo(xa, y);
2465 rel.LineTo(xa + w, y);
2466 rel.LineTo(xa + w, y - 2 * dy);
2467 rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
2468 y - 2 * dy);
2471 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
2472 if (alpha != SC_ALPHA_NOALPHA) {
2473 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
2477 void DrawTextBlob(Surface *surface, ViewStyle &vsDraw, PRectangle rcSegment,
2478 const char *s, ColourDesired textBack, ColourDesired textFore, bool twoPhaseDraw) {
2479 if (!twoPhaseDraw) {
2480 surface->FillRectangle(rcSegment, textBack);
2482 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2483 int normalCharHeight = surface->Ascent(ctrlCharsFont) -
2484 surface->InternalLeading(ctrlCharsFont);
2485 PRectangle rcCChar = rcSegment;
2486 rcCChar.left = rcCChar.left + 1;
2487 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
2488 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
2489 PRectangle rcCentral = rcCChar;
2490 rcCentral.top++;
2491 rcCentral.bottom--;
2492 surface->FillRectangle(rcCentral, textFore);
2493 PRectangle rcChar = rcCChar;
2494 rcChar.left++;
2495 rcChar.right--;
2496 surface->DrawTextClipped(rcChar, ctrlCharsFont,
2497 rcSegment.top + vsDraw.maxAscent, s, istrlen(s),
2498 textBack, textFore);
2501 void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
2502 int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
2503 bool overrideBackground, ColourDesired background,
2504 bool drawWrapMarkEnd, ColourDesired wrapColour) {
2506 const int posLineStart = pdoc->LineStart(line);
2507 const int styleMask = pdoc->stylingBitsMask;
2508 PRectangle rcSegment = rcLine;
2510 const bool lastSubLine = subLine == (ll->lines - 1);
2511 XYPOSITION virtualSpace = 0;
2512 if (lastSubLine) {
2513 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
2514 virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * spaceWidth;
2516 XYPOSITION xEol = ll->positions[lineEnd] - subLineStart;
2518 // Fill the virtual space and show selections within it
2519 if (virtualSpace) {
2520 rcSegment.left = xEol + xStart;
2521 rcSegment.right = xEol + xStart + virtualSpace;
2522 surface->FillRectangle(rcSegment, overrideBackground ? background : vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2523 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
2524 SelectionSegment virtualSpaceRange(SelectionPosition(pdoc->LineEnd(line)), SelectionPosition(pdoc->LineEnd(line), sel.VirtualSpaceFor(pdoc->LineEnd(line))));
2525 for (size_t r=0; r<sel.Count(); r++) {
2526 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2527 if (alpha == SC_ALPHA_NOALPHA) {
2528 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
2529 if (!portion.Empty()) {
2530 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
2531 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
2532 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
2533 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
2534 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
2535 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == sel.Main()));
2542 int eolInSelection = 0;
2543 int alpha = SC_ALPHA_NOALPHA;
2544 if (!hideSelection) {
2545 int posAfterLineEnd = pdoc->LineStart(line + 1);
2546 eolInSelection = (subLine == (ll->lines - 1)) ? sel.InSelectionForEOL(posAfterLineEnd) : 0;
2547 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2550 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
2551 XYPOSITION blobsWidth = 0;
2552 if (lastSubLine) {
2553 for (int eolPos=ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
2554 rcSegment.left = xStart + ll->positions[eolPos] - subLineStart + virtualSpace;
2555 rcSegment.right = xStart + ll->positions[eolPos+1] - subLineStart + virtualSpace;
2556 blobsWidth += rcSegment.Width();
2557 char hexits[4];
2558 const char *ctrlChar;
2559 unsigned char chEOL = ll->chars[eolPos];
2560 int styleMain = ll->styles[eolPos];
2561 ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, eolInSelection, false, styleMain, eolPos, ll);
2562 if (UTF8IsAscii(chEOL)) {
2563 ctrlChar = ControlCharacterString(chEOL);
2564 } else {
2565 Representation *repr = reprs.RepresentationFromCharacter(ll->chars + eolPos, ll->numCharsInLine - eolPos);
2566 if (repr) {
2567 ctrlChar = repr->stringRep.c_str();
2568 eolPos = ll->numCharsInLine;
2569 } else {
2570 sprintf(hexits, "x%2X", chEOL);
2571 ctrlChar = hexits;
2574 ColourDesired textFore = vsDraw.styles[styleMain].fore;
2575 if (eolInSelection && vsDraw.selColours.fore.isSet) {
2576 textFore = (eolInSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
2578 if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1)) {
2579 if (alpha == SC_ALPHA_NOALPHA) {
2580 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2581 } else {
2582 surface->FillRectangle(rcSegment, textBack);
2584 } else {
2585 surface->FillRectangle(rcSegment, textBack);
2587 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
2588 if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2589 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2594 // Draw the eol-is-selected rectangle
2595 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
2596 rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
2598 if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2599 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2600 } else {
2601 if (overrideBackground) {
2602 surface->FillRectangle(rcSegment, background);
2603 } else if (line < pdoc->LinesTotal() - 1) {
2604 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2605 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2606 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2607 } else {
2608 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
2610 if (eolInSelection && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2611 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2615 // Fill the remainder of the line
2616 rcSegment.left = rcSegment.right;
2617 if (rcSegment.left < rcLine.left)
2618 rcSegment.left = rcLine.left;
2619 rcSegment.right = rcLine.right;
2621 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2622 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2623 } else {
2624 if (overrideBackground) {
2625 surface->FillRectangle(rcSegment, background);
2626 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2627 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2628 } else {
2629 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
2631 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selColours.back.isSet && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2632 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2636 if (drawWrapMarkEnd) {
2637 PRectangle rcPlace = rcSegment;
2639 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
2640 rcPlace.left = xEol + xStart + virtualSpace;
2641 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2642 } else {
2643 // rcLine is clipped to text area
2644 rcPlace.right = rcLine.right;
2645 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2647 DrawWrapMarker(surface, rcPlace, true, wrapColour);
2651 void Editor::DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, ViewStyle &vsDraw,
2652 int xStart, PRectangle rcLine, LineLayout *ll, int subLine) {
2653 const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
2654 PRectangle rcIndic(
2655 ll->positions[startPos] + xStart - subLineStart,
2656 rcLine.top + vsDraw.maxAscent,
2657 ll->positions[endPos] + xStart - subLineStart,
2658 rcLine.top + vsDraw.maxAscent + 3);
2659 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine);
2662 void Editor::DrawIndicators(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2663 PRectangle rcLine, LineLayout *ll, int subLine, int lineEnd, bool under) {
2664 // Draw decorators
2665 const int posLineStart = pdoc->LineStart(line);
2666 const int lineStart = ll->LineStart(subLine);
2667 const int posLineEnd = posLineStart + lineEnd;
2669 if (!under) {
2670 // Draw indicators
2671 // foreach indicator...
2672 for (int indicnum = 0, mask = 1 << pdoc->stylingBits; mask < 0x100; indicnum++) {
2673 if (!(mask & ll->styleBitsSet)) {
2674 mask <<= 1;
2675 continue;
2677 int startPos = -1;
2678 // foreach style pos in line...
2679 for (int indicPos = lineStart; indicPos <= lineEnd; indicPos++) {
2680 // look for starts...
2681 if (startPos < 0) {
2682 // NOT in indicator run, looking for START
2683 if (indicPos < lineEnd && (ll->indicators[indicPos] & mask))
2684 startPos = indicPos;
2686 // ... or ends
2687 if (startPos >= 0) {
2688 // IN indicator run, looking for END
2689 if (indicPos >= lineEnd || !(ll->indicators[indicPos] & mask)) {
2690 // AT end of indicator run, DRAW it!
2691 DrawIndicator(indicnum, startPos, indicPos, surface, vsDraw, xStart, rcLine, ll, subLine);
2692 // RESET control var
2693 startPos = -1;
2697 mask <<= 1;
2701 for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
2702 if (under == vsDraw.indicators[deco->indicator].under) {
2703 int startPos = posLineStart + lineStart;
2704 if (!deco->rs.ValueAt(startPos)) {
2705 startPos = deco->rs.EndRun(startPos);
2707 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
2708 int endPos = deco->rs.EndRun(startPos);
2709 if (endPos > posLineEnd)
2710 endPos = posLineEnd;
2711 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
2712 surface, vsDraw, xStart, rcLine, ll, subLine);
2713 startPos = endPos;
2714 if (!deco->rs.ValueAt(startPos)) {
2715 startPos = deco->rs.EndRun(startPos);
2721 // Use indicators to highlight matching braces
2722 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
2723 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
2724 int braceIndicator = (bracesMatchStyle == STYLE_BRACELIGHT) ? vs.braceHighlightIndicator : vs.braceBadLightIndicator;
2725 if (under == vsDraw.indicators[braceIndicator].under) {
2726 Range rangeLine(posLineStart + lineStart, posLineEnd);
2727 if (rangeLine.ContainsCharacter(braces[0])) {
2728 int braceOffset = braces[0] - posLineStart;
2729 if (braceOffset < ll->numCharsInLine) {
2730 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2733 if (rangeLine.ContainsCharacter(braces[1])) {
2734 int braceOffset = braces[1] - posLineStart;
2735 if (braceOffset < ll->numCharsInLine) {
2736 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2743 void Editor::DrawAnnotation(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2744 PRectangle rcLine, LineLayout *ll, int subLine) {
2745 int indent = pdoc->GetLineIndentation(line) * vsDraw.spaceWidth;
2746 PRectangle rcSegment = rcLine;
2747 int annotationLine = subLine - ll->lines;
2748 const StyledText stAnnotation = pdoc->AnnotationStyledText(line);
2749 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
2750 surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
2751 rcSegment.left = xStart;
2752 if (trackLineWidth || (vs.annotationVisible == ANNOTATION_BOXED)) {
2753 // Only care about calculating width if tracking or need to draw box
2754 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
2755 if (vs.annotationVisible == ANNOTATION_BOXED) {
2756 widthAnnotation += vsDraw.spaceWidth * 2; // Margins
2758 if (widthAnnotation > lineWidthMaxSeen)
2759 lineWidthMaxSeen = widthAnnotation;
2760 if (vs.annotationVisible == ANNOTATION_BOXED) {
2761 rcSegment.left = xStart + indent;
2762 rcSegment.right = rcSegment.left + widthAnnotation;
2765 const int annotationLines = pdoc->AnnotationLines(line);
2766 size_t start = 0;
2767 size_t lengthAnnotation = stAnnotation.LineLength(start);
2768 int lineInAnnotation = 0;
2769 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
2770 start += lengthAnnotation + 1;
2771 lengthAnnotation = stAnnotation.LineLength(start);
2772 lineInAnnotation++;
2774 PRectangle rcText = rcSegment;
2775 if (vs.annotationVisible == ANNOTATION_BOXED) {
2776 surface->FillRectangle(rcText,
2777 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
2778 rcText.left += vsDraw.spaceWidth;
2780 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText, rcText.top + vsDraw.maxAscent,
2781 stAnnotation, start, lengthAnnotation);
2782 if (vs.annotationVisible == ANNOTATION_BOXED) {
2783 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
2784 surface->MoveTo(rcSegment.left, rcSegment.top);
2785 surface->LineTo(rcSegment.left, rcSegment.bottom);
2786 surface->MoveTo(rcSegment.right, rcSegment.top);
2787 surface->LineTo(rcSegment.right, rcSegment.bottom);
2788 if (subLine == ll->lines) {
2789 surface->MoveTo(rcSegment.left, rcSegment.top);
2790 surface->LineTo(rcSegment.right, rcSegment.top);
2792 if (subLine == ll->lines+annotationLines-1) {
2793 surface->MoveTo(rcSegment.left, rcSegment.bottom - 1);
2794 surface->LineTo(rcSegment.right, rcSegment.bottom - 1);
2800 void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
2801 PRectangle rcLine, LineLayout *ll, int subLine) {
2803 if (subLine >= ll->lines) {
2804 DrawAnnotation(surface, vsDraw, line, xStart, rcLine, ll, subLine);
2805 return; // No further drawing
2808 PRectangle rcSegment = rcLine;
2810 // Using one font for all control characters so it can be controlled independently to ensure
2811 // the box goes around the characters tightly. Seems to be no way to work out what height
2812 // is taken by an individual character - internal leading gives varying results.
2813 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2815 // See if something overrides the line background color: Either if caret is on the line
2816 // and background color is set for that, or if a marker is defined that forces its background
2817 // color onto the line, or if a marker is defined but has no selection margin in which to
2818 // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
2819 // with the earlier taking precedence. When multiple markers cause background override,
2820 // the color for the highest numbered one is used.
2821 bool overrideBackground = false;
2822 ColourDesired background;
2823 if ((caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && (vsDraw.caretLineAlpha == SC_ALPHA_NOALPHA) && ll->containsCaret) {
2824 overrideBackground = true;
2825 background = vsDraw.caretLineBackground;
2827 if (!overrideBackground) {
2828 int marks = pdoc->GetMark(line);
2829 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
2830 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) &&
2831 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2832 background = vsDraw.markers[markBit].back;
2833 overrideBackground = true;
2835 marks >>= 1;
2838 if (!overrideBackground) {
2839 if (vsDraw.maskInLine) {
2840 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
2841 if (marksMasked) {
2842 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
2843 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY) &&
2844 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2845 overrideBackground = true;
2846 background = vsDraw.markers[markBit].back;
2848 marksMasked >>= 1;
2854 const bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
2855 (!overrideBackground) && (vsDraw.whitespaceColours.back.isSet);
2857 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2858 const XYPOSITION indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
2859 const XYPOSITION epsilon = 0.0001f; // A small nudge to avoid floating point precision issues
2861 const int posLineStart = pdoc->LineStart(line);
2863 const int startseg = ll->LineStart(subLine);
2864 const XYACCUMULATOR subLineStart = ll->positions[startseg];
2865 int lineStart = 0;
2866 int lineEnd = 0;
2867 if (subLine < ll->lines) {
2868 lineStart = ll->LineStart(subLine);
2869 lineEnd = ll->LineStart(subLine + 1);
2870 if (subLine == ll->lines - 1) {
2871 lineEnd = ll->numCharsBeforeEOL;
2875 const ColourDesired wrapColour = vsDraw.WrapColour();
2877 bool drawWrapMarkEnd = false;
2879 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2880 if (subLine + 1 < ll->lines) {
2881 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
2885 if (ll->wrapIndent != 0) {
2887 bool continuedWrapLine = false;
2888 if (subLine < ll->lines) {
2889 continuedWrapLine = ll->LineStart(subLine) != 0;
2892 if (continuedWrapLine) {
2893 // draw continuation rect
2894 PRectangle rcPlace = rcSegment;
2896 rcPlace.left = ll->positions[startseg] + xStart - subLineStart;
2897 rcPlace.right = rcPlace.left + ll->wrapIndent;
2899 // default bgnd here..
2900 surface->FillRectangle(rcSegment, overrideBackground ? background :
2901 vsDraw.styles[STYLE_DEFAULT].back);
2903 // main line style would be below but this would be inconsistent with end markers
2904 // also would possibly not be the style at wrap point
2905 //int styleMain = ll->styles[lineStart];
2906 //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back);
2908 if (vsDraw.wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
2910 if (vsDraw.wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
2911 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2912 else
2913 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2915 DrawWrapMarker(surface, rcPlace, false, wrapColour);
2918 xStart += static_cast<int>(ll->wrapIndent);
2922 const bool selBackDrawn = vsDraw.selColours.back.isSet &&
2923 ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA));
2925 // Does not take margin into account but not significant
2926 const int xStartVisible = static_cast<int>(subLineStart) - xStart;
2928 ll->psel = &sel;
2930 if (twoPhaseDraw) {
2931 BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, xStartVisible, selBackDrawn, pdoc, &reprs);
2933 // Background drawing loop
2934 while (bfBack.More()) {
2936 const TextSegment ts = bfBack.Next();
2937 const int i = ts.end() - 1;
2938 const int iDoc = i + posLineStart;
2940 rcSegment.left = ll->positions[ts.start] + xStart - subLineStart;
2941 rcSegment.right = ll->positions[ts.end()] + xStart - subLineStart;
2942 // Only try to draw if really visible - enhances performance by not calling environment to
2943 // draw strings that are completely past the right side of the window.
2944 if (rcSegment.Intersects(rcLine)) {
2945 // Clip to line rectangle, since may have a huge position which will not work with some platforms
2946 if (rcSegment.left < rcLine.left)
2947 rcSegment.left = rcLine.left;
2948 if (rcSegment.right > rcLine.right)
2949 rcSegment.right = rcLine.right;
2951 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2952 const bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2953 ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, inSelection,
2954 inHotspot, ll->styles[i], i, ll);
2955 if (ts.representation) {
2956 if (ll->chars[i] == '\t') {
2957 // Tab display
2958 if (drawWhitespaceBackground &&
2959 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2960 textBack = vsDraw.whitespaceColours.back;
2961 } else {
2962 // Blob display
2963 inIndentation = false;
2965 surface->FillRectangle(rcSegment, textBack);
2966 } else {
2967 // Normal text display
2968 surface->FillRectangle(rcSegment, textBack);
2969 if (vsDraw.viewWhitespace != wsInvisible ||
2970 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
2971 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
2972 if (ll->chars[cpos + ts.start] == ' ') {
2973 if (drawWhitespaceBackground &&
2974 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
2975 PRectangle rcSpace(ll->positions[cpos + ts.start] + xStart - subLineStart,
2976 rcSegment.top,
2977 ll->positions[cpos + ts.start + 1] + xStart - subLineStart,
2978 rcSegment.bottom);
2979 surface->FillRectangle(rcSpace, vsDraw.whitespaceColours.back);
2981 } else {
2982 inIndentation = false;
2987 } else if (rcSegment.left > rcLine.right) {
2988 break;
2992 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
2993 xStart, subLine, subLineStart, overrideBackground, background,
2994 drawWrapMarkEnd, wrapColour);
2997 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, true);
2999 if (vsDraw.edgeState == EDGE_LINE) {
3000 int edgeX = vsDraw.theEdge * vsDraw.spaceWidth;
3001 rcSegment.left = edgeX + xStart;
3002 if ((ll->wrapIndent != 0) && (lineStart != 0))
3003 rcSegment.left -= ll->wrapIndent;
3004 rcSegment.right = rcSegment.left + 1;
3005 surface->FillRectangle(rcSegment, vsDraw.edgecolour);
3008 // Draw underline mark as part of background if not transparent
3009 int marks = pdoc->GetMark(line);
3010 int markBit;
3011 for (markBit = 0; (markBit < 32) && marks; markBit++) {
3012 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
3013 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
3014 PRectangle rcUnderline = rcLine;
3015 rcUnderline.top = rcUnderline.bottom - 2;
3016 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
3018 marks >>= 1;
3021 inIndentation = subLine == 0; // Do not handle indentation except on first subline.
3022 // Foreground drawing loop
3023 BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, xStartVisible,
3024 ((!twoPhaseDraw && selBackDrawn) || vsDraw.selColours.fore.isSet), pdoc, &reprs);
3026 while (bfFore.More()) {
3028 const TextSegment ts = bfFore.Next();
3029 const int i = ts.end() - 1;
3030 const int iDoc = i + posLineStart;
3032 rcSegment.left = ll->positions[ts.start] + xStart - subLineStart;
3033 rcSegment.right = ll->positions[ts.end()] + xStart - subLineStart;
3034 // Only try to draw if really visible - enhances performance by not calling environment to
3035 // draw strings that are completely past the right side of the window.
3036 if (rcSegment.Intersects(rcLine)) {
3037 int styleMain = ll->styles[i];
3038 ColourDesired textFore = vsDraw.styles[styleMain].fore;
3039 Font &textFont = vsDraw.styles[styleMain].font;
3040 //hotspot foreground
3041 if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) {
3042 if (vsDraw.hotspotColours.fore.isSet)
3043 textFore = vsDraw.hotspotColours.fore;
3045 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
3046 if (inSelection && (vsDraw.selColours.fore.isSet)) {
3047 textFore = (inSelection == 1) ? vsDraw.selColours.fore : vsDraw.selAdditionalForeground;
3049 const bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
3050 ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
3051 if (ts.representation) {
3052 if (ll->chars[i] == '\t') {
3053 // Tab display
3054 if (!twoPhaseDraw) {
3055 if (drawWhitespaceBackground &&
3056 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
3057 textBack = vsDraw.whitespaceColours.back;
3058 surface->FillRectangle(rcSegment, textBack);
3060 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
3061 for (int indentCount = (ll->positions[i] + epsilon) / indentWidth;
3062 indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
3063 indentCount++) {
3064 if (indentCount > 0) {
3065 int xIndent = indentCount * indentWidth;
3066 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3067 (ll->xHighlightGuide == xIndent));
3071 if (vsDraw.viewWhitespace != wsInvisible) {
3072 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3073 if (vsDraw.whitespaceColours.fore.isSet)
3074 textFore = vsDraw.whitespaceColours.fore;
3075 surface->PenColour(textFore);
3076 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
3077 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
3078 DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
3081 } else {
3082 inIndentation = false;
3083 if (vsDraw.controlCharSymbol >= 32) {
3084 char cc[2] = { static_cast<char>(vsDraw.controlCharSymbol), '\0' };
3085 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
3086 rcSegment.top + vsDraw.maxAscent,
3087 cc, 1, textBack, textFore);
3088 } else {
3089 DrawTextBlob(surface, vsDraw, rcSegment, ts.representation->stringRep.c_str(), textBack, textFore, twoPhaseDraw);
3092 } else {
3093 // Normal text display
3094 if (vsDraw.styles[styleMain].visible) {
3095 if (twoPhaseDraw) {
3096 surface->DrawTextTransparent(rcSegment, textFont,
3097 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
3098 i - ts.start + 1, textFore);
3099 } else {
3100 surface->DrawTextNoClip(rcSegment, textFont,
3101 rcSegment.top + vsDraw.maxAscent, ll->chars + ts.start,
3102 i - ts.start + 1, textFore, textBack);
3105 if (vsDraw.viewWhitespace != wsInvisible ||
3106 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
3107 for (int cpos = 0; cpos <= i - ts.start; cpos++) {
3108 if (ll->chars[cpos + ts.start] == ' ') {
3109 if (vsDraw.viewWhitespace != wsInvisible) {
3110 if (vsDraw.whitespaceColours.fore.isSet)
3111 textFore = vsDraw.whitespaceColours.fore;
3112 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3113 XYPOSITION xmid = (ll->positions[cpos + ts.start] + ll->positions[cpos + ts.start + 1]) / 2;
3114 if (!twoPhaseDraw && drawWhitespaceBackground &&
3115 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
3116 textBack = vsDraw.whitespaceColours.back;
3117 PRectangle rcSpace(ll->positions[cpos + ts.start] + xStart - subLineStart,
3118 rcSegment.top,
3119 ll->positions[cpos + ts.start + 1] + xStart - subLineStart,
3120 rcSegment.bottom);
3121 surface->FillRectangle(rcSpace, textBack);
3123 PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
3124 rcDot.right = rcDot.left + vs.whitespaceSize;
3125 rcDot.bottom = rcDot.top + vs.whitespaceSize;
3126 surface->FillRectangle(rcDot, textFore);
3129 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
3130 for (int indentCount = (ll->positions[cpos + ts.start] + epsilon) / indentWidth;
3131 indentCount <= (ll->positions[cpos + ts.start + 1] - epsilon) / indentWidth;
3132 indentCount++) {
3133 if (indentCount > 0) {
3134 int xIndent = indentCount * indentWidth;
3135 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3136 (ll->xHighlightGuide == xIndent));
3140 } else {
3141 inIndentation = false;
3146 if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd) {
3147 PRectangle rcUL = rcSegment;
3148 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3149 rcUL.bottom = rcUL.top + 1;
3150 if (vsDraw.hotspotColours.fore.isSet)
3151 surface->FillRectangle(rcUL, vsDraw.hotspotColours.fore);
3152 else
3153 surface->FillRectangle(rcUL, textFore);
3154 } else if (vsDraw.styles[styleMain].underline) {
3155 PRectangle rcUL = rcSegment;
3156 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3157 rcUL.bottom = rcUL.top + 1;
3158 surface->FillRectangle(rcUL, textFore);
3160 } else if (rcSegment.left > rcLine.right) {
3161 break;
3164 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
3165 && (subLine == 0)) {
3166 int indentSpace = pdoc->GetLineIndentation(line);
3167 int xStartText = ll->positions[pdoc->GetLineIndentPosition(line) - posLineStart];
3169 // Find the most recent line with some text
3171 int lineLastWithText = line;
3172 while (lineLastWithText > Platform::Maximum(line-20, 0) && pdoc->IsWhiteLine(lineLastWithText)) {
3173 lineLastWithText--;
3175 if (lineLastWithText < line) {
3176 xStartText = 100000; // Don't limit to visible indentation on empty line
3177 // This line is empty, so use indentation of last line with text
3178 int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText);
3179 int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
3180 if (isFoldHeader) {
3181 // Level is one more level than parent
3182 indentLastWithText += pdoc->IndentSize();
3184 if (vsDraw.viewIndentationGuides == ivLookForward) {
3185 // In viLookForward mode, previous line only used if it is a fold header
3186 if (isFoldHeader) {
3187 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3189 } else { // viLookBoth
3190 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3194 int lineNextWithText = line;
3195 while (lineNextWithText < Platform::Minimum(line+20, pdoc->LinesTotal()) && pdoc->IsWhiteLine(lineNextWithText)) {
3196 lineNextWithText++;
3198 if (lineNextWithText > line) {
3199 xStartText = 100000; // Don't limit to visible indentation on empty line
3200 // This line is empty, so use indentation of first next line with text
3201 indentSpace = Platform::Maximum(indentSpace,
3202 pdoc->GetLineIndentation(lineNextWithText));
3205 for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) {
3206 int xIndent = indentPos * vsDraw.spaceWidth;
3207 if (xIndent < xStartText) {
3208 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3209 (ll->xHighlightGuide == xIndent));
3214 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, false);
3216 // End of the drawing of the current line
3217 if (!twoPhaseDraw) {
3218 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
3219 xStart, subLine, subLineStart, overrideBackground, background,
3220 drawWrapMarkEnd, wrapColour);
3222 if (!hideSelection && ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA))) {
3223 // For each selection draw
3224 int virtualSpaces = 0;
3225 if (subLine == (ll->lines - 1)) {
3226 virtualSpaces = sel.VirtualSpaceFor(pdoc->LineEnd(line));
3228 SelectionPosition posStart(posLineStart + lineStart);
3229 SelectionPosition posEnd(posLineStart + lineEnd, virtualSpaces);
3230 SelectionSegment virtualSpaceRange(posStart, posEnd);
3231 for (size_t r=0; r<sel.Count(); r++) {
3232 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
3233 if (alpha != SC_ALPHA_NOALPHA) {
3234 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
3235 if (!portion.Empty()) {
3236 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
3237 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
3238 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
3239 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3240 if ((portion.start.Position() - posLineStart) == lineStart && sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
3241 rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
3243 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
3244 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
3245 if (rcSegment.right > rcLine.left)
3246 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == sel.Main()), alpha);
3252 // Draw any translucent whole line states
3253 rcSegment = rcLine;
3254 if ((caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) {
3255 SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
3257 marks = pdoc->GetMark(line);
3258 for (markBit = 0; (markBit < 32) && marks; markBit++) {
3259 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
3260 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3261 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
3262 PRectangle rcUnderline = rcSegment;
3263 rcUnderline.top = rcUnderline.bottom - 2;
3264 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3266 marks >>= 1;
3268 if (vsDraw.maskInLine) {
3269 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
3270 if (marksMasked) {
3271 for (markBit = 0; (markBit < 32) && marksMasked; markBit++) {
3272 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
3273 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3275 marksMasked >>= 1;
3281 void Editor::DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine,
3282 int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) {
3284 int lineStart = ll->LineStart(subLine);
3285 int posBefore = posCaret;
3286 int posAfter = MovePositionOutsideChar(posCaret + 1, 1);
3287 int numCharsToDraw = posAfter - posCaret;
3289 // Work out where the starting and ending offsets are. We need to
3290 // see if the previous character shares horizontal space, such as a
3291 // glyph / combining character. If so we'll need to draw that too.
3292 int offsetFirstChar = offset;
3293 int offsetLastChar = offset + (posAfter - posCaret);
3294 while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
3295 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
3296 // The char does not share horizontal space
3297 break;
3299 // Char shares horizontal space, update the numChars to draw
3300 // Update posBefore to point to the prev char
3301 posBefore = MovePositionOutsideChar(posBefore - 1, -1);
3302 numCharsToDraw = posAfter - posBefore;
3303 offsetFirstChar = offset - (posCaret - posBefore);
3306 // See if the next character shares horizontal space, if so we'll
3307 // need to draw that too.
3308 if (offsetFirstChar < 0)
3309 offsetFirstChar = 0;
3310 numCharsToDraw = offsetLastChar - offsetFirstChar;
3311 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
3312 // Update posAfter to point to the 2nd next char, this is where
3313 // the next character ends, and 2nd next begins. We'll need
3314 // to compare these two
3315 posBefore = posAfter;
3316 posAfter = MovePositionOutsideChar(posAfter + 1, 1);
3317 offsetLastChar = offset + (posAfter - posCaret);
3318 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
3319 // The char does not share horizontal space
3320 break;
3322 // Char shares horizontal space, update the numChars to draw
3323 numCharsToDraw = offsetLastChar - offsetFirstChar;
3326 // We now know what to draw, update the caret drawing rectangle
3327 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
3328 rcCaret.right = ll->positions[offsetFirstChar+numCharsToDraw] - ll->positions[lineStart] + xStart;
3330 // Adjust caret position to take into account any word wrapping symbols.
3331 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3332 XYPOSITION wordWrapCharWidth = ll->wrapIndent;
3333 rcCaret.left += wordWrapCharWidth;
3334 rcCaret.right += wordWrapCharWidth;
3337 // This character is where the caret block is, we override the colours
3338 // (inversed) for drawing the caret here.
3339 int styleMain = ll->styles[offsetFirstChar];
3340 surface->DrawTextClipped(rcCaret, vsDraw.styles[styleMain].font,
3341 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
3342 numCharsToDraw, vsDraw.styles[styleMain].back,
3343 caretColour);
3346 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
3347 if (!pixmapSelPattern->Initialised()) {
3348 const int patternSize = 8;
3349 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3350 pixmapSelPatternOffset1->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3351 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
3352 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
3353 // way between the chrome colour and the chrome highlight colour making a nice transition
3354 // between the window chrome and the content area. And it works in low colour depths.
3355 PRectangle rcPattern(0, 0, patternSize, patternSize);
3357 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
3358 ColourDesired colourFMFill = vs.selbar;
3359 ColourDesired colourFMStripes = vs.selbarlight;
3361 if (!(vs.selbarlight == ColourDesired(0xff, 0xff, 0xff))) {
3362 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
3363 // (Typically, the highlight colour is white.)
3364 colourFMFill = vs.selbarlight;
3367 if (vs.foldmarginColour.isSet) {
3368 // override default fold margin colour
3369 colourFMFill = vs.foldmarginColour;
3371 if (vs.foldmarginHighlightColour.isSet) {
3372 // override default fold margin highlight colour
3373 colourFMStripes = vs.foldmarginHighlightColour;
3376 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
3377 pixmapSelPatternOffset1->FillRectangle(rcPattern, colourFMStripes);
3378 for (int y = 0; y < patternSize; y++) {
3379 for (int x = y % 2; x < patternSize; x+=2) {
3380 PRectangle rcPixel(x, y, x+1, y+1);
3381 pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);
3382 pixmapSelPatternOffset1->FillRectangle(rcPixel, colourFMFill);
3387 if (!pixmapIndentGuide->Initialised()) {
3388 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
3389 pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3390 pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3391 PRectangle rcIG(0, 0, 1, vs.lineHeight);
3392 pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back);
3393 pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore);
3394 pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back);
3395 pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore);
3396 for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
3397 PRectangle rcPixel(0, stripe, 1, stripe+1);
3398 pixmapIndentGuide->FillRectangle(rcPixel, vs.styles[STYLE_INDENTGUIDE].fore);
3399 pixmapIndentGuideHighlight->FillRectangle(rcPixel, vs.styles[STYLE_BRACELIGHT].fore);
3403 if (bufferedDraw) {
3404 if (!pixmapLine->Initialised()) {
3405 PRectangle rcClient = GetClientRectangle();
3406 pixmapLine->InitPixMap(rcClient.Width(), vs.lineHeight,
3407 surfaceWindow, wMain.GetID());
3408 pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
3409 rcClient.Height(), surfaceWindow, wMain.GetID());
3414 void Editor::DrawCarets(Surface *surface, ViewStyle &vsDraw, int lineDoc, int xStart,
3415 PRectangle rcLine, LineLayout *ll, int subLine) {
3416 // When drag is active it is the only caret drawn
3417 bool drawDrag = posDrag.IsValid();
3418 if (hideSelection && !drawDrag)
3419 return;
3420 const int posLineStart = pdoc->LineStart(lineDoc);
3421 // For each selection draw
3422 for (size_t r=0; (r<sel.Count()) || drawDrag; r++) {
3423 const bool mainCaret = r == sel.Main();
3424 const SelectionPosition posCaret = (drawDrag ? posDrag : sel.Range(r).caret);
3425 const int offset = posCaret.Position() - posLineStart;
3426 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
3427 const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
3428 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
3429 XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
3430 if (ll->wrapIndent != 0) {
3431 int lineStart = ll->LineStart(subLine);
3432 if (lineStart != 0) // Wrapped
3433 xposCaret += ll->wrapIndent;
3435 bool caretBlinkState = (caret.active && caret.on) || (!additionalCaretsBlink && !mainCaret);
3436 bool caretVisibleState = additionalCaretsVisible || mainCaret;
3437 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
3438 ((posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
3439 bool caretAtEOF = false;
3440 bool caretAtEOL = false;
3441 bool drawBlockCaret = false;
3442 XYPOSITION widthOverstrikeCaret;
3443 XYPOSITION caretWidthOffset = 0;
3444 PRectangle rcCaret = rcLine;
3446 if (posCaret.Position() == pdoc->Length()) { // At end of document
3447 caretAtEOF = true;
3448 widthOverstrikeCaret = vsDraw.aveCharWidth;
3449 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
3450 caretAtEOL = true;
3451 widthOverstrikeCaret = vsDraw.aveCharWidth;
3452 } else {
3453 widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
3455 if (widthOverstrikeCaret < 3) // Make sure its visible
3456 widthOverstrikeCaret = 3;
3458 if (xposCaret > 0)
3459 caretWidthOffset = 0.51f; // Move back so overlaps both character cells.
3460 xposCaret += xStart;
3461 if (posDrag.IsValid()) {
3462 /* Dragging text, use a line caret */
3463 rcCaret.left = RoundXYPosition(xposCaret - caretWidthOffset);
3464 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3465 } else if (inOverstrike && drawOverstrikeCaret) {
3466 /* Overstrike (insert mode), use a modified bar caret */
3467 rcCaret.top = rcCaret.bottom - 2;
3468 rcCaret.left = xposCaret + 1;
3469 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
3470 } else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) {
3471 /* Block caret */
3472 rcCaret.left = xposCaret;
3473 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
3474 drawBlockCaret = true;
3475 rcCaret.right = xposCaret + widthOverstrikeCaret;
3476 } else {
3477 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
3479 } else {
3480 /* Line caret */
3481 rcCaret.left = RoundXYPosition(xposCaret - caretWidthOffset);
3482 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3484 ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
3485 if (drawBlockCaret) {
3486 DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
3487 } else {
3488 surface->FillRectangle(rcCaret, caretColour);
3492 if (drawDrag)
3493 break;
3497 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
3498 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
3499 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
3500 AllocateGraphics();
3502 RefreshStyleData();
3503 if (paintState == paintAbandoned)
3504 return; // Scroll bars may have changed so need redraw
3505 RefreshPixMaps(surfaceWindow);
3507 paintAbandonedByStyling = false;
3509 StyleToPositionInView(PositionAfterArea(rcArea));
3511 PRectangle rcClient = GetClientRectangle();
3512 Point ptOrigin = GetVisibleOriginInMain();
3513 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
3514 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3516 int screenLinePaintFirst = rcArea.top / vs.lineHeight;
3518 int xStart = vs.textStart - xOffset + ptOrigin.x;
3519 int ypos = 0;
3520 if (!bufferedDraw)
3521 ypos += screenLinePaintFirst * vs.lineHeight;
3522 int yposScreen = screenLinePaintFirst * vs.lineHeight;
3524 if (NotifyUpdateUI()) {
3525 RefreshStyleData();
3526 RefreshPixMaps(surfaceWindow);
3529 // Wrap the visible lines if needed.
3530 if (WrapLines(wsVisible)) {
3531 // The wrapping process has changed the height of some lines so
3532 // abandon this paint for a complete repaint.
3533 if (AbandonPaint()) {
3534 return;
3536 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
3538 PLATFORM_ASSERT(pixmapSelPattern->Initialised());
3540 if (!bufferedDraw)
3541 surfaceWindow->SetClip(rcArea);
3543 if (paintState != paintAbandoned) {
3544 if (vs.marginInside) {
3545 PaintSelMargin(surfaceWindow, rcArea);
3546 PRectangle rcRightMargin = rcClient;
3547 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
3548 if (rcArea.Intersects(rcRightMargin)) {
3549 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
3551 } else { // Else separate view so separate paint event but leftMargin included to allow overlap
3552 PRectangle rcLeftMargin = rcArea;
3553 rcLeftMargin.left = 0;
3554 rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
3555 if (rcArea.Intersects(rcLeftMargin)) {
3556 surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[STYLE_DEFAULT].back);
3561 if (paintState == paintAbandoned) {
3562 // Either styling or NotifyUpdateUI noticed that painting is needed
3563 // outside the current painting rectangle
3564 //Platform::DebugPrintf("Abandoning paint\n");
3565 if (Wrapping()) {
3566 if (paintAbandonedByStyling) {
3567 // Styling has spilled over a line end, such as occurs by starting a multiline
3568 // comment. The width of subsequent text may have changed, so rewrap.
3569 NeedWrapping(cs.DocFromDisplay(topLine));
3572 return;
3574 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
3576 // Allow text at start of line to overlap 1 pixel into the margin as this displays
3577 // serifs and italic stems for aliased text.
3578 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
3580 // Do the painting
3581 if (rcArea.right > vs.textStart - leftTextOverlap) {
3583 Surface *surface = surfaceWindow;
3584 if (bufferedDraw) {
3585 surface = pixmapLine;
3586 PLATFORM_ASSERT(pixmapLine->Initialised());
3588 surface->SetUnicodeMode(IsUnicodeMode());
3589 surface->SetDBCSMode(CodePage());
3591 int visibleLine = TopLineOfMain() + screenLinePaintFirst;
3593 SelectionPosition posCaret = sel.RangeMain().caret;
3594 if (posDrag.IsValid())
3595 posCaret = posDrag;
3596 int lineCaret = pdoc->LineFromPosition(posCaret.Position());
3598 PRectangle rcTextArea = rcClient;
3599 if (vs.marginInside) {
3600 rcTextArea.left += vs.textStart;
3601 rcTextArea.right -= vs.rightMarginWidth;
3602 } else {
3603 rcTextArea = rcArea;
3606 // Remove selection margin from drawing area so text will not be drawn
3607 // on it in unbuffered mode.
3608 if (!bufferedDraw && vs.marginInside) {
3609 PRectangle rcClipText = rcTextArea;
3610 rcClipText.left -= leftTextOverlap;
3611 surfaceWindow->SetClip(rcClipText);
3614 // Loop on visible lines
3615 //double durLayout = 0.0;
3616 //double durPaint = 0.0;
3617 //double durCopy = 0.0;
3618 //ElapsedTime etWhole;
3619 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
3620 AutoLineLayout ll(llc, 0);
3621 while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
3623 int lineDoc = cs.DocFromDisplay(visibleLine);
3624 // Only visible lines should be handled by the code within the loop
3625 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
3626 int lineStartSet = cs.DisplayFromDoc(lineDoc);
3627 int subLine = visibleLine - lineStartSet;
3629 // Copy this line and its styles from the document into local arrays
3630 // and determine the x position at which each character starts.
3631 //ElapsedTime et;
3632 if (lineDoc != lineDocPrevious) {
3633 ll.Set(0);
3634 ll.Set(RetrieveLineLayout(lineDoc));
3635 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
3636 lineDocPrevious = lineDoc;
3638 //durLayout += et.Duration(true);
3640 if (ll) {
3641 ll->containsCaret = lineDoc == lineCaret;
3642 if (hideSelection) {
3643 ll->containsCaret = false;
3646 GetHotSpotRange(ll->hsStart, ll->hsEnd);
3648 PRectangle rcLine = rcTextArea;
3649 rcLine.top = ypos;
3650 rcLine.bottom = ypos + vs.lineHeight;
3652 bool bracesIgnoreStyle = false;
3653 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
3654 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
3655 bracesIgnoreStyle = true;
3657 Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
3658 // Highlight the current braces if any
3659 ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
3660 highlightGuideColumn * vs.spaceWidth, bracesIgnoreStyle);
3662 if (leftTextOverlap && bufferedDraw) {
3663 PRectangle rcSpacer = rcLine;
3664 rcSpacer.right = rcSpacer.left;
3665 rcSpacer.left -= 1;
3666 surface->FillRectangle(rcSpacer, vs.styles[STYLE_DEFAULT].back);
3669 // Draw the line
3670 DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
3671 //durPaint += et.Duration(true);
3673 // Restore the previous styles for the brace highlights in case layout is in cache.
3674 ll->RestoreBracesHighlight(rangeLine, braces, bracesIgnoreStyle);
3676 bool expanded = cs.GetExpanded(lineDoc);
3677 const int level = pdoc->GetLevel(lineDoc);
3678 const int levelNext = pdoc->GetLevel(lineDoc + 1);
3679 if ((level & SC_FOLDLEVELHEADERFLAG) &&
3680 ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
3681 // Paint the line above the fold
3682 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
3684 (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
3685 PRectangle rcFoldLine = rcLine;
3686 rcFoldLine.bottom = rcFoldLine.top + 1;
3687 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore);
3689 // Paint the line below the fold
3690 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
3692 (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
3693 PRectangle rcFoldLine = rcLine;
3694 rcFoldLine.top = rcFoldLine.bottom - 1;
3695 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore);
3699 DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine);
3701 if (bufferedDraw) {
3702 Point from(vs.textStart-leftTextOverlap, 0);
3703 PRectangle rcCopyArea(vs.textStart-leftTextOverlap, yposScreen,
3704 rcClient.right - vs.rightMarginWidth, yposScreen + vs.lineHeight);
3705 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
3708 lineWidthMaxSeen = Platform::Maximum(
3709 lineWidthMaxSeen, ll->positions[ll->numCharsInLine]);
3710 //durCopy += et.Duration(true);
3713 if (!bufferedDraw) {
3714 ypos += vs.lineHeight;
3717 yposScreen += vs.lineHeight;
3718 visibleLine++;
3720 //gdk_flush();
3722 ll.Set(0);
3723 //if (durPaint < 0.00000001)
3724 // durPaint = 0.00000001;
3726 // Right column limit indicator
3727 PRectangle rcBeyondEOF = (vs.marginInside) ? rcClient : rcArea;
3728 rcBeyondEOF.left = vs.textStart;
3729 rcBeyondEOF.right = rcBeyondEOF.right - ((vs.marginInside) ? vs.rightMarginWidth : 0);
3730 rcBeyondEOF.top = (cs.LinesDisplayed() - TopLineOfMain()) * vs.lineHeight;
3731 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
3732 surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back);
3733 if (vs.edgeState == EDGE_LINE) {
3734 int edgeX = vs.theEdge * vs.spaceWidth;
3735 rcBeyondEOF.left = edgeX + xStart;
3736 rcBeyondEOF.right = rcBeyondEOF.left + 1;
3737 surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour);
3740 //Platform::DebugPrintf(
3741 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
3742 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
3743 NotifyPainted();
3747 // Space (3 space characters) between line numbers and text when printing.
3748 #define lineNumberPrintSpace " "
3750 ColourDesired InvertedLight(ColourDesired orig) {
3751 unsigned int r = orig.GetRed();
3752 unsigned int g = orig.GetGreen();
3753 unsigned int b = orig.GetBlue();
3754 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
3755 unsigned int il = 0xff - l;
3756 if (l == 0)
3757 return ColourDesired(0xff, 0xff, 0xff);
3758 r = r * il / l;
3759 g = g * il / l;
3760 b = b * il / l;
3761 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
3764 // This is mostly copied from the Paint method but with some things omitted
3765 // such as the margin markers, line numbers, selection and caret
3766 // Should be merged back into a combined Draw method.
3767 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
3768 if (!pfr)
3769 return 0;
3771 AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
3772 if (!surface)
3773 return 0;
3774 AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
3775 if (!surfaceMeasure) {
3776 return 0;
3779 // Can't use measurements cached for screen
3780 posCache.Clear();
3782 ViewStyle vsPrint(vs);
3783 vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
3785 // Modify the view style for printing as do not normally want any of the transient features to be printed
3786 // Printing supports only the line number margin.
3787 int lineNumberIndex = -1;
3788 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
3789 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
3790 lineNumberIndex = margin;
3791 } else {
3792 vsPrint.ms[margin].width = 0;
3795 vsPrint.fixedColumnWidth = 0;
3796 vsPrint.zoomLevel = printParameters.magnification;
3797 // Don't show indentation guides
3798 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
3799 vsPrint.viewIndentationGuides = ivNone;
3800 // Don't show the selection when printing
3801 vsPrint.selColours.back.isSet = false;
3802 vsPrint.selColours.fore.isSet = false;
3803 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
3804 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
3805 vsPrint.whitespaceColours.back.isSet = false;
3806 vsPrint.whitespaceColours.fore.isSet = false;
3807 vsPrint.showCaretLineBackground = false;
3808 vsPrint.alwaysShowCaretLineBackground = false;
3809 // Don't highlight matching braces using indicators
3810 vsPrint.braceHighlightIndicatorSet = false;
3811 vsPrint.braceBadLightIndicatorSet = false;
3813 // Set colours for printing according to users settings
3814 for (size_t sty = 0; sty < vsPrint.styles.size(); sty++) {
3815 if (printParameters.colourMode == SC_PRINT_INVERTLIGHT) {
3816 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
3817 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
3818 } else if (printParameters.colourMode == SC_PRINT_BLACKONWHITE) {
3819 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
3820 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3821 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITE) {
3822 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3823 } else if (printParameters.colourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
3824 if (sty <= STYLE_DEFAULT) {
3825 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3829 // White background for the line numbers
3830 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
3832 // Printing uses different margins, so reset screen margins
3833 vsPrint.leftMarginWidth = 0;
3834 vsPrint.rightMarginWidth = 0;
3836 vsPrint.Refresh(*surfaceMeasure, pdoc->tabInChars);
3837 // Determining width must hapen after fonts have been realised in Refresh
3838 int lineNumberWidth = 0;
3839 if (lineNumberIndex >= 0) {
3840 lineNumberWidth = surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
3841 "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace));
3842 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
3843 vsPrint.Refresh(*surfaceMeasure, pdoc->tabInChars); // Recalculate fixedColumnWidth
3846 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
3847 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
3848 if (linePrintLast < linePrintStart)
3849 linePrintLast = linePrintStart;
3850 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
3851 if (linePrintLast > linePrintMax)
3852 linePrintLast = linePrintMax;
3853 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
3854 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
3855 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
3856 int endPosPrint = pdoc->Length();
3857 if (linePrintLast < pdoc->LinesTotal())
3858 endPosPrint = pdoc->LineStart(linePrintLast + 1);
3860 // Ensure we are styled to where we are formatting.
3861 pdoc->EnsureStyledTo(endPosPrint);
3863 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
3864 int ypos = pfr->rc.top;
3866 int lineDoc = linePrintStart;
3868 int nPrintPos = pfr->chrg.cpMin;
3869 int visibleLine = 0;
3870 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
3871 if (printParameters.wrapState == eWrapNone)
3872 widthPrint = LineLayout::wrapWidthInfinite;
3874 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
3876 // When printing, the hdc and hdcTarget may be the same, so
3877 // changing the state of surfaceMeasure may change the underlying
3878 // state of surface. Therefore, any cached state is discarded before
3879 // using each surface.
3880 surfaceMeasure->FlushCachedState();
3882 // Copy this line and its styles from the document into local arrays
3883 // and determine the x position at which each character starts.
3884 LineLayout ll(pdoc->LineStart(lineDoc+1)-pdoc->LineStart(lineDoc)+1);
3885 LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
3887 ll.containsCaret = false;
3889 PRectangle rcLine;
3890 rcLine.left = pfr->rc.left;
3891 rcLine.top = ypos;
3892 rcLine.right = pfr->rc.right - 1;
3893 rcLine.bottom = ypos + vsPrint.lineHeight;
3895 // When document line is wrapped over multiple display lines, find where
3896 // to start printing from to ensure a particular position is on the first
3897 // line of the page.
3898 if (visibleLine == 0) {
3899 int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
3900 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
3901 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
3902 visibleLine = -iwl;
3906 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
3907 visibleLine = -(ll.lines - 1);
3911 if (draw && lineNumberWidth &&
3912 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
3913 (visibleLine >= 0)) {
3914 char number[100];
3915 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
3916 PRectangle rcNumber = rcLine;
3917 rcNumber.right = rcNumber.left + lineNumberWidth;
3918 // Right justify
3919 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
3920 vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
3921 surface->FlushCachedState();
3922 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
3923 ypos + vsPrint.maxAscent, number, istrlen(number),
3924 vsPrint.styles[STYLE_LINENUMBER].fore,
3925 vsPrint.styles[STYLE_LINENUMBER].back);
3928 // Draw the line
3929 surface->FlushCachedState();
3931 for (int iwl = 0; iwl < ll.lines; iwl++) {
3932 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
3933 if (visibleLine >= 0) {
3934 if (draw) {
3935 rcLine.top = ypos;
3936 rcLine.bottom = ypos + vsPrint.lineHeight;
3937 DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
3939 ypos += vsPrint.lineHeight;
3941 visibleLine++;
3942 if (iwl == ll.lines - 1)
3943 nPrintPos = pdoc->LineStart(lineDoc + 1);
3944 else
3945 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
3949 ++lineDoc;
3952 // Clear cache so measurements are not used for screen
3953 posCache.Clear();
3955 return nPrintPos;
3958 int Editor::TextWidth(int style, const char *text) {
3959 RefreshStyleData();
3960 AutoSurface surface(this);
3961 if (surface) {
3962 return surface->WidthText(vs.styles[style].font, text, istrlen(text));
3963 } else {
3964 return 1;
3968 // Empty method is overridden on GTK+ to show / hide scrollbars
3969 void Editor::ReconfigureScrollBars() {}
3971 void Editor::SetScrollBars() {
3972 RefreshStyleData();
3974 int nMax = MaxScrollPos();
3975 int nPage = LinesOnScreen();
3976 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
3977 if (modified) {
3978 DwellEnd(true);
3981 // TODO: ensure always showing as many lines as possible
3982 // May not be, if, for example, window made larger
3983 if (topLine > MaxScrollPos()) {
3984 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
3985 SetVerticalScrollPos();
3986 Redraw();
3988 if (modified) {
3989 if (!AbandonPaint())
3990 Redraw();
3992 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
3995 void Editor::ChangeSize() {
3996 DropGraphics(false);
3997 SetScrollBars();
3998 if (Wrapping()) {
3999 PRectangle rcTextArea = GetClientRectangle();
4000 rcTextArea.left = vs.textStart;
4001 rcTextArea.right -= vs.rightMarginWidth;
4002 if (wrapWidth != rcTextArea.Width()) {
4003 NeedWrapping();
4004 Redraw();
4009 int Editor::InsertSpace(int position, unsigned int spaces) {
4010 if (spaces > 0) {
4011 std::string spaceText(spaces, ' ');
4012 pdoc->InsertString(position, spaceText.c_str(), spaces);
4013 position += spaces;
4015 return position;
4018 void Editor::AddChar(char ch) {
4019 char s[2];
4020 s[0] = ch;
4021 s[1] = '\0';
4022 AddCharUTF(s, 1);
4025 void Editor::FilterSelections() {
4026 if (!additionalSelectionTyping && (sel.Count() > 1)) {
4027 SelectionRange rangeOnly = sel.RangeMain();
4028 InvalidateSelection(rangeOnly, true);
4029 sel.SetSelection(rangeOnly);
4033 static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
4034 return *a < *b;
4037 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
4038 void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
4039 FilterSelections();
4041 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
4043 std::vector<SelectionRange *> selPtrs;
4044 for (size_t r = 0; r < sel.Count(); r++) {
4045 selPtrs.push_back(&sel.Range(r));
4047 std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
4049 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
4050 rit != selPtrs.rend(); ++rit) {
4051 SelectionRange *currentSel = *rit;
4052 if (!RangeContainsProtected(currentSel->Start().Position(),
4053 currentSel->End().Position())) {
4054 int positionInsert = currentSel->Start().Position();
4055 if (!currentSel->Empty()) {
4056 if (currentSel->Length()) {
4057 pdoc->DeleteChars(positionInsert, currentSel->Length());
4058 currentSel->ClearVirtualSpace();
4059 } else {
4060 // Range is all virtual so collapse to start of virtual space
4061 currentSel->MinimizeVirtualSpace();
4063 } else if (inOverstrike) {
4064 if (positionInsert < pdoc->Length()) {
4065 if (!pdoc->IsPositionInLineEnd(positionInsert)) {
4066 pdoc->DelChar(positionInsert);
4067 currentSel->ClearVirtualSpace();
4071 positionInsert = InsertSpace(positionInsert, currentSel->caret.VirtualSpace());
4072 if (pdoc->InsertString(positionInsert, s, len)) {
4073 currentSel->caret.SetPosition(positionInsert + len);
4074 currentSel->anchor.SetPosition(positionInsert + len);
4076 currentSel->ClearVirtualSpace();
4077 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4078 if (Wrapping()) {
4079 AutoSurface surface(this);
4080 if (surface) {
4081 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
4082 SetScrollBars();
4083 SetVerticalScrollPos();
4084 Redraw();
4091 if (Wrapping()) {
4092 SetScrollBars();
4094 ThinRectangularRange();
4095 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4096 EnsureCaretVisible();
4097 // Avoid blinking during rapid typing:
4098 ShowCaretAtCurrentPosition();
4099 if ((caretSticky == SC_CARETSTICKY_OFF) ||
4100 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
4101 SetLastXChosen();
4104 if (treatAsDBCS) {
4105 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
4106 static_cast<unsigned char>(s[1]));
4107 } else if (len > 0) {
4108 int byte = static_cast<unsigned char>(s[0]);
4109 if ((byte < 0xC0) || (1 == len)) {
4110 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
4111 // characters when not in UTF-8 mode.
4112 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
4113 // characters representing themselves.
4114 } else {
4115 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
4116 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
4117 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
4118 if (byte < 0xE0) {
4119 int byte2 = static_cast<unsigned char>(s[1]);
4120 if ((byte2 & 0xC0) == 0x80) {
4121 // Two-byte-character lead-byte followed by a trail-byte.
4122 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
4124 // A two-byte-character lead-byte not followed by trail-byte
4125 // represents itself.
4126 } else if (byte < 0xF0) {
4127 int byte2 = static_cast<unsigned char>(s[1]);
4128 int byte3 = static_cast<unsigned char>(s[2]);
4129 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
4130 // Three-byte-character lead byte followed by two trail bytes.
4131 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
4132 (byte3 & 0x3F));
4134 // A three-byte-character lead-byte not followed by two trail-bytes
4135 // represents itself.
4138 NotifyChar(byte);
4141 if (recordingMacro) {
4142 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
4146 void Editor::InsertPaste(SelectionPosition selStart, const char *text, int len) {
4147 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
4148 selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
4149 if (pdoc->InsertString(selStart.Position(), text, len)) {
4150 SetEmptySelection(selStart.Position() + len);
4152 } else {
4153 // SC_MULTIPASTE_EACH
4154 for (size_t r=0; r<sel.Count(); r++) {
4155 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4156 sel.Range(r).End().Position())) {
4157 int positionInsert = sel.Range(r).Start().Position();
4158 if (!sel.Range(r).Empty()) {
4159 if (sel.Range(r).Length()) {
4160 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
4161 sel.Range(r).ClearVirtualSpace();
4162 } else {
4163 // Range is all virtual so collapse to start of virtual space
4164 sel.Range(r).MinimizeVirtualSpace();
4167 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
4168 if (pdoc->InsertString(positionInsert, text, len)) {
4169 sel.Range(r).caret.SetPosition(positionInsert + len);
4170 sel.Range(r).anchor.SetPosition(positionInsert + len);
4172 sel.Range(r).ClearVirtualSpace();
4178 void Editor::ClearSelection(bool retainMultipleSelections) {
4179 if (!sel.IsRectangular() && !retainMultipleSelections)
4180 FilterSelections();
4181 UndoGroup ug(pdoc);
4182 for (size_t r=0; r<sel.Count(); r++) {
4183 if (!sel.Range(r).Empty()) {
4184 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4185 sel.Range(r).End().Position())) {
4186 pdoc->DeleteChars(sel.Range(r).Start().Position(),
4187 sel.Range(r).Length());
4188 sel.Range(r) = SelectionRange(sel.Range(r).Start());
4192 ThinRectangularRange();
4193 sel.RemoveDuplicates();
4194 ClaimSelection();
4197 void Editor::ClearAll() {
4199 UndoGroup ug(pdoc);
4200 if (0 != pdoc->Length()) {
4201 pdoc->DeleteChars(0, pdoc->Length());
4203 if (!pdoc->IsReadOnly()) {
4204 cs.Clear();
4205 pdoc->AnnotationClearAll();
4206 pdoc->MarginClearAll();
4209 sel.Clear();
4210 SetTopLine(0);
4211 SetVerticalScrollPos();
4212 InvalidateStyleRedraw();
4215 void Editor::ClearDocumentStyle() {
4216 Decoration *deco = pdoc->decorations.root;
4217 while (deco) {
4218 // Save next in case deco deleted
4219 Decoration *decoNext = deco->next;
4220 if (deco->indicator < INDIC_CONTAINER) {
4221 pdoc->decorations.SetCurrentIndicator(deco->indicator);
4222 pdoc->DecorationFillRange(0, 0, pdoc->Length());
4224 deco = decoNext;
4226 pdoc->StartStyling(0, '\377');
4227 pdoc->SetStyleFor(pdoc->Length(), 0);
4228 cs.ShowAll();
4229 pdoc->ClearLevels();
4232 void Editor::CopyAllowLine() {
4233 SelectionText selectedText;
4234 CopySelectionRange(&selectedText, true);
4235 CopyToClipboard(selectedText);
4238 void Editor::Cut() {
4239 pdoc->CheckReadOnly();
4240 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
4241 Copy();
4242 ClearSelection();
4246 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
4247 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
4248 return;
4250 sel.Clear();
4251 sel.RangeMain() = SelectionRange(pos);
4252 int line = pdoc->LineFromPosition(sel.MainCaret());
4253 UndoGroup ug(pdoc);
4254 sel.RangeMain().caret = SelectionPosition(
4255 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
4256 int xInsert = XFromPosition(sel.RangeMain().caret);
4257 bool prevCr = false;
4258 while ((len > 0) && IsEOLChar(ptr[len-1]))
4259 len--;
4260 for (int i = 0; i < len; i++) {
4261 if (IsEOLChar(ptr[i])) {
4262 if ((ptr[i] == '\r') || (!prevCr))
4263 line++;
4264 if (line >= pdoc->LinesTotal()) {
4265 if (pdoc->eolMode != SC_EOL_LF)
4266 pdoc->InsertChar(pdoc->Length(), '\r');
4267 if (pdoc->eolMode != SC_EOL_CR)
4268 pdoc->InsertChar(pdoc->Length(), '\n');
4270 // Pad the end of lines with spaces if required
4271 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
4272 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
4273 while (XFromPosition(sel.MainCaret()) < xInsert) {
4274 pdoc->InsertChar(sel.MainCaret(), ' ');
4275 sel.RangeMain().caret.Add(1);
4278 prevCr = ptr[i] == '\r';
4279 } else {
4280 pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
4281 sel.RangeMain().caret.Add(1);
4282 prevCr = false;
4285 SetEmptySelection(pos);
4288 bool Editor::CanPaste() {
4289 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
4292 void Editor::Clear() {
4293 // If multiple selections, don't delete EOLS
4294 if (sel.Empty()) {
4295 bool singleVirtual = false;
4296 if ((sel.Count() == 1) &&
4297 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
4298 sel.RangeMain().Start().VirtualSpace()) {
4299 singleVirtual = true;
4301 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
4302 for (size_t r=0; r<sel.Count(); r++) {
4303 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
4304 if (sel.Range(r).Start().VirtualSpace()) {
4305 if (sel.Range(r).anchor < sel.Range(r).caret)
4306 sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
4307 else
4308 sel.Range(r) = SelectionRange(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
4310 if ((sel.Count() == 1) || !pdoc->IsPositionInLineEnd(sel.Range(r).caret.Position())) {
4311 pdoc->DelChar(sel.Range(r).caret.Position());
4312 sel.Range(r).ClearVirtualSpace();
4313 } // else multiple selection so don't eat line ends
4314 } else {
4315 sel.Range(r).ClearVirtualSpace();
4318 } else {
4319 ClearSelection();
4321 sel.RemoveDuplicates();
4324 void Editor::SelectAll() {
4325 sel.Clear();
4326 SetSelection(0, pdoc->Length());
4327 Redraw();
4330 void Editor::Undo() {
4331 if (pdoc->CanUndo()) {
4332 InvalidateCaret();
4333 int newPos = pdoc->Undo();
4334 if (newPos >= 0)
4335 SetEmptySelection(newPos);
4336 EnsureCaretVisible();
4340 void Editor::Redo() {
4341 if (pdoc->CanRedo()) {
4342 int newPos = pdoc->Redo();
4343 if (newPos >= 0)
4344 SetEmptySelection(newPos);
4345 EnsureCaretVisible();
4349 void Editor::DelChar() {
4350 if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) {
4351 pdoc->DelChar(sel.MainCaret());
4353 // Avoid blinking during rapid typing:
4354 ShowCaretAtCurrentPosition();
4357 void Editor::DelCharBack(bool allowLineStartDeletion) {
4358 if (!sel.IsRectangular())
4359 FilterSelections();
4360 if (sel.IsRectangular())
4361 allowLineStartDeletion = false;
4362 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
4363 if (sel.Empty()) {
4364 for (size_t r=0; r<sel.Count(); r++) {
4365 if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
4366 if (sel.Range(r).caret.VirtualSpace()) {
4367 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
4368 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
4369 } else {
4370 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4371 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
4372 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
4373 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
4374 UndoGroup ugInner(pdoc, !ug.Needed());
4375 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4376 int indentationStep = pdoc->IndentSize();
4377 if (indentation % indentationStep == 0) {
4378 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
4379 } else {
4380 pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
4382 // SetEmptySelection
4383 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos),
4384 pdoc->GetLineIndentPosition(lineCurrentPos));
4385 } else {
4386 pdoc->DelCharBack(sel.Range(r).caret.Position());
4390 } else {
4391 sel.Range(r).ClearVirtualSpace();
4394 ThinRectangularRange();
4395 } else {
4396 ClearSelection();
4398 sel.RemoveDuplicates();
4399 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
4400 // Avoid blinking during rapid typing:
4401 ShowCaretAtCurrentPosition();
4404 int Editor::ModifierFlags(bool shift, bool ctrl, bool alt, bool meta) {
4405 return
4406 (shift ? SCI_SHIFT : 0) |
4407 (ctrl ? SCI_CTRL : 0) |
4408 (alt ? SCI_ALT : 0) |
4409 (meta ? SCI_META : 0);
4412 void Editor::NotifyFocus(bool focus) {
4413 SCNotification scn = {};
4414 scn.nmhdr.code = focus ? SCN_FOCUSIN : SCN_FOCUSOUT;
4415 NotifyParent(scn);
4418 void Editor::SetCtrlID(int identifier) {
4419 ctrlID = identifier;
4422 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
4423 SCNotification scn = {};
4424 scn.nmhdr.code = SCN_STYLENEEDED;
4425 scn.position = endStyleNeeded;
4426 NotifyParent(scn);
4429 void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
4430 NotifyStyleToNeeded(endStyleNeeded);
4433 void Editor::NotifyLexerChanged(Document *, void *) {
4436 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
4437 errorStatus = status;
4440 void Editor::NotifyChar(int ch) {
4441 SCNotification scn = {};
4442 scn.nmhdr.code = SCN_CHARADDED;
4443 scn.ch = ch;
4444 NotifyParent(scn);
4447 void Editor::NotifySavePoint(bool isSavePoint) {
4448 SCNotification scn = {};
4449 if (isSavePoint) {
4450 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
4451 } else {
4452 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
4454 NotifyParent(scn);
4457 void Editor::NotifyModifyAttempt() {
4458 SCNotification scn = {};
4459 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
4460 NotifyParent(scn);
4463 void Editor::NotifyDoubleClick(Point pt, int modifiers) {
4464 SCNotification scn = {};
4465 scn.nmhdr.code = SCN_DOUBLECLICK;
4466 scn.line = LineFromLocation(pt);
4467 scn.position = PositionFromLocation(pt, true);
4468 scn.modifiers = modifiers;
4469 NotifyParent(scn);
4472 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
4473 NotifyDoubleClick(pt, ModifierFlags(shift, ctrl, alt));
4476 void Editor::NotifyHotSpotDoubleClicked(int position, int modifiers) {
4477 SCNotification scn = {};
4478 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
4479 scn.position = position;
4480 scn.modifiers = modifiers;
4481 NotifyParent(scn);
4484 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
4485 NotifyHotSpotDoubleClicked(position, ModifierFlags(shift, ctrl, alt));
4488 void Editor::NotifyHotSpotClicked(int position, int modifiers) {
4489 SCNotification scn = {};
4490 scn.nmhdr.code = SCN_HOTSPOTCLICK;
4491 scn.position = position;
4492 scn.modifiers = modifiers;
4493 NotifyParent(scn);
4496 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
4497 NotifyHotSpotClicked(position, ModifierFlags(shift, ctrl, alt));
4500 void Editor::NotifyHotSpotReleaseClick(int position, int modifiers) {
4501 SCNotification scn = {};
4502 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
4503 scn.position = position;
4504 scn.modifiers = modifiers;
4505 NotifyParent(scn);
4508 void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
4509 NotifyHotSpotReleaseClick(position, ModifierFlags(shift, ctrl, alt));
4512 bool Editor::NotifyUpdateUI() {
4513 if (needUpdateUI) {
4514 SCNotification scn = {};
4515 scn.nmhdr.code = SCN_UPDATEUI;
4516 scn.updated = needUpdateUI;
4517 NotifyParent(scn);
4518 needUpdateUI = 0;
4519 return true;
4521 return false;
4524 void Editor::NotifyPainted() {
4525 SCNotification scn = {};
4526 scn.nmhdr.code = SCN_PAINTED;
4527 NotifyParent(scn);
4530 void Editor::NotifyIndicatorClick(bool click, int position, int modifiers) {
4531 int mask = pdoc->decorations.AllOnFor(position);
4532 if ((click && mask) || pdoc->decorations.clickNotified) {
4533 SCNotification scn = {};
4534 pdoc->decorations.clickNotified = click;
4535 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
4536 scn.modifiers = modifiers;
4537 scn.position = position;
4538 NotifyParent(scn);
4542 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
4543 NotifyIndicatorClick(click, position, ModifierFlags(shift, ctrl, alt));
4546 bool Editor::NotifyMarginClick(Point pt, int modifiers) {
4547 int marginClicked = -1;
4548 int x = vs.textStart - vs.fixedColumnWidth;
4549 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
4550 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
4551 marginClicked = margin;
4552 x += vs.ms[margin].width;
4554 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
4555 int position = pdoc->LineStart(LineFromLocation(pt));
4556 if ((vs.ms[marginClicked].mask & SC_MASK_FOLDERS) && (foldAutomatic & SC_AUTOMATICFOLD_CLICK)) {
4557 const bool ctrl = (modifiers & SCI_CTRL) != 0;
4558 const bool shift = (modifiers & SCI_SHIFT) != 0;
4559 int lineClick = pdoc->LineFromPosition(position);
4560 if (shift && ctrl) {
4561 FoldAll(SC_FOLDACTION_TOGGLE);
4562 } else {
4563 int levelClick = pdoc->GetLevel(lineClick);
4564 if (levelClick & SC_FOLDLEVELHEADERFLAG) {
4565 if (shift) {
4566 // Ensure all children visible
4567 FoldExpand(lineClick, SC_FOLDACTION_EXPAND, levelClick);
4568 } else if (ctrl) {
4569 FoldExpand(lineClick, SC_FOLDACTION_TOGGLE, levelClick);
4570 } else {
4571 // Toggle this line
4572 FoldLine(lineClick, SC_FOLDACTION_TOGGLE);
4576 return true;
4578 SCNotification scn = {};
4579 scn.nmhdr.code = SCN_MARGINCLICK;
4580 scn.modifiers = modifiers;
4581 scn.position = position;
4582 scn.margin = marginClicked;
4583 NotifyParent(scn);
4584 return true;
4585 } else {
4586 return false;
4590 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
4591 return NotifyMarginClick(pt, ModifierFlags(shift, ctrl, alt));
4594 void Editor::NotifyNeedShown(int pos, int len) {
4595 SCNotification scn = {};
4596 scn.nmhdr.code = SCN_NEEDSHOWN;
4597 scn.position = pos;
4598 scn.length = len;
4599 NotifyParent(scn);
4602 void Editor::NotifyDwelling(Point pt, bool state) {
4603 SCNotification scn = {};
4604 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
4605 scn.position = PositionFromLocation(pt, true);
4606 scn.x = pt.x + vs.ExternalMarginWidth();
4607 scn.y = pt.y;
4608 NotifyParent(scn);
4611 void Editor::NotifyZoom() {
4612 SCNotification scn = {};
4613 scn.nmhdr.code = SCN_ZOOM;
4614 NotifyParent(scn);
4617 // Notifications from document
4618 void Editor::NotifyModifyAttempt(Document *, void *) {
4619 //Platform::DebugPrintf("** Modify Attempt\n");
4620 NotifyModifyAttempt();
4623 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
4624 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
4625 NotifySavePoint(atSavePoint);
4628 void Editor::CheckModificationForWrap(DocModification mh) {
4629 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
4630 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4631 int lineDoc = pdoc->LineFromPosition(mh.position);
4632 int lines = Platform::Maximum(0, mh.linesAdded);
4633 if (Wrapping()) {
4634 NeedWrapping(lineDoc, lineDoc + lines + 1);
4636 RefreshStyleData();
4637 // Fix up annotation heights
4638 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
4642 // Move a position so it is still after the same character as before the insertion.
4643 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
4644 if (position > startInsertion) {
4645 return position + length;
4647 return position;
4650 // Move a position so it is still after the same character as before the deletion if that
4651 // character is still present else after the previous surviving character.
4652 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
4653 if (position > startDeletion) {
4654 int endDeletion = startDeletion + length;
4655 if (position > endDeletion) {
4656 return position - length;
4657 } else {
4658 return startDeletion;
4660 } else {
4661 return position;
4665 void Editor::NotifyModified(Document *, DocModification mh, void *) {
4666 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
4667 if (paintState == painting) {
4668 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
4670 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
4671 if (paintState == painting) {
4672 CheckForChangeOutsidePaint(
4673 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
4674 } else {
4675 // Could check that change is before last visible line.
4676 Redraw();
4679 if (mh.modificationType & SC_MOD_LEXERSTATE) {
4680 if (paintState == painting) {
4681 CheckForChangeOutsidePaint(
4682 Range(mh.position, mh.position + mh.length));
4683 } else {
4684 Redraw();
4687 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
4688 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4689 pdoc->IncrementStyleClock();
4691 if (paintState == notPainting) {
4692 if (mh.position < pdoc->LineStart(topLine)) {
4693 // Styling performed before this view
4694 Redraw();
4695 } else {
4696 InvalidateRange(mh.position, mh.position + mh.length);
4699 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4700 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4702 } else {
4703 // Move selection and brace highlights
4704 if (mh.modificationType & SC_MOD_INSERTTEXT) {
4705 sel.MovePositions(true, mh.position, mh.length);
4706 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
4707 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
4708 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
4709 sel.MovePositions(false, mh.position, mh.length);
4710 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
4711 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
4713 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && cs.HiddenLines()) {
4714 // Some lines are hidden so may need shown.
4715 // TODO: check if the modified area is hidden.
4716 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
4717 int lineOfPos = pdoc->LineFromPosition(mh.position);
4718 bool insertingNewLine = false;
4719 for (int i=0; i < mh.length; i++) {
4720 if ((mh.text[i] == '\n') || (mh.text[i] == '\r'))
4721 insertingNewLine = true;
4723 if (insertingNewLine && (mh.position != pdoc->LineStart(lineOfPos)))
4724 NeedShown(mh.position, pdoc->LineStart(lineOfPos+1) - mh.position);
4725 else
4726 NeedShown(mh.position, 0);
4727 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
4728 NeedShown(mh.position, mh.length);
4731 if (mh.linesAdded != 0) {
4732 // Update contraction state for inserted and removed lines
4733 // lineOfPos should be calculated in context of state before modification, shouldn't it
4734 int lineOfPos = pdoc->LineFromPosition(mh.position);
4735 if (mh.position > pdoc->LineStart(lineOfPos))
4736 lineOfPos++; // Affecting subsequent lines
4737 if (mh.linesAdded > 0) {
4738 cs.InsertLines(lineOfPos, mh.linesAdded);
4739 } else {
4740 cs.DeleteLines(lineOfPos, -mh.linesAdded);
4743 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
4744 int lineDoc = pdoc->LineFromPosition(mh.position);
4745 if (vs.annotationVisible) {
4746 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
4747 Redraw();
4750 CheckModificationForWrap(mh);
4751 if (mh.linesAdded != 0) {
4752 // Avoid scrolling of display if change before current display
4753 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
4754 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
4755 if (newTop != topLine) {
4756 SetTopLine(newTop);
4757 SetVerticalScrollPos();
4761 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
4762 QueueIdleWork(WorkNeeded::workStyle, pdoc->Length());
4763 Redraw();
4765 } else {
4766 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
4767 QueueIdleWork(WorkNeeded::workStyle, mh.position + mh.length);
4768 InvalidateRange(mh.position, mh.position + mh.length);
4773 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
4774 SetScrollBars();
4777 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
4778 if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) {
4779 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
4780 // Fold changes can affect the drawing of following lines so redraw whole margin
4781 RedrawSelMargin(highlightDelimiter.isEnabled ? -1 : mh.line-1, true);
4782 } else {
4783 RedrawSelMargin(mh.line);
4787 if ((mh.modificationType & SC_MOD_CHANGEFOLD) && (foldAutomatic & SC_AUTOMATICFOLD_CHANGE)) {
4788 FoldChanged(mh.line, mh.foldLevelNow, mh.foldLevelPrev);
4791 // NOW pay the piper WRT "deferred" visual updates
4792 if (IsLastStep(mh)) {
4793 SetScrollBars();
4794 Redraw();
4797 // If client wants to see this modification
4798 if (mh.modificationType & modEventMask) {
4799 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
4800 // Real modification made to text of document.
4801 NotifyChange(); // Send EN_CHANGE
4804 SCNotification scn = {};
4805 scn.nmhdr.code = SCN_MODIFIED;
4806 scn.position = mh.position;
4807 scn.modificationType = mh.modificationType;
4808 scn.text = mh.text;
4809 scn.length = mh.length;
4810 scn.linesAdded = mh.linesAdded;
4811 scn.line = mh.line;
4812 scn.foldLevelNow = mh.foldLevelNow;
4813 scn.foldLevelPrev = mh.foldLevelPrev;
4814 scn.token = mh.token;
4815 scn.annotationLinesAdded = mh.annotationLinesAdded;
4816 NotifyParent(scn);
4820 void Editor::NotifyDeleted(Document *, void *) {
4821 /* Do nothing */
4824 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
4826 // Enumerates all macroable messages
4827 switch (iMessage) {
4828 case SCI_CUT:
4829 case SCI_COPY:
4830 case SCI_PASTE:
4831 case SCI_CLEAR:
4832 case SCI_REPLACESEL:
4833 case SCI_ADDTEXT:
4834 case SCI_INSERTTEXT:
4835 case SCI_APPENDTEXT:
4836 case SCI_CLEARALL:
4837 case SCI_SELECTALL:
4838 case SCI_GOTOLINE:
4839 case SCI_GOTOPOS:
4840 case SCI_SEARCHANCHOR:
4841 case SCI_SEARCHNEXT:
4842 case SCI_SEARCHPREV:
4843 case SCI_LINEDOWN:
4844 case SCI_LINEDOWNEXTEND:
4845 case SCI_PARADOWN:
4846 case SCI_PARADOWNEXTEND:
4847 case SCI_LINEUP:
4848 case SCI_LINEUPEXTEND:
4849 case SCI_PARAUP:
4850 case SCI_PARAUPEXTEND:
4851 case SCI_CHARLEFT:
4852 case SCI_CHARLEFTEXTEND:
4853 case SCI_CHARRIGHT:
4854 case SCI_CHARRIGHTEXTEND:
4855 case SCI_WORDLEFT:
4856 case SCI_WORDLEFTEXTEND:
4857 case SCI_WORDRIGHT:
4858 case SCI_WORDRIGHTEXTEND:
4859 case SCI_WORDPARTLEFT:
4860 case SCI_WORDPARTLEFTEXTEND:
4861 case SCI_WORDPARTRIGHT:
4862 case SCI_WORDPARTRIGHTEXTEND:
4863 case SCI_WORDLEFTEND:
4864 case SCI_WORDLEFTENDEXTEND:
4865 case SCI_WORDRIGHTEND:
4866 case SCI_WORDRIGHTENDEXTEND:
4867 case SCI_HOME:
4868 case SCI_HOMEEXTEND:
4869 case SCI_LINEEND:
4870 case SCI_LINEENDEXTEND:
4871 case SCI_HOMEWRAP:
4872 case SCI_HOMEWRAPEXTEND:
4873 case SCI_LINEENDWRAP:
4874 case SCI_LINEENDWRAPEXTEND:
4875 case SCI_DOCUMENTSTART:
4876 case SCI_DOCUMENTSTARTEXTEND:
4877 case SCI_DOCUMENTEND:
4878 case SCI_DOCUMENTENDEXTEND:
4879 case SCI_STUTTEREDPAGEUP:
4880 case SCI_STUTTEREDPAGEUPEXTEND:
4881 case SCI_STUTTEREDPAGEDOWN:
4882 case SCI_STUTTEREDPAGEDOWNEXTEND:
4883 case SCI_PAGEUP:
4884 case SCI_PAGEUPEXTEND:
4885 case SCI_PAGEDOWN:
4886 case SCI_PAGEDOWNEXTEND:
4887 case SCI_EDITTOGGLEOVERTYPE:
4888 case SCI_CANCEL:
4889 case SCI_DELETEBACK:
4890 case SCI_TAB:
4891 case SCI_BACKTAB:
4892 case SCI_FORMFEED:
4893 case SCI_VCHOME:
4894 case SCI_VCHOMEEXTEND:
4895 case SCI_VCHOMEWRAP:
4896 case SCI_VCHOMEWRAPEXTEND:
4897 case SCI_VCHOMEDISPLAY:
4898 case SCI_VCHOMEDISPLAYEXTEND:
4899 case SCI_DELWORDLEFT:
4900 case SCI_DELWORDRIGHT:
4901 case SCI_DELWORDRIGHTEND:
4902 case SCI_DELLINELEFT:
4903 case SCI_DELLINERIGHT:
4904 case SCI_LINECOPY:
4905 case SCI_LINECUT:
4906 case SCI_LINEDELETE:
4907 case SCI_LINETRANSPOSE:
4908 case SCI_LINEDUPLICATE:
4909 case SCI_LOWERCASE:
4910 case SCI_UPPERCASE:
4911 case SCI_LINESCROLLDOWN:
4912 case SCI_LINESCROLLUP:
4913 case SCI_DELETEBACKNOTLINE:
4914 case SCI_HOMEDISPLAY:
4915 case SCI_HOMEDISPLAYEXTEND:
4916 case SCI_LINEENDDISPLAY:
4917 case SCI_LINEENDDISPLAYEXTEND:
4918 case SCI_SETSELECTIONMODE:
4919 case SCI_LINEDOWNRECTEXTEND:
4920 case SCI_LINEUPRECTEXTEND:
4921 case SCI_CHARLEFTRECTEXTEND:
4922 case SCI_CHARRIGHTRECTEXTEND:
4923 case SCI_HOMERECTEXTEND:
4924 case SCI_VCHOMERECTEXTEND:
4925 case SCI_LINEENDRECTEXTEND:
4926 case SCI_PAGEUPRECTEXTEND:
4927 case SCI_PAGEDOWNRECTEXTEND:
4928 case SCI_SELECTIONDUPLICATE:
4929 case SCI_COPYALLOWLINE:
4930 case SCI_VERTICALCENTRECARET:
4931 case SCI_MOVESELECTEDLINESUP:
4932 case SCI_MOVESELECTEDLINESDOWN:
4933 case SCI_SCROLLTOSTART:
4934 case SCI_SCROLLTOEND:
4935 break;
4937 // Filter out all others like display changes. Also, newlines are redundant
4938 // with char insert messages.
4939 case SCI_NEWLINE:
4940 default:
4941 // printf("Filtered out %ld of macro recording\n", iMessage);
4942 return;
4945 // Send notification
4946 SCNotification scn = {};
4947 scn.nmhdr.code = SCN_MACRORECORD;
4948 scn.message = iMessage;
4949 scn.wParam = wParam;
4950 scn.lParam = lParam;
4951 NotifyParent(scn);
4954 // Something has changed that the container should know about
4955 void Editor::ContainerNeedsUpdate(int flags) {
4956 needUpdateUI |= flags;
4960 * Force scroll and keep position relative to top of window.
4962 * If stuttered = true and not already at first/last row, move to first/last row of window.
4963 * If stuttered = true and already at first/last row, scroll as normal.
4965 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
4966 int topLineNew;
4967 SelectionPosition newPos;
4969 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
4970 int topStutterLine = topLine + caretYSlop;
4971 int bottomStutterLine =
4972 pdoc->LineFromPosition(PositionFromLocation(
4973 Point(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
4974 - caretYSlop - 1;
4976 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
4977 topLineNew = topLine;
4978 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
4979 false, false, UserVirtualSpace());
4981 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
4982 topLineNew = topLine;
4983 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
4984 false, false, UserVirtualSpace());
4986 } else {
4987 Point pt = LocationFromPosition(sel.MainCaret());
4989 topLineNew = Platform::Clamp(
4990 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
4991 newPos = SPositionFromLocation(
4992 Point(lastXChosen - xOffset, pt.y + direction * (vs.lineHeight * LinesToScroll())),
4993 false, false, UserVirtualSpace());
4996 if (topLineNew != topLine) {
4997 SetTopLine(topLineNew);
4998 MovePositionTo(newPos, selt);
4999 Redraw();
5000 SetVerticalScrollPos();
5001 } else {
5002 MovePositionTo(newPos, selt);
5006 void Editor::ChangeCaseOfSelection(int caseMapping) {
5007 UndoGroup ug(pdoc);
5008 for (size_t r=0; r<sel.Count(); r++) {
5009 SelectionRange current = sel.Range(r);
5010 SelectionRange currentNoVS = current;
5011 currentNoVS.ClearVirtualSpace();
5012 size_t rangeBytes = currentNoVS.Length();
5013 if (rangeBytes > 0) {
5014 std::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position());
5016 std::string sMapped = CaseMapString(sText, caseMapping);
5018 if (sMapped != sText) {
5019 size_t firstDifference = 0;
5020 while (sMapped[firstDifference] == sText[firstDifference])
5021 firstDifference++;
5022 size_t lastDifferenceText = sText.size() - 1;
5023 size_t lastDifferenceMapped = sMapped.size() - 1;
5024 while (sMapped[lastDifferenceMapped] == sText[lastDifferenceText]) {
5025 lastDifferenceText--;
5026 lastDifferenceMapped--;
5028 size_t endDifferenceText = sText.size() - 1 - lastDifferenceText;
5029 pdoc->DeleteChars(
5030 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
5031 static_cast<int>(rangeBytes - firstDifference - endDifferenceText));
5032 pdoc->InsertString(
5033 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
5034 sMapped.c_str() + firstDifference,
5035 static_cast<int>(lastDifferenceMapped - firstDifference + 1));
5036 // Automatic movement changes selection so reset to exactly the same as it was.
5037 int diffSizes = static_cast<int>(sMapped.size() - sText.size());
5038 if (diffSizes != 0) {
5039 if (current.anchor > current.caret)
5040 current.anchor.Add(diffSizes);
5041 else
5042 current.caret.Add(diffSizes);
5044 sel.Range(r) = current;
5050 void Editor::LineTranspose() {
5051 int line = pdoc->LineFromPosition(sel.MainCaret());
5052 if (line > 0) {
5053 UndoGroup ug(pdoc);
5054 int startPrev = pdoc->LineStart(line - 1);
5055 int endPrev = pdoc->LineEnd(line - 1);
5056 int start = pdoc->LineStart(line);
5057 int end = pdoc->LineEnd(line);
5058 std::string line1 = RangeText(startPrev, endPrev);
5059 int len1 = endPrev - startPrev;
5060 std::string line2 = RangeText(start, end);
5061 int len2 = end - start;
5062 pdoc->DeleteChars(start, len2);
5063 pdoc->DeleteChars(startPrev, len1);
5064 pdoc->InsertString(startPrev, line2.c_str(), len2);
5065 pdoc->InsertString(start - len1 + len2, line1.c_str(), len1);
5066 MovePositionTo(SelectionPosition(start - len1 + len2));
5070 void Editor::Duplicate(bool forLine) {
5071 if (sel.Empty()) {
5072 forLine = true;
5074 UndoGroup ug(pdoc);
5075 const char *eol = "";
5076 int eolLen = 0;
5077 if (forLine) {
5078 eol = StringFromEOLMode(pdoc->eolMode);
5079 eolLen = istrlen(eol);
5081 for (size_t r=0; r<sel.Count(); r++) {
5082 SelectionPosition start = sel.Range(r).Start();
5083 SelectionPosition end = sel.Range(r).End();
5084 if (forLine) {
5085 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
5086 start = SelectionPosition(pdoc->LineStart(line));
5087 end = SelectionPosition(pdoc->LineEnd(line));
5089 std::string text = RangeText(start.Position(), end.Position());
5090 if (forLine)
5091 pdoc->InsertString(end.Position(), eol, eolLen);
5092 pdoc->InsertString(end.Position() + eolLen, text.c_str(), SelectionRange(end, start).Length());
5094 if (sel.Count() && sel.IsRectangular()) {
5095 SelectionPosition last = sel.Last();
5096 if (forLine) {
5097 int line = pdoc->LineFromPosition(last.Position());
5098 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
5100 if (sel.Rectangular().anchor > sel.Rectangular().caret)
5101 sel.Rectangular().anchor = last;
5102 else
5103 sel.Rectangular().caret = last;
5104 SetRectangularRange();
5108 void Editor::CancelModes() {
5109 sel.SetMoveExtends(false);
5112 void Editor::NewLine() {
5113 // Remove non-main ranges
5114 InvalidateSelection(sel.RangeMain(), true);
5115 sel.SetSelection(sel.RangeMain());
5116 sel.RangeMain().ClearVirtualSpace();
5118 // Clear main range and insert line end
5119 bool needGroupUndo = !sel.Empty();
5120 if (needGroupUndo)
5121 pdoc->BeginUndoAction();
5123 if (!sel.Empty())
5124 ClearSelection();
5125 const char *eol = "\n";
5126 if (pdoc->eolMode == SC_EOL_CRLF) {
5127 eol = "\r\n";
5128 } else if (pdoc->eolMode == SC_EOL_CR) {
5129 eol = "\r";
5130 } // else SC_EOL_LF -> "\n" already set
5131 bool inserted = pdoc->InsertCString(sel.MainCaret(), eol);
5132 // Want to end undo group before NotifyChar as applications often modify text here
5133 if (needGroupUndo)
5134 pdoc->EndUndoAction();
5135 if (inserted) {
5136 SetEmptySelection(sel.MainCaret() + istrlen(eol));
5137 while (*eol) {
5138 NotifyChar(*eol);
5139 if (recordingMacro) {
5140 char txt[2];
5141 txt[0] = *eol;
5142 txt[1] = '\0';
5143 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
5145 eol++;
5148 SetLastXChosen();
5149 SetScrollBars();
5150 EnsureCaretVisible();
5151 // Avoid blinking during rapid typing:
5152 ShowCaretAtCurrentPosition();
5155 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
5156 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
5157 if (sel.IsRectangular()) {
5158 if (selt == Selection::noSel) {
5159 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
5160 } else {
5161 caretToUse = sel.Rectangular().caret;
5165 Point pt = LocationFromPosition(caretToUse);
5166 int skipLines = 0;
5168 if (vs.annotationVisible) {
5169 int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
5170 Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
5171 int subLine = (pt.y - ptStartLine.y) / vs.lineHeight;
5173 if (direction < 0 && subLine == 0) {
5174 int lineDisplay = cs.DisplayFromDoc(lineDoc);
5175 if (lineDisplay > 0) {
5176 skipLines = pdoc->AnnotationLines(cs.DocFromDisplay(lineDisplay - 1));
5178 } else if (direction > 0 && subLine >= (cs.GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
5179 skipLines = pdoc->AnnotationLines(lineDoc);
5183 int newY = pt.y + (1 + skipLines) * direction * vs.lineHeight;
5184 SelectionPosition posNew = SPositionFromLocation(
5185 Point(lastXChosen - xOffset, newY), false, false, UserVirtualSpace());
5187 if (direction < 0) {
5188 // Line wrapping may lead to a location on the same line, so
5189 // seek back if that is the case.
5190 Point ptNew = LocationFromPosition(posNew.Position());
5191 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
5192 posNew.Add(-1);
5193 posNew.SetVirtualSpace(0);
5194 ptNew = LocationFromPosition(posNew.Position());
5196 } else if (direction > 0 && posNew.Position() != pdoc->Length()) {
5197 // There is an equivalent case when moving down which skips
5198 // over a line.
5199 Point ptNew = LocationFromPosition(posNew.Position());
5200 while ((posNew.Position() > caretToUse.Position()) && (ptNew.y > newY)) {
5201 posNew.Add(-1);
5202 posNew.SetVirtualSpace(0);
5203 ptNew = LocationFromPosition(posNew.Position());
5207 MovePositionTo(MovePositionSoVisible(posNew, direction), selt);
5210 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
5211 int lineDoc, savedPos = sel.MainCaret();
5212 do {
5213 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
5214 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
5215 if (direction > 0) {
5216 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
5217 if (selt == Selection::noSel) {
5218 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
5220 break;
5223 } while (!cs.GetVisible(lineDoc));
5226 int Editor::StartEndDisplayLine(int pos, bool start) {
5227 RefreshStyleData();
5228 int line = pdoc->LineFromPosition(pos);
5229 AutoSurface surface(this);
5230 AutoLineLayout ll(llc, RetrieveLineLayout(line));
5231 int posRet = INVALID_POSITION;
5232 if (surface && ll) {
5233 unsigned int posLineStart = pdoc->LineStart(line);
5234 LayoutLine(line, surface, vs, ll, wrapWidth);
5235 int posInLine = pos - posLineStart;
5236 if (posInLine <= ll->maxLineLength) {
5237 for (int subLine = 0; subLine < ll->lines; subLine++) {
5238 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
5239 if (start) {
5240 posRet = ll->LineStart(subLine) + posLineStart;
5241 } else {
5242 if (subLine == ll->lines - 1)
5243 posRet = ll->LineStart(subLine + 1) + posLineStart;
5244 else
5245 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
5251 if (posRet == INVALID_POSITION) {
5252 return pos;
5253 } else {
5254 return posRet;
5258 int Editor::KeyCommand(unsigned int iMessage) {
5259 switch (iMessage) {
5260 case SCI_LINEDOWN:
5261 CursorUpOrDown(1);
5262 break;
5263 case SCI_LINEDOWNEXTEND:
5264 CursorUpOrDown(1, Selection::selStream);
5265 break;
5266 case SCI_LINEDOWNRECTEXTEND:
5267 CursorUpOrDown(1, Selection::selRectangle);
5268 break;
5269 case SCI_PARADOWN:
5270 ParaUpOrDown(1);
5271 break;
5272 case SCI_PARADOWNEXTEND:
5273 ParaUpOrDown(1, Selection::selStream);
5274 break;
5275 case SCI_LINESCROLLDOWN:
5276 ScrollTo(topLine + 1);
5277 MoveCaretInsideView(false);
5278 break;
5279 case SCI_LINEUP:
5280 CursorUpOrDown(-1);
5281 break;
5282 case SCI_LINEUPEXTEND:
5283 CursorUpOrDown(-1, Selection::selStream);
5284 break;
5285 case SCI_LINEUPRECTEXTEND:
5286 CursorUpOrDown(-1, Selection::selRectangle);
5287 break;
5288 case SCI_PARAUP:
5289 ParaUpOrDown(-1);
5290 break;
5291 case SCI_PARAUPEXTEND:
5292 ParaUpOrDown(-1, Selection::selStream);
5293 break;
5294 case SCI_LINESCROLLUP:
5295 ScrollTo(topLine - 1);
5296 MoveCaretInsideView(false);
5297 break;
5298 case SCI_CHARLEFT:
5299 if (SelectionEmpty() || sel.MoveExtends()) {
5300 if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5301 SelectionPosition spCaret = sel.RangeMain().caret;
5302 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5303 MovePositionTo(spCaret);
5304 } else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
5305 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1));
5306 } else {
5307 MovePositionTo(MovePositionSoVisible(
5308 SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
5310 } else {
5311 MovePositionTo(sel.LimitsForRectangularElseMain().start);
5313 SetLastXChosen();
5314 break;
5315 case SCI_CHARLEFTEXTEND:
5316 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5317 SelectionPosition spCaret = sel.RangeMain().caret;
5318 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5319 MovePositionTo(spCaret, Selection::selStream);
5320 } else {
5321 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
5323 SetLastXChosen();
5324 break;
5325 case SCI_CHARLEFTRECTEXTEND:
5326 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5327 SelectionPosition spCaret = sel.RangeMain().caret;
5328 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5329 MovePositionTo(spCaret, Selection::selRectangle);
5330 } else {
5331 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
5333 SetLastXChosen();
5334 break;
5335 case SCI_CHARRIGHT:
5336 if (SelectionEmpty() || sel.MoveExtends()) {
5337 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5338 SelectionPosition spCaret = sel.RangeMain().caret;
5339 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5340 MovePositionTo(spCaret);
5341 } else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
5342 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1));
5343 } else {
5344 MovePositionTo(MovePositionSoVisible(
5345 SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
5347 } else {
5348 MovePositionTo(sel.LimitsForRectangularElseMain().end);
5350 SetLastXChosen();
5351 break;
5352 case SCI_CHARRIGHTEXTEND:
5353 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5354 SelectionPosition spCaret = sel.RangeMain().caret;
5355 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5356 MovePositionTo(spCaret, Selection::selStream);
5357 } else {
5358 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
5360 SetLastXChosen();
5361 break;
5362 case SCI_CHARRIGHTRECTEXTEND:
5363 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5364 SelectionPosition spCaret = sel.RangeMain().caret;
5365 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5366 MovePositionTo(spCaret, Selection::selRectangle);
5367 } else {
5368 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
5370 SetLastXChosen();
5371 break;
5372 case SCI_WORDLEFT:
5373 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
5374 SetLastXChosen();
5375 break;
5376 case SCI_WORDLEFTEXTEND:
5377 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
5378 SetLastXChosen();
5379 break;
5380 case SCI_WORDRIGHT:
5381 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
5382 SetLastXChosen();
5383 break;
5384 case SCI_WORDRIGHTEXTEND:
5385 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
5386 SetLastXChosen();
5387 break;
5389 case SCI_WORDLEFTEND:
5390 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
5391 SetLastXChosen();
5392 break;
5393 case SCI_WORDLEFTENDEXTEND:
5394 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
5395 SetLastXChosen();
5396 break;
5397 case SCI_WORDRIGHTEND:
5398 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
5399 SetLastXChosen();
5400 break;
5401 case SCI_WORDRIGHTENDEXTEND:
5402 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
5403 SetLastXChosen();
5404 break;
5406 case SCI_HOME:
5407 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5408 SetLastXChosen();
5409 break;
5410 case SCI_HOMEEXTEND:
5411 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
5412 SetLastXChosen();
5413 break;
5414 case SCI_HOMERECTEXTEND:
5415 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
5416 SetLastXChosen();
5417 break;
5418 case SCI_LINEEND:
5419 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
5420 SetLastXChosen();
5421 break;
5422 case SCI_LINEENDEXTEND:
5423 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
5424 SetLastXChosen();
5425 break;
5426 case SCI_LINEENDRECTEXTEND:
5427 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
5428 SetLastXChosen();
5429 break;
5430 case SCI_HOMEWRAP: {
5431 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5432 if (sel.RangeMain().caret <= homePos)
5433 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5434 MovePositionTo(homePos);
5435 SetLastXChosen();
5437 break;
5438 case SCI_HOMEWRAPEXTEND: {
5439 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5440 if (sel.RangeMain().caret <= homePos)
5441 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5442 MovePositionTo(homePos, Selection::selStream);
5443 SetLastXChosen();
5445 break;
5446 case SCI_LINEENDWRAP: {
5447 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5448 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5449 if (endPos > realEndPos // if moved past visible EOLs
5450 || sel.RangeMain().caret >= endPos) // if at end of display line already
5451 endPos = realEndPos;
5452 MovePositionTo(endPos);
5453 SetLastXChosen();
5455 break;
5456 case SCI_LINEENDWRAPEXTEND: {
5457 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5458 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5459 if (endPos > realEndPos // if moved past visible EOLs
5460 || sel.RangeMain().caret >= endPos) // if at end of display line already
5461 endPos = realEndPos;
5462 MovePositionTo(endPos, Selection::selStream);
5463 SetLastXChosen();
5465 break;
5466 case SCI_DOCUMENTSTART:
5467 MovePositionTo(0);
5468 SetLastXChosen();
5469 break;
5470 case SCI_DOCUMENTSTARTEXTEND:
5471 MovePositionTo(0, Selection::selStream);
5472 SetLastXChosen();
5473 break;
5474 case SCI_DOCUMENTEND:
5475 MovePositionTo(pdoc->Length());
5476 SetLastXChosen();
5477 break;
5478 case SCI_DOCUMENTENDEXTEND:
5479 MovePositionTo(pdoc->Length(), Selection::selStream);
5480 SetLastXChosen();
5481 break;
5482 case SCI_STUTTEREDPAGEUP:
5483 PageMove(-1, Selection::noSel, true);
5484 break;
5485 case SCI_STUTTEREDPAGEUPEXTEND:
5486 PageMove(-1, Selection::selStream, true);
5487 break;
5488 case SCI_STUTTEREDPAGEDOWN:
5489 PageMove(1, Selection::noSel, true);
5490 break;
5491 case SCI_STUTTEREDPAGEDOWNEXTEND:
5492 PageMove(1, Selection::selStream, true);
5493 break;
5494 case SCI_PAGEUP:
5495 PageMove(-1);
5496 break;
5497 case SCI_PAGEUPEXTEND:
5498 PageMove(-1, Selection::selStream);
5499 break;
5500 case SCI_PAGEUPRECTEXTEND:
5501 PageMove(-1, Selection::selRectangle);
5502 break;
5503 case SCI_PAGEDOWN:
5504 PageMove(1);
5505 break;
5506 case SCI_PAGEDOWNEXTEND:
5507 PageMove(1, Selection::selStream);
5508 break;
5509 case SCI_PAGEDOWNRECTEXTEND:
5510 PageMove(1, Selection::selRectangle);
5511 break;
5512 case SCI_EDITTOGGLEOVERTYPE:
5513 inOverstrike = !inOverstrike;
5514 DropCaret();
5515 ShowCaretAtCurrentPosition();
5516 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
5517 NotifyUpdateUI();
5518 break;
5519 case SCI_CANCEL: // Cancel any modes - handled in subclass
5520 // Also unselect text
5521 CancelModes();
5522 break;
5523 case SCI_DELETEBACK:
5524 DelCharBack(true);
5525 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5526 SetLastXChosen();
5528 EnsureCaretVisible();
5529 break;
5530 case SCI_DELETEBACKNOTLINE:
5531 DelCharBack(false);
5532 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5533 SetLastXChosen();
5535 EnsureCaretVisible();
5536 break;
5537 case SCI_TAB:
5538 Indent(true);
5539 if (caretSticky == SC_CARETSTICKY_OFF) {
5540 SetLastXChosen();
5542 EnsureCaretVisible();
5543 ShowCaretAtCurrentPosition(); // Avoid blinking
5544 break;
5545 case SCI_BACKTAB:
5546 Indent(false);
5547 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5548 SetLastXChosen();
5550 EnsureCaretVisible();
5551 ShowCaretAtCurrentPosition(); // Avoid blinking
5552 break;
5553 case SCI_NEWLINE:
5554 NewLine();
5555 break;
5556 case SCI_FORMFEED:
5557 AddChar('\f');
5558 break;
5559 case SCI_VCHOME:
5560 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
5561 SetLastXChosen();
5562 break;
5563 case SCI_VCHOMEEXTEND:
5564 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
5565 SetLastXChosen();
5566 break;
5567 case SCI_VCHOMERECTEXTEND:
5568 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
5569 SetLastXChosen();
5570 break;
5571 case SCI_VCHOMEWRAP: {
5572 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5573 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5574 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5575 homePos = viewLineStart;
5577 MovePositionTo(homePos);
5578 SetLastXChosen();
5580 break;
5581 case SCI_VCHOMEWRAPEXTEND: {
5582 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5583 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5584 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5585 homePos = viewLineStart;
5587 MovePositionTo(homePos, Selection::selStream);
5588 SetLastXChosen();
5590 break;
5591 case SCI_ZOOMIN:
5592 if (vs.zoomLevel < 20) {
5593 vs.zoomLevel++;
5594 InvalidateStyleRedraw();
5595 NotifyZoom();
5597 break;
5598 case SCI_ZOOMOUT:
5599 if (vs.zoomLevel > -10) {
5600 vs.zoomLevel--;
5601 InvalidateStyleRedraw();
5602 NotifyZoom();
5604 break;
5605 case SCI_DELWORDLEFT: {
5606 int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
5607 pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
5608 sel.RangeMain().ClearVirtualSpace();
5609 SetLastXChosen();
5611 break;
5612 case SCI_DELWORDRIGHT: {
5613 UndoGroup ug(pdoc);
5614 sel.RangeMain().caret = SelectionPosition(
5615 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5616 sel.RangeMain().anchor = sel.RangeMain().caret;
5617 int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
5618 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5620 break;
5621 case SCI_DELWORDRIGHTEND: {
5622 UndoGroup ug(pdoc);
5623 sel.RangeMain().caret = SelectionPosition(
5624 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5625 int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
5626 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5628 break;
5629 case SCI_DELLINELEFT: {
5630 int line = pdoc->LineFromPosition(sel.MainCaret());
5631 int start = pdoc->LineStart(line);
5632 pdoc->DeleteChars(start, sel.MainCaret() - start);
5633 sel.RangeMain().ClearVirtualSpace();
5634 SetLastXChosen();
5636 break;
5637 case SCI_DELLINERIGHT: {
5638 int line = pdoc->LineFromPosition(sel.MainCaret());
5639 int end = pdoc->LineEnd(line);
5640 pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
5642 break;
5643 case SCI_LINECOPY: {
5644 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5645 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5646 CopyRangeToClipboard(pdoc->LineStart(lineStart),
5647 pdoc->LineStart(lineEnd + 1));
5649 break;
5650 case SCI_LINECUT: {
5651 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5652 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5653 int start = pdoc->LineStart(lineStart);
5654 int end = pdoc->LineStart(lineEnd + 1);
5655 SetSelection(start, end);
5656 Cut();
5657 SetLastXChosen();
5659 break;
5660 case SCI_LINEDELETE: {
5661 int line = pdoc->LineFromPosition(sel.MainCaret());
5662 int start = pdoc->LineStart(line);
5663 int end = pdoc->LineStart(line + 1);
5664 pdoc->DeleteChars(start, end - start);
5666 break;
5667 case SCI_LINETRANSPOSE:
5668 LineTranspose();
5669 break;
5670 case SCI_LINEDUPLICATE:
5671 Duplicate(true);
5672 break;
5673 case SCI_SELECTIONDUPLICATE:
5674 Duplicate(false);
5675 break;
5676 case SCI_LOWERCASE:
5677 ChangeCaseOfSelection(cmLower);
5678 break;
5679 case SCI_UPPERCASE:
5680 ChangeCaseOfSelection(cmUpper);
5681 break;
5682 case SCI_WORDPARTLEFT:
5683 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
5684 SetLastXChosen();
5685 break;
5686 case SCI_WORDPARTLEFTEXTEND:
5687 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
5688 SetLastXChosen();
5689 break;
5690 case SCI_WORDPARTRIGHT:
5691 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
5692 SetLastXChosen();
5693 break;
5694 case SCI_WORDPARTRIGHTEXTEND:
5695 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
5696 SetLastXChosen();
5697 break;
5698 case SCI_HOMEDISPLAY:
5699 MovePositionTo(MovePositionSoVisible(
5700 StartEndDisplayLine(sel.MainCaret(), true), -1));
5701 SetLastXChosen();
5702 break;
5703 case SCI_VCHOMEDISPLAY: {
5704 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5705 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5706 if (viewLineStart > homePos)
5707 homePos = viewLineStart;
5709 MovePositionTo(homePos);
5710 SetLastXChosen();
5712 break;
5713 case SCI_HOMEDISPLAYEXTEND:
5714 MovePositionTo(MovePositionSoVisible(
5715 StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
5716 SetLastXChosen();
5717 break;
5718 case SCI_VCHOMEDISPLAYEXTEND: {
5719 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5720 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5721 if (viewLineStart > homePos)
5722 homePos = viewLineStart;
5724 MovePositionTo(homePos, Selection::selStream);
5725 SetLastXChosen();
5727 break;
5728 case SCI_LINEENDDISPLAY:
5729 MovePositionTo(MovePositionSoVisible(
5730 StartEndDisplayLine(sel.MainCaret(), false), 1));
5731 SetLastXChosen();
5732 break;
5733 case SCI_LINEENDDISPLAYEXTEND:
5734 MovePositionTo(MovePositionSoVisible(
5735 StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
5736 SetLastXChosen();
5737 break;
5738 case SCI_SCROLLTOSTART:
5739 ScrollTo(0);
5740 break;
5741 case SCI_SCROLLTOEND:
5742 ScrollTo(MaxScrollPos());
5743 break;
5745 return 0;
5748 int Editor::KeyDefault(int, int) {
5749 return 0;
5752 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
5753 DwellEnd(false);
5754 int msg = kmap.Find(key, modifiers);
5755 if (msg) {
5756 if (consumed)
5757 *consumed = true;
5758 return WndProc(msg, 0, 0);
5759 } else {
5760 if (consumed)
5761 *consumed = false;
5762 return KeyDefault(key, modifiers);
5766 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
5767 return KeyDownWithModifiers(key, ModifierFlags(shift, ctrl, alt), consumed);
5770 void Editor::Indent(bool forwards) {
5771 UndoGroup ug(pdoc);
5772 for (size_t r=0; r<sel.Count(); r++) {
5773 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
5774 int caretPosition = sel.Range(r).caret.Position();
5775 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
5776 if (lineOfAnchor == lineCurrentPos) {
5777 if (forwards) {
5778 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
5779 caretPosition = sel.Range(r).caret.Position();
5780 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
5781 pdoc->tabIndents) {
5782 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5783 int indentationStep = pdoc->IndentSize();
5784 pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
5785 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5786 } else {
5787 if (pdoc->useTabs) {
5788 pdoc->InsertChar(caretPosition, '\t');
5789 sel.Range(r) = SelectionRange(caretPosition+1);
5790 } else {
5791 int numSpaces = (pdoc->tabInChars) -
5792 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
5793 if (numSpaces < 1)
5794 numSpaces = pdoc->tabInChars;
5795 for (int i = 0; i < numSpaces; i++) {
5796 pdoc->InsertChar(caretPosition + i, ' ');
5798 sel.Range(r) = SelectionRange(caretPosition+numSpaces);
5801 } else {
5802 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
5803 pdoc->tabIndents) {
5804 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5805 int indentationStep = pdoc->IndentSize();
5806 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
5807 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5808 } else {
5809 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
5810 pdoc->tabInChars;
5811 if (newColumn < 0)
5812 newColumn = 0;
5813 int newPos = caretPosition;
5814 while (pdoc->GetColumn(newPos) > newColumn)
5815 newPos--;
5816 sel.Range(r) = SelectionRange(newPos);
5819 } else { // Multiline
5820 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
5821 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
5822 // Multiple lines selected so indent / dedent
5823 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
5824 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
5825 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
5826 lineBottomSel--; // If not selecting any characters on a line, do not indent
5827 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
5828 if (lineOfAnchor < lineCurrentPos) {
5829 if (currentPosPosOnLine == 0)
5830 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5831 else
5832 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
5833 } else {
5834 if (anchorPosOnLine == 0)
5835 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5836 else
5837 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
5841 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
5844 class CaseFolderASCII : public CaseFolderTable {
5845 public:
5846 CaseFolderASCII() {
5847 StandardASCII();
5849 ~CaseFolderASCII() {
5854 CaseFolder *Editor::CaseFolderForEncoding() {
5855 // Simple default that only maps ASCII upper case to lower case.
5856 return new CaseFolderASCII();
5860 * Search of a text in the document, in the given range.
5861 * @return The position of the found text, -1 if not found.
5863 long Editor::FindText(
5864 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5865 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5866 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
5868 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
5869 int lengthFound = istrlen(ft->lpstrText);
5870 if (!pdoc->HasCaseFolder())
5871 pdoc->SetCaseFolder(CaseFolderForEncoding());
5872 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
5873 (wParam & SCFIND_MATCHCASE) != 0,
5874 (wParam & SCFIND_WHOLEWORD) != 0,
5875 (wParam & SCFIND_WORDSTART) != 0,
5876 (wParam & SCFIND_REGEXP) != 0,
5877 wParam,
5878 &lengthFound);
5879 if (pos != -1) {
5880 ft->chrgText.cpMin = pos;
5881 ft->chrgText.cpMax = pos + lengthFound;
5883 return pos;
5887 * Relocatable search support : Searches relative to current selection
5888 * point and sets the selection to the found text range with
5889 * each search.
5892 * Anchor following searches at current selection start: This allows
5893 * multiple incremental interactive searches to be macro recorded
5894 * while still setting the selection to found text so the find/select
5895 * operation is self-contained.
5897 void Editor::SearchAnchor() {
5898 searchAnchor = SelectionStart().Position();
5902 * Find text from current search anchor: Must call @c SearchAnchor first.
5903 * Used for next text and previous text requests.
5904 * @return The position of the found text, -1 if not found.
5906 long Editor::SearchText(
5907 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
5908 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5909 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5910 sptr_t lParam) { ///< The text to search for.
5912 const char *txt = reinterpret_cast<char *>(lParam);
5913 int pos;
5914 int lengthFound = istrlen(txt);
5915 if (!pdoc->HasCaseFolder())
5916 pdoc->SetCaseFolder(CaseFolderForEncoding());
5917 if (iMessage == SCI_SEARCHNEXT) {
5918 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
5919 (wParam & SCFIND_MATCHCASE) != 0,
5920 (wParam & SCFIND_WHOLEWORD) != 0,
5921 (wParam & SCFIND_WORDSTART) != 0,
5922 (wParam & SCFIND_REGEXP) != 0,
5923 wParam,
5924 &lengthFound);
5925 } else {
5926 pos = pdoc->FindText(searchAnchor, 0, txt,
5927 (wParam & SCFIND_MATCHCASE) != 0,
5928 (wParam & SCFIND_WHOLEWORD) != 0,
5929 (wParam & SCFIND_WORDSTART) != 0,
5930 (wParam & SCFIND_REGEXP) != 0,
5931 wParam,
5932 &lengthFound);
5934 if (pos != -1) {
5935 SetSelection(pos, pos + lengthFound);
5938 return pos;
5941 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
5942 std::string ret(s);
5943 for (size_t i=0; i<ret.size(); i++) {
5944 switch (caseMapping) {
5945 case cmUpper:
5946 if (ret[i] >= 'a' && ret[i] <= 'z')
5947 ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
5948 break;
5949 case cmLower:
5950 if (ret[i] >= 'A' && ret[i] <= 'Z')
5951 ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
5952 break;
5955 return ret;
5959 * Search for text in the target range of the document.
5960 * @return The position of the found text, -1 if not found.
5962 long Editor::SearchInTarget(const char *text, int length) {
5963 int lengthFound = length;
5965 if (!pdoc->HasCaseFolder())
5966 pdoc->SetCaseFolder(CaseFolderForEncoding());
5967 int pos = pdoc->FindText(targetStart, targetEnd, text,
5968 (searchFlags & SCFIND_MATCHCASE) != 0,
5969 (searchFlags & SCFIND_WHOLEWORD) != 0,
5970 (searchFlags & SCFIND_WORDSTART) != 0,
5971 (searchFlags & SCFIND_REGEXP) != 0,
5972 searchFlags,
5973 &lengthFound);
5974 if (pos != -1) {
5975 targetStart = pos;
5976 targetEnd = pos + lengthFound;
5978 return pos;
5981 void Editor::GoToLine(int lineNo) {
5982 if (lineNo > pdoc->LinesTotal())
5983 lineNo = pdoc->LinesTotal();
5984 if (lineNo < 0)
5985 lineNo = 0;
5986 SetEmptySelection(pdoc->LineStart(lineNo));
5987 ShowCaretAtCurrentPosition();
5988 EnsureCaretVisible();
5991 static bool Close(Point pt1, Point pt2) {
5992 if (abs(pt1.x - pt2.x) > 3)
5993 return false;
5994 if (abs(pt1.y - pt2.y) > 3)
5995 return false;
5996 return true;
5999 std::string Editor::RangeText(int start, int end) const {
6000 if (start < end) {
6001 int len = end - start;
6002 std::string ret(len, '\0');
6003 for (int i = 0; i < len; i++) {
6004 ret[i] = pdoc->CharAt(start + i);
6006 return ret;
6008 return std::string();
6011 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
6012 if (sel.Empty()) {
6013 if (allowLineCopy) {
6014 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
6015 int start = pdoc->LineStart(currentLine);
6016 int end = pdoc->LineEnd(currentLine);
6018 std::string text = RangeText(start, end);
6019 if (pdoc->eolMode != SC_EOL_LF)
6020 text.push_back('\r');
6021 if (pdoc->eolMode != SC_EOL_CR)
6022 text.push_back('\n');
6023 ss->Copy(text, pdoc->dbcsCodePage,
6024 vs.styles[STYLE_DEFAULT].characterSet, false, true);
6026 } else {
6027 std::string text;
6028 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
6029 if (sel.selType == Selection::selRectangle)
6030 std::sort(rangesInOrder.begin(), rangesInOrder.end());
6031 for (size_t r=0; r<rangesInOrder.size(); r++) {
6032 SelectionRange current = rangesInOrder[r];
6033 text.append(RangeText(current.Start().Position(), current.End().Position()));
6034 if (sel.selType == Selection::selRectangle) {
6035 if (pdoc->eolMode != SC_EOL_LF)
6036 text.push_back('\r');
6037 if (pdoc->eolMode != SC_EOL_CR)
6038 text.push_back('\n');
6041 ss->Copy(text, pdoc->dbcsCodePage,
6042 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
6046 void Editor::CopyRangeToClipboard(int start, int end) {
6047 start = pdoc->ClampPositionIntoDocument(start);
6048 end = pdoc->ClampPositionIntoDocument(end);
6049 SelectionText selectedText;
6050 std::string text = RangeText(start, end);
6051 selectedText.Copy(text,
6052 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
6053 CopyToClipboard(selectedText);
6056 void Editor::CopyText(int length, const char *text) {
6057 SelectionText selectedText;
6058 selectedText.Copy(std::string(text, length),
6059 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
6060 CopyToClipboard(selectedText);
6063 void Editor::SetDragPosition(SelectionPosition newPos) {
6064 if (newPos.Position() >= 0) {
6065 newPos = MovePositionOutsideChar(newPos, 1);
6066 posDrop = newPos;
6068 if (!(posDrag == newPos)) {
6069 caret.on = true;
6070 SetTicking(true);
6071 InvalidateCaret();
6072 posDrag = newPos;
6073 InvalidateCaret();
6077 void Editor::DisplayCursor(Window::Cursor c) {
6078 if (cursorMode == SC_CURSORNORMAL)
6079 wMain.SetCursor(c);
6080 else
6081 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
6084 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
6085 int xMove = ptStart.x - ptNow.x;
6086 int yMove = ptStart.y - ptNow.y;
6087 int distanceSquared = xMove * xMove + yMove * yMove;
6088 return distanceSquared > 16;
6091 void Editor::StartDrag() {
6092 // Always handled by subclasses
6093 //SetMouseCapture(true);
6094 //DisplayCursor(Window::cursorArrow);
6097 void Editor::DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular) {
6098 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
6099 if (inDragDrop == ddDragging)
6100 dropWentOutside = false;
6102 bool positionWasInSelection = PositionInSelection(position.Position());
6104 bool positionOnEdgeOfSelection =
6105 (position == SelectionStart()) || (position == SelectionEnd());
6107 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
6108 (positionOnEdgeOfSelection && !moving)) {
6110 SelectionPosition selStart = SelectionStart();
6111 SelectionPosition selEnd = SelectionEnd();
6113 UndoGroup ug(pdoc);
6115 SelectionPosition positionAfterDeletion = position;
6116 if ((inDragDrop == ddDragging) && moving) {
6117 // Remove dragged out text
6118 if (rectangular || sel.selType == Selection::selLines) {
6119 for (size_t r=0; r<sel.Count(); r++) {
6120 if (position >= sel.Range(r).Start()) {
6121 if (position > sel.Range(r).End()) {
6122 positionAfterDeletion.Add(-sel.Range(r).Length());
6123 } else {
6124 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
6128 } else {
6129 if (position > selStart) {
6130 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
6133 ClearSelection();
6135 position = positionAfterDeletion;
6137 if (rectangular) {
6138 PasteRectangular(position, value, static_cast<int>(lengthValue));
6139 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
6140 SetEmptySelection(position);
6141 } else {
6142 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
6143 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
6144 if (pdoc->InsertString(position.Position(), value, static_cast<int>(lengthValue))) {
6145 SelectionPosition posAfterInsertion = position;
6146 posAfterInsertion.Add(static_cast<int>(lengthValue));
6147 SetSelection(posAfterInsertion, position);
6150 } else if (inDragDrop == ddDragging) {
6151 SetEmptySelection(position);
6155 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
6156 DropAt(position, value, strlen(value), moving, rectangular);
6160 * @return true if given position is inside the selection,
6162 bool Editor::PositionInSelection(int pos) {
6163 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
6164 for (size_t r=0; r<sel.Count(); r++) {
6165 if (sel.Range(r).Contains(pos))
6166 return true;
6168 return false;
6171 bool Editor::PointInSelection(Point pt) {
6172 SelectionPosition pos = SPositionFromLocation(pt, false, true);
6173 Point ptPos = LocationFromPosition(pos);
6174 for (size_t r=0; r<sel.Count(); r++) {
6175 SelectionRange range = sel.Range(r);
6176 if (range.Contains(pos)) {
6177 bool hit = true;
6178 if (pos == range.Start()) {
6179 // see if just before selection
6180 if (pt.x < ptPos.x) {
6181 hit = false;
6184 if (pos == range.End()) {
6185 // see if just after selection
6186 if (pt.x > ptPos.x) {
6187 hit = false;
6190 if (hit)
6191 return true;
6194 return false;
6197 bool Editor::PointInSelMargin(Point pt) {
6198 // Really means: "Point in a margin"
6199 if (vs.fixedColumnWidth > 0) { // There is a margin
6200 PRectangle rcSelMargin = GetClientRectangle();
6201 rcSelMargin.right = vs.textStart - vs.leftMarginWidth;
6202 rcSelMargin.left = vs.textStart - vs.fixedColumnWidth;
6203 return rcSelMargin.Contains(pt);
6204 } else {
6205 return false;
6209 Window::Cursor Editor::GetMarginCursor(Point pt) const {
6210 int x = 0;
6211 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
6212 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
6213 return static_cast<Window::Cursor>(vs.ms[margin].cursor);
6214 x += vs.ms[margin].width;
6216 return Window::cursorReverseArrow;
6219 void Editor::TrimAndSetSelection(int currentPos_, int anchor_) {
6220 sel.TrimSelection(SelectionRange(currentPos_, anchor_));
6221 SetSelection(currentPos_, anchor_);
6224 void Editor::LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine) {
6225 int selCurrentPos, selAnchorPos;
6226 if (wholeLine) {
6227 int lineCurrent_ = pdoc->LineFromPosition(lineCurrentPos_);
6228 int lineAnchor_ = pdoc->LineFromPosition(lineAnchorPos_);
6229 if (lineAnchorPos_ < lineCurrentPos_) {
6230 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
6231 selAnchorPos = pdoc->LineStart(lineAnchor_);
6232 } else if (lineAnchorPos_ > lineCurrentPos_) {
6233 selCurrentPos = pdoc->LineStart(lineCurrent_);
6234 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
6235 } else { // Same line, select it
6236 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
6237 selAnchorPos = pdoc->LineStart(lineAnchor_);
6239 } else {
6240 if (lineAnchorPos_ < lineCurrentPos_) {
6241 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
6242 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6243 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6244 } else if (lineAnchorPos_ > lineCurrentPos_) {
6245 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
6246 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6247 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
6248 } else { // Same line, select it
6249 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6250 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6251 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6254 TrimAndSetSelection(selCurrentPos, selAnchorPos);
6257 void Editor::WordSelection(int pos) {
6258 if (pos < wordSelectAnchorStartPos) {
6259 // Extend backward to the word containing pos.
6260 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
6261 // This ensures that a series of empty lines isn't counted as a single "word".
6262 if (!pdoc->IsLineEndPosition(pos))
6263 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
6264 TrimAndSetSelection(pos, wordSelectAnchorEndPos);
6265 } else if (pos > wordSelectAnchorEndPos) {
6266 // Extend forward to the word containing the character to the left of pos.
6267 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
6268 // This ensures that a series of empty lines isn't counted as a single "word".
6269 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
6270 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
6271 TrimAndSetSelection(pos, wordSelectAnchorStartPos);
6272 } else {
6273 // Select only the anchored word
6274 if (pos >= originalAnchorPos)
6275 TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
6276 else
6277 TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
6281 void Editor::DwellEnd(bool mouseMoved) {
6282 if (mouseMoved)
6283 ticksToDwell = dwellDelay;
6284 else
6285 ticksToDwell = SC_TIME_FOREVER;
6286 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
6287 dwelling = false;
6288 NotifyDwelling(ptMouseLast, dwelling);
6292 void Editor::MouseLeave() {
6293 SetHotSpotRange(NULL);
6294 if (!HaveMouseCapture()) {
6295 ptMouseLast = Point(-1,-1);
6296 DwellEnd(true);
6300 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
6301 return (!rectangular && ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0))
6302 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
6305 void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) {
6306 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
6307 ptMouseLast = pt;
6308 const bool ctrl = (modifiers & SCI_CTRL) != 0;
6309 const bool shift = (modifiers & SCI_SHIFT) != 0;
6310 const bool alt = (modifiers & SCI_ALT) != 0;
6311 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
6312 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6313 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
6314 newCharPos = MovePositionOutsideChar(newCharPos, -1);
6315 inDragDrop = ddNone;
6316 sel.SetMoveExtends(false);
6318 if (NotifyMarginClick(pt, modifiers))
6319 return;
6321 NotifyIndicatorClick(true, newPos.Position(), modifiers);
6323 bool inSelMargin = PointInSelMargin(pt);
6324 // In margin ctrl+(double)click should always select everything
6325 if (ctrl && inSelMargin) {
6326 SelectAll();
6327 lastClickTime = curTime;
6328 lastClick = pt;
6329 return;
6331 if (shift && !inSelMargin) {
6332 SetSelection(newPos);
6334 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
6335 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
6336 SetMouseCapture(true);
6337 if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))
6338 SetEmptySelection(newPos.Position());
6339 bool doubleClick = false;
6340 // Stop mouse button bounce changing selection type
6341 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
6342 if (inSelMargin) {
6343 // Inside margin selection type should be either selSubLine or selWholeLine.
6344 if (selectionType == selSubLine) {
6345 // If it is selSubLine, we're inside a *double* click and word wrap is enabled,
6346 // so we switch to selWholeLine in order to select whole line.
6347 selectionType = selWholeLine;
6348 } else if (selectionType != selSubLine && selectionType != selWholeLine) {
6349 // If it is neither, reset selection type to line selection.
6350 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6352 } else {
6353 if (selectionType == selChar) {
6354 selectionType = selWord;
6355 doubleClick = true;
6356 } else if (selectionType == selWord) {
6357 // Since we ended up here, we're inside a *triple* click, which should always select
6358 // whole line irregardless of word wrap being enabled or not.
6359 selectionType = selWholeLine;
6360 } else {
6361 selectionType = selChar;
6362 originalAnchorPos = sel.MainCaret();
6367 if (selectionType == selWord) {
6368 int charPos = originalAnchorPos;
6369 if (sel.MainCaret() == originalAnchorPos) {
6370 charPos = PositionFromLocation(pt, false, true);
6371 charPos = MovePositionOutsideChar(charPos, -1);
6374 int startWord, endWord;
6375 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
6376 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
6377 endWord = pdoc->ExtendWordSelect(charPos, 1);
6378 } else {
6379 // Selecting backwards, or anchor beyond last character on line. In these cases,
6380 // we select the word containing the character to the *left* of the anchor.
6381 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
6382 startWord = pdoc->ExtendWordSelect(charPos, -1);
6383 endWord = pdoc->ExtendWordSelect(startWord, 1);
6384 } else {
6385 // Anchor at start of line; select nothing to begin with.
6386 startWord = charPos;
6387 endWord = charPos;
6391 wordSelectAnchorStartPos = startWord;
6392 wordSelectAnchorEndPos = endWord;
6393 wordSelectInitialCaretPos = sel.MainCaret();
6394 WordSelection(wordSelectInitialCaretPos);
6395 } else if (selectionType == selSubLine || selectionType == selWholeLine) {
6396 lineAnchorPos = newPos.Position();
6397 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6398 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
6399 } else {
6400 SetEmptySelection(sel.MainCaret());
6402 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
6403 if (doubleClick) {
6404 NotifyDoubleClick(pt, modifiers);
6405 if (PositionIsHotspot(newCharPos.Position()))
6406 NotifyHotSpotDoubleClicked(newCharPos.Position(), modifiers);
6408 } else { // Single click
6409 if (inSelMargin) {
6410 sel.selType = Selection::selStream;
6411 if (!shift) {
6412 // Single click in margin: select whole line or only subline if word wrap is enabled
6413 lineAnchorPos = newPos.Position();
6414 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6415 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6416 } else {
6417 // Single shift+click in margin: select from line anchor to clicked line
6418 if (sel.MainAnchor() > sel.MainCaret())
6419 lineAnchorPos = sel.MainAnchor() - 1;
6420 else
6421 lineAnchorPos = sel.MainAnchor();
6422 // Reset selection type if there is an empty selection.
6423 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
6424 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
6425 // This ensures that we continue selecting in the same selection mode.
6426 if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
6427 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6428 LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
6431 SetDragPosition(SelectionPosition(invalidPosition));
6432 SetMouseCapture(true);
6433 } else {
6434 if (PointIsHotspot(pt)) {
6435 NotifyHotSpotClicked(newCharPos.Position(), modifiers);
6436 hotSpotClickPos = newCharPos.Position();
6438 if (!shift) {
6439 if (PointInSelection(pt) && !SelectionEmpty())
6440 inDragDrop = ddInitial;
6441 else
6442 inDragDrop = ddNone;
6444 SetMouseCapture(true);
6445 if (inDragDrop != ddInitial) {
6446 SetDragPosition(SelectionPosition(invalidPosition));
6447 if (!shift) {
6448 if (ctrl && multipleSelection) {
6449 SelectionRange range(newPos);
6450 sel.TentativeSelection(range);
6451 InvalidateSelection(range, true);
6452 } else {
6453 InvalidateSelection(SelectionRange(newPos), true);
6454 if (sel.Count() > 1)
6455 Redraw();
6456 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
6457 sel.Clear();
6458 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6459 SetSelection(newPos, newPos);
6462 SelectionPosition anchorCurrent = newPos;
6463 if (shift)
6464 anchorCurrent = sel.IsRectangular() ?
6465 sel.Rectangular().anchor : sel.RangeMain().anchor;
6466 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6467 selectionType = selChar;
6468 originalAnchorPos = sel.MainCaret();
6469 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
6470 SetRectangularRange();
6474 lastClickTime = curTime;
6475 lastClick = pt;
6476 lastXChosen = pt.x + xOffset;
6477 ShowCaretAtCurrentPosition();
6480 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
6481 return ButtonDownWithModifiers(pt, curTime, ModifierFlags(shift, ctrl, alt));
6484 bool Editor::PositionIsHotspot(int position) const {
6485 return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
6488 bool Editor::PointIsHotspot(Point pt) {
6489 int pos = PositionFromLocation(pt, true, true);
6490 if (pos == INVALID_POSITION)
6491 return false;
6492 return PositionIsHotspot(pos);
6495 void Editor::SetHotSpotRange(Point *pt) {
6496 if (pt) {
6497 int pos = PositionFromLocation(*pt, false, true);
6499 // If we don't limit this to word characters then the
6500 // range can encompass more than the run range and then
6501 // the underline will not be drawn properly.
6502 int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
6503 int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
6505 // Only invalidate the range if the hotspot range has changed...
6506 if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
6507 if (hsStart != -1) {
6508 InvalidateRange(hsStart, hsEnd);
6510 hsStart = hsStart_;
6511 hsEnd = hsEnd_;
6512 InvalidateRange(hsStart, hsEnd);
6514 } else {
6515 if (hsStart != -1) {
6516 int hsStart_ = hsStart;
6517 int hsEnd_ = hsEnd;
6518 hsStart = -1;
6519 hsEnd = -1;
6520 InvalidateRange(hsStart_, hsEnd_);
6521 } else {
6522 hsStart = -1;
6523 hsEnd = -1;
6528 void Editor::GetHotSpotRange(int &hsStart_, int &hsEnd_) const {
6529 hsStart_ = hsStart;
6530 hsEnd_ = hsEnd;
6533 void Editor::ButtonMoveWithModifiers(Point pt, int modifiers) {
6534 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
6535 DwellEnd(true);
6538 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
6539 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6540 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
6542 if (inDragDrop == ddInitial) {
6543 if (DragThreshold(ptMouseLast, pt)) {
6544 SetMouseCapture(false);
6545 SetDragPosition(movePos);
6546 CopySelectionRange(&drag);
6547 StartDrag();
6549 return;
6552 ptMouseLast = pt;
6553 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
6554 if (HaveMouseCapture()) {
6556 // Slow down autoscrolling/selection
6557 autoScrollTimer.ticksToWait -= timer.tickSize;
6558 if (autoScrollTimer.ticksToWait > 0)
6559 return;
6560 autoScrollTimer.ticksToWait = autoScrollDelay;
6562 // Adjust selection
6563 if (posDrag.IsValid()) {
6564 SetDragPosition(movePos);
6565 } else {
6566 if (selectionType == selChar) {
6567 if (sel.selType == Selection::selStream && (modifiers & SCI_ALT) && mouseSelectionRectangularSwitch) {
6568 sel.selType = Selection::selRectangle;
6570 if (sel.IsRectangular()) {
6571 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
6572 SetSelection(movePos, sel.RangeMain().anchor);
6573 } else if (sel.Count() > 1) {
6574 InvalidateSelection(sel.RangeMain(), false);
6575 SelectionRange range(movePos, sel.RangeMain().anchor);
6576 sel.TentativeSelection(range);
6577 InvalidateSelection(range, true);
6578 } else {
6579 SetSelection(movePos, sel.RangeMain().anchor);
6581 } else if (selectionType == selWord) {
6582 // Continue selecting by word
6583 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
6584 // No need to do anything. Previously this case was lumped
6585 // in with "Moved forward", but that can be harmful in this
6586 // case: a handler for the NotifyDoubleClick re-adjusts
6587 // the selection for a fancier definition of "word" (for
6588 // example, in Perl it is useful to include the leading
6589 // '$', '%' or '@' on variables for word selection). In this
6590 // the ButtonMove() called via Tick() for auto-scrolling
6591 // could result in the fancier word selection adjustment
6592 // being unmade.
6593 } else {
6594 wordSelectInitialCaretPos = -1;
6595 WordSelection(movePos.Position());
6597 } else {
6598 // Continue selecting by line
6599 LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
6603 // Autoscroll
6604 PRectangle rcClient = GetClientRectangle();
6605 Point ptOrigin = GetVisibleOriginInMain();
6606 rcClient.Move(0, -ptOrigin.y);
6607 int lineMove = DisplayFromPosition(movePos.Position());
6608 if (pt.y > rcClient.bottom) {
6609 ScrollTo(lineMove - LinesOnScreen() + 1);
6610 Redraw();
6611 } else if (pt.y < rcClient.top) {
6612 ScrollTo(lineMove);
6613 Redraw();
6615 EnsureCaretVisible(false, false, true);
6617 if (hsStart != -1 && !PointIsHotspot(pt))
6618 SetHotSpotRange(NULL);
6620 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,true) != hotSpotClickPos) {
6621 if (inDragDrop == ddNone) {
6622 DisplayCursor(Window::cursorText);
6624 hotSpotClickPos = INVALID_POSITION;
6627 } else {
6628 if (vs.fixedColumnWidth > 0) { // There is a margin
6629 if (PointInSelMargin(pt)) {
6630 DisplayCursor(GetMarginCursor(pt));
6631 SetHotSpotRange(NULL);
6632 return; // No need to test for selection
6635 // Display regular (drag) cursor over selection
6636 if (PointInSelection(pt) && !SelectionEmpty()) {
6637 DisplayCursor(Window::cursorArrow);
6638 } else if (PointIsHotspot(pt)) {
6639 DisplayCursor(Window::cursorHand);
6640 SetHotSpotRange(&pt);
6641 } else {
6642 DisplayCursor(Window::cursorText);
6643 SetHotSpotRange(NULL);
6648 void Editor::ButtonMove(Point pt) {
6649 ButtonMoveWithModifiers(pt, 0);
6652 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
6653 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
6654 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
6655 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6656 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6657 if (inDragDrop == ddInitial) {
6658 inDragDrop = ddNone;
6659 SetEmptySelection(newPos);
6660 selectionType = selChar;
6661 originalAnchorPos = sel.MainCaret();
6663 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
6664 hotSpotClickPos = INVALID_POSITION;
6665 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
6666 newCharPos = MovePositionOutsideChar(newCharPos, -1);
6667 NotifyHotSpotReleaseClick(newCharPos.Position(), ctrl ? SCI_CTRL : 0);
6669 if (HaveMouseCapture()) {
6670 if (PointInSelMargin(pt)) {
6671 DisplayCursor(GetMarginCursor(pt));
6672 } else {
6673 DisplayCursor(Window::cursorText);
6674 SetHotSpotRange(NULL);
6676 ptMouseLast = pt;
6677 SetMouseCapture(false);
6678 NotifyIndicatorClick(false, newPos.Position(), 0);
6679 if (inDragDrop == ddDragging) {
6680 SelectionPosition selStart = SelectionStart();
6681 SelectionPosition selEnd = SelectionEnd();
6682 if (selStart < selEnd) {
6683 if (drag.Length()) {
6684 if (ctrl) {
6685 if (pdoc->InsertString(newPos.Position(), drag.Data(), static_cast<int>(drag.Length()))) {
6686 SetSelection(newPos.Position(), newPos.Position() + static_cast<int>(drag.Length()));
6688 } else if (newPos < selStart) {
6689 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
6690 if (pdoc->InsertString(newPos.Position(), drag.Data(), static_cast<int>(drag.Length()))) {
6691 SetSelection(newPos.Position(), newPos.Position() + static_cast<int>(drag.Length()));
6693 } else if (newPos > selEnd) {
6694 pdoc->DeleteChars(selStart.Position(), static_cast<int>(drag.Length()));
6695 newPos.Add(-static_cast<int>(drag.Length()));
6696 if (pdoc->InsertString(newPos.Position(), drag.Data(), static_cast<int>(drag.Length()))) {
6697 SetSelection(newPos.Position(), newPos.Position() + static_cast<int>(drag.Length()));
6699 } else {
6700 SetEmptySelection(newPos.Position());
6702 drag.Clear();
6704 selectionType = selChar;
6706 } else {
6707 if (selectionType == selChar) {
6708 if (sel.Count() > 1) {
6709 sel.RangeMain() =
6710 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
6711 InvalidateSelection(sel.RangeMain(), true);
6712 } else {
6713 SetSelection(newPos, sel.RangeMain().anchor);
6716 sel.CommitTentative();
6718 SetRectangularRange();
6719 lastClickTime = curTime;
6720 lastClick = pt;
6721 lastXChosen = pt.x + xOffset;
6722 if (sel.selType == Selection::selStream) {
6723 SetLastXChosen();
6725 inDragDrop = ddNone;
6726 EnsureCaretVisible(false);
6730 // Called frequently to perform background UI including
6731 // caret blinking and automatic scrolling.
6732 void Editor::Tick() {
6733 if (HaveMouseCapture()) {
6734 // Auto scroll
6735 ButtonMove(ptMouseLast);
6737 if (caret.period > 0) {
6738 timer.ticksToWait -= timer.tickSize;
6739 if (timer.ticksToWait <= 0) {
6740 caret.on = !caret.on;
6741 timer.ticksToWait = caret.period;
6742 if (caret.active) {
6743 InvalidateCaret();
6747 if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) {
6748 scrollWidth = lineWidthMaxSeen;
6749 SetScrollBars();
6751 if ((dwellDelay < SC_TIME_FOREVER) &&
6752 (ticksToDwell > 0) &&
6753 (!HaveMouseCapture()) &&
6754 (ptMouseLast.y >= 0)) {
6755 ticksToDwell -= timer.tickSize;
6756 if (ticksToDwell <= 0) {
6757 dwelling = true;
6758 NotifyDwelling(ptMouseLast, dwelling);
6763 bool Editor::Idle() {
6765 bool idleDone;
6767 bool wrappingDone = !Wrapping();
6769 if (!wrappingDone) {
6770 // Wrap lines during idle.
6771 WrapLines(wsIdle);
6772 // No more wrapping
6773 if (!wrapPending.NeedsWrap())
6774 wrappingDone = true;
6777 // Add more idle things to do here, but make sure idleDone is
6778 // set correctly before the function returns. returning
6779 // false will stop calling this idle funtion until SetIdle() is
6780 // called again.
6782 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
6784 return !idleDone;
6787 void Editor::SetFocusState(bool focusState) {
6788 hasFocus = focusState;
6789 NotifyFocus(hasFocus);
6790 if (hasFocus) {
6791 ShowCaretAtCurrentPosition();
6792 } else {
6793 CancelModes();
6794 DropCaret();
6798 int Editor::PositionAfterArea(PRectangle rcArea) const {
6799 // The start of the document line after the display line after the area
6800 // This often means that the line after a modification is restyled which helps
6801 // detect multiline comment additions and heals single line comments
6802 int lineAfter = TopLineOfMain() + (rcArea.bottom - 1) / vs.lineHeight + 1;
6803 if (lineAfter < cs.LinesDisplayed())
6804 return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
6805 else
6806 return pdoc->Length();
6809 // Style to a position within the view. If this causes a change at end of last line then
6810 // affects later lines so style all the viewed text.
6811 void Editor::StyleToPositionInView(Position pos) {
6812 int endWindow = PositionAfterArea(GetClientDrawingRectangle());
6813 if (pos > endWindow)
6814 pos = endWindow;
6815 int styleAtEnd = pdoc->StyleAt(pos-1);
6816 pdoc->EnsureStyledTo(pos);
6817 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleAt(pos-1))) {
6818 // Style at end of line changed so is multi-line change like starting a comment
6819 // so require rest of window to be styled.
6820 DiscardOverdraw(); // Prepared bitmaps may be invalid
6821 // DiscardOverdraw may have truncated client drawing area so recalculate endWindow
6822 endWindow = PositionAfterArea(GetClientDrawingRectangle());
6823 pdoc->EnsureStyledTo(endWindow);
6827 void Editor::IdleWork() {
6828 // Style the line after the modification as this allows modifications that change just the
6829 // line of the modification to heal instead of propagating to the rest of the window.
6830 if (workNeeded.items & WorkNeeded::workStyle)
6831 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2));
6833 NotifyUpdateUI();
6834 workNeeded.Reset();
6837 void Editor::QueueIdleWork(WorkNeeded::workItems items, int upTo) {
6838 workNeeded.Need(items, upTo);
6841 bool Editor::PaintContains(PRectangle rc) {
6842 if (rc.Empty()) {
6843 return true;
6844 } else {
6845 return rcPaint.Contains(rc);
6849 bool Editor::PaintContainsMargin() {
6850 if (wMargin.GetID()) {
6851 // With separate margin view, paint of text view
6852 // never contains margin.
6853 return false;
6855 PRectangle rcSelMargin = GetClientRectangle();
6856 rcSelMargin.right = vs.textStart;
6857 return PaintContains(rcSelMargin);
6860 void Editor::CheckForChangeOutsidePaint(Range r) {
6861 if (paintState == painting && !paintingAllText) {
6862 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
6863 if (!r.Valid())
6864 return;
6866 PRectangle rcRange = RectangleFromRange(r.start, r.end);
6867 PRectangle rcText = GetTextRectangle();
6868 if (rcRange.top < rcText.top) {
6869 rcRange.top = rcText.top;
6871 if (rcRange.bottom > rcText.bottom) {
6872 rcRange.bottom = rcText.bottom;
6875 if (!PaintContains(rcRange)) {
6876 AbandonPaint();
6877 paintAbandonedByStyling = true;
6882 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
6883 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
6884 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
6885 CheckForChangeOutsidePaint(Range(braces[0]));
6886 CheckForChangeOutsidePaint(Range(pos0));
6887 braces[0] = pos0;
6889 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
6890 CheckForChangeOutsidePaint(Range(braces[1]));
6891 CheckForChangeOutsidePaint(Range(pos1));
6892 braces[1] = pos1;
6894 bracesMatchStyle = matchStyle;
6895 if (paintState == notPainting) {
6896 Redraw();
6901 void Editor::SetAnnotationHeights(int start, int end) {
6902 if (vs.annotationVisible) {
6903 bool changedHeight = false;
6904 for (int line=start; line<end && line<pdoc->LinesTotal(); line++) {
6905 int linesWrapped = 1;
6906 if (Wrapping()) {
6907 AutoSurface surface(this);
6908 AutoLineLayout ll(llc, RetrieveLineLayout(line));
6909 if (surface && ll) {
6910 LayoutLine(line, surface, vs, ll, wrapWidth);
6911 linesWrapped = ll->lines;
6914 if (cs.SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
6915 changedHeight = true;
6917 if (changedHeight) {
6918 Redraw();
6923 void Editor::SetDocPointer(Document *document) {
6924 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
6925 pdoc->RemoveWatcher(this, 0);
6926 pdoc->Release();
6927 if (document == NULL) {
6928 pdoc = new Document();
6929 } else {
6930 pdoc = document;
6932 pdoc->AddRef();
6934 // Ensure all positions within document
6935 sel.Clear();
6936 targetStart = 0;
6937 targetEnd = 0;
6939 braces[0] = invalidPosition;
6940 braces[1] = invalidPosition;
6942 vs.ReleaseAllExtendedStyles();
6944 SetRepresentations();
6946 // Reset the contraction state to fully shown.
6947 cs.Clear();
6948 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6949 SetAnnotationHeights(0, pdoc->LinesTotal());
6950 llc.Deallocate();
6951 NeedWrapping();
6953 pdoc->AddWatcher(this, 0);
6954 SetScrollBars();
6955 Redraw();
6958 void Editor::SetAnnotationVisible(int visible) {
6959 if (vs.annotationVisible != visible) {
6960 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
6961 vs.annotationVisible = visible;
6962 if (changedFromOrToHidden) {
6963 int dir = vs.annotationVisible ? 1 : -1;
6964 for (int line=0; line<pdoc->LinesTotal(); line++) {
6965 int annotationLines = pdoc->AnnotationLines(line);
6966 if (annotationLines > 0) {
6967 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
6971 Redraw();
6976 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
6978 int Editor::ExpandLine(int line) {
6979 int lineMaxSubord = pdoc->GetLastChild(line);
6980 line++;
6981 while (line <= lineMaxSubord) {
6982 cs.SetVisible(line, line, true);
6983 int level = pdoc->GetLevel(line);
6984 if (level & SC_FOLDLEVELHEADERFLAG) {
6985 if (cs.GetExpanded(line)) {
6986 line = ExpandLine(line);
6987 } else {
6988 line = pdoc->GetLastChild(line);
6991 line++;
6993 return lineMaxSubord;
6996 void Editor::SetFoldExpanded(int lineDoc, bool expanded) {
6997 if (cs.SetExpanded(lineDoc, expanded)) {
6998 RedrawSelMargin();
7002 void Editor::FoldLine(int line, int action) {
7003 if (line >= 0) {
7004 if (action == SC_FOLDACTION_TOGGLE) {
7005 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
7006 line = pdoc->GetFoldParent(line);
7007 if (line < 0)
7008 return;
7010 action = (cs.GetExpanded(line)) ? SC_FOLDACTION_CONTRACT : SC_FOLDACTION_EXPAND;
7013 if (action == SC_FOLDACTION_CONTRACT) {
7014 int lineMaxSubord = pdoc->GetLastChild(line);
7015 if (lineMaxSubord > line) {
7016 cs.SetExpanded(line, 0);
7017 cs.SetVisible(line + 1, lineMaxSubord, false);
7019 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
7020 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
7021 // This does not re-expand the fold
7022 EnsureCaretVisible();
7026 } else {
7027 if (!(cs.GetVisible(line))) {
7028 EnsureLineVisible(line, false);
7029 GoToLine(line);
7031 cs.SetExpanded(line, 1);
7032 ExpandLine(line);
7035 SetScrollBars();
7036 Redraw();
7040 void Editor::FoldExpand(int line, int action, int level) {
7041 bool expanding = action == SC_FOLDACTION_EXPAND;
7042 if (action == SC_FOLDACTION_TOGGLE) {
7043 expanding = !cs.GetExpanded(line);
7045 SetFoldExpanded(line, expanding);
7046 if (expanding && (cs.HiddenLines() == 0))
7047 // Nothing to do
7048 return;
7049 int lineMaxSubord = pdoc->GetLastChild(line, level & SC_FOLDLEVELNUMBERMASK);
7050 line++;
7051 cs.SetVisible(line, lineMaxSubord, expanding);
7052 while (line <= lineMaxSubord) {
7053 int levelLine = pdoc->GetLevel(line);
7054 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
7055 SetFoldExpanded(line, expanding);
7057 line++;
7059 SetScrollBars();
7060 Redraw();
7063 int Editor::ContractedFoldNext(int lineStart) const {
7064 for (int line = lineStart; line<pdoc->LinesTotal();) {
7065 if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
7066 return line;
7067 line = cs.ContractedNext(line+1);
7068 if (line < 0)
7069 return -1;
7072 return -1;
7076 * Recurse up from this line to find any folds that prevent this line from being visible
7077 * and unfold them all.
7079 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
7081 // In case in need of wrapping to ensure DisplayFromDoc works.
7082 if (lineDoc >= wrapPending.start)
7083 WrapLines(wsAll);
7085 if (!cs.GetVisible(lineDoc)) {
7086 // Back up to find a non-blank line
7087 int lookLine = lineDoc;
7088 int lookLineLevel = pdoc->GetLevel(lookLine);
7089 while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
7090 lookLineLevel = pdoc->GetLevel(--lookLine);
7092 int lineParent = pdoc->GetFoldParent(lookLine);
7093 if (lineParent < 0) {
7094 // Backed up to a top level line, so try to find parent of initial line
7095 lineParent = pdoc->GetFoldParent(lineDoc);
7097 if (lineParent >= 0) {
7098 if (lineDoc != lineParent)
7099 EnsureLineVisible(lineParent, enforcePolicy);
7100 if (!cs.GetExpanded(lineParent)) {
7101 cs.SetExpanded(lineParent, 1);
7102 ExpandLine(lineParent);
7105 SetScrollBars();
7106 Redraw();
7108 if (enforcePolicy) {
7109 int lineDisplay = cs.DisplayFromDoc(lineDoc);
7110 if (visiblePolicy & VISIBLE_SLOP) {
7111 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
7112 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
7113 SetVerticalScrollPos();
7114 Redraw();
7115 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
7116 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
7117 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
7118 SetVerticalScrollPos();
7119 Redraw();
7121 } else {
7122 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
7123 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
7124 SetVerticalScrollPos();
7125 Redraw();
7131 void Editor::FoldAll(int action) {
7132 pdoc->EnsureStyledTo(pdoc->Length());
7133 int maxLine = pdoc->LinesTotal();
7134 bool expanding = action == SC_FOLDACTION_EXPAND;
7135 if (action == SC_FOLDACTION_TOGGLE) {
7136 // Discover current state
7137 for (int lineSeek = 0; lineSeek < maxLine; lineSeek++) {
7138 if (pdoc->GetLevel(lineSeek) & SC_FOLDLEVELHEADERFLAG) {
7139 expanding = !cs.GetExpanded(lineSeek);
7140 break;
7144 if (expanding) {
7145 cs.SetVisible(0, maxLine-1, true);
7146 for (int line = 0; line < maxLine; line++) {
7147 int levelLine = pdoc->GetLevel(line);
7148 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
7149 SetFoldExpanded(line, true);
7152 } else {
7153 for (int line = 0; line < maxLine; line++) {
7154 int level = pdoc->GetLevel(line);
7155 if ((level & SC_FOLDLEVELHEADERFLAG) &&
7156 (SC_FOLDLEVELBASE == (level & SC_FOLDLEVELNUMBERMASK))) {
7157 SetFoldExpanded(line, false);
7158 int lineMaxSubord = pdoc->GetLastChild(line, -1);
7159 if (lineMaxSubord > line) {
7160 cs.SetVisible(line + 1, lineMaxSubord, false);
7165 SetScrollBars();
7166 Redraw();
7169 void Editor::FoldChanged(int line, int levelNow, int levelPrev) {
7170 if (levelNow & SC_FOLDLEVELHEADERFLAG) {
7171 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
7172 // Adding a fold point.
7173 if (cs.SetExpanded(line, true)) {
7174 RedrawSelMargin();
7176 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
7178 } else if (levelPrev & SC_FOLDLEVELHEADERFLAG) {
7179 if (!cs.GetExpanded(line)) {
7180 // Removing the fold from one that has been contracted so should expand
7181 // otherwise lines are left invisible with no way to make them visible
7182 if (cs.SetExpanded(line, true)) {
7183 RedrawSelMargin();
7185 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
7188 if (!(levelNow & SC_FOLDLEVELWHITEFLAG) &&
7189 ((levelPrev & SC_FOLDLEVELNUMBERMASK) > (levelNow & SC_FOLDLEVELNUMBERMASK))) {
7190 if (cs.HiddenLines()) {
7191 // See if should still be hidden
7192 int parentLine = pdoc->GetFoldParent(line);
7193 if ((parentLine < 0) || (cs.GetExpanded(parentLine) && cs.GetVisible(parentLine))) {
7194 cs.SetVisible(line, line, true);
7195 SetScrollBars();
7196 Redraw();
7202 void Editor::NeedShown(int pos, int len) {
7203 if (foldAutomatic & SC_AUTOMATICFOLD_SHOW) {
7204 int lineStart = pdoc->LineFromPosition(pos);
7205 int lineEnd = pdoc->LineFromPosition(pos+len);
7206 for (int line = lineStart; line <= lineEnd; line++) {
7207 EnsureLineVisible(line, false);
7209 } else {
7210 NotifyNeedShown(pos, len);
7214 int Editor::GetTag(char *tagValue, int tagNumber) {
7215 const char *text = 0;
7216 int length = 0;
7217 if ((tagNumber >= 1) && (tagNumber <= 9)) {
7218 char name[3] = "\\?";
7219 name[1] = static_cast<char>(tagNumber + '0');
7220 length = 2;
7221 text = pdoc->SubstituteByPosition(name, &length);
7223 if (tagValue) {
7224 if (text)
7225 memcpy(tagValue, text, length + 1);
7226 else
7227 *tagValue = '\0';
7229 return length;
7232 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
7233 UndoGroup ug(pdoc);
7234 if (length == -1)
7235 length = istrlen(text);
7236 if (replacePatterns) {
7237 text = pdoc->SubstituteByPosition(text, &length);
7238 if (!text) {
7239 return 0;
7242 if (targetStart != targetEnd)
7243 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
7244 targetEnd = targetStart;
7245 pdoc->InsertString(targetStart, text, length);
7246 targetEnd = targetStart + length;
7247 return length;
7250 bool Editor::IsUnicodeMode() const {
7251 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
7254 int Editor::CodePage() const {
7255 if (pdoc)
7256 return pdoc->dbcsCodePage;
7257 else
7258 return 0;
7261 int Editor::WrapCount(int line) {
7262 AutoSurface surface(this);
7263 AutoLineLayout ll(llc, RetrieveLineLayout(line));
7265 if (surface && ll) {
7266 LayoutLine(line, surface, vs, ll, wrapWidth);
7267 return ll->lines;
7268 } else {
7269 return 1;
7273 void Editor::AddStyledText(char *buffer, int appendLength) {
7274 // The buffer consists of alternating character bytes and style bytes
7275 int textLength = appendLength / 2;
7276 std::string text(textLength, '\0');
7277 int i;
7278 for (i = 0; i < textLength; i++) {
7279 text[i] = buffer[i*2];
7281 pdoc->InsertString(CurrentPosition(), text.c_str(), textLength);
7282 for (i = 0; i < textLength; i++) {
7283 text[i] = buffer[i*2+1];
7285 pdoc->StartStyling(CurrentPosition(), static_cast<char>(0xff));
7286 pdoc->SetStyles(textLength, text.c_str());
7287 SetEmptySelection(sel.MainCaret() + textLength);
7290 static bool ValidMargin(unsigned long wParam) {
7291 return wParam <= SC_MAX_MARGIN;
7294 static char *CharPtrFromSPtr(sptr_t lParam) {
7295 return reinterpret_cast<char *>(lParam);
7298 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7299 vs.EnsureStyle(wParam);
7300 switch (iMessage) {
7301 case SCI_STYLESETFORE:
7302 vs.styles[wParam].fore = ColourDesired(lParam);
7303 break;
7304 case SCI_STYLESETBACK:
7305 vs.styles[wParam].back = ColourDesired(lParam);
7306 break;
7307 case SCI_STYLESETBOLD:
7308 vs.styles[wParam].weight = lParam != 0 ? SC_WEIGHT_BOLD : SC_WEIGHT_NORMAL;
7309 break;
7310 case SCI_STYLESETWEIGHT:
7311 vs.styles[wParam].weight = lParam;
7312 break;
7313 case SCI_STYLESETITALIC:
7314 vs.styles[wParam].italic = lParam != 0;
7315 break;
7316 case SCI_STYLESETEOLFILLED:
7317 vs.styles[wParam].eolFilled = lParam != 0;
7318 break;
7319 case SCI_STYLESETSIZE:
7320 vs.styles[wParam].size = lParam * SC_FONT_SIZE_MULTIPLIER;
7321 break;
7322 case SCI_STYLESETSIZEFRACTIONAL:
7323 vs.styles[wParam].size = lParam;
7324 break;
7325 case SCI_STYLESETFONT:
7326 if (lParam != 0) {
7327 vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam));
7329 break;
7330 case SCI_STYLESETUNDERLINE:
7331 vs.styles[wParam].underline = lParam != 0;
7332 break;
7333 case SCI_STYLESETCASE:
7334 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
7335 break;
7336 case SCI_STYLESETCHARACTERSET:
7337 vs.styles[wParam].characterSet = lParam;
7338 pdoc->SetCaseFolder(NULL);
7339 break;
7340 case SCI_STYLESETVISIBLE:
7341 vs.styles[wParam].visible = lParam != 0;
7342 break;
7343 case SCI_STYLESETCHANGEABLE:
7344 vs.styles[wParam].changeable = lParam != 0;
7345 break;
7346 case SCI_STYLESETHOTSPOT:
7347 vs.styles[wParam].hotspot = lParam != 0;
7348 break;
7350 InvalidateStyleRedraw();
7353 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7354 vs.EnsureStyle(wParam);
7355 switch (iMessage) {
7356 case SCI_STYLEGETFORE:
7357 return vs.styles[wParam].fore.AsLong();
7358 case SCI_STYLEGETBACK:
7359 return vs.styles[wParam].back.AsLong();
7360 case SCI_STYLEGETBOLD:
7361 return vs.styles[wParam].weight > SC_WEIGHT_NORMAL;
7362 case SCI_STYLEGETWEIGHT:
7363 return vs.styles[wParam].weight;
7364 case SCI_STYLEGETITALIC:
7365 return vs.styles[wParam].italic ? 1 : 0;
7366 case SCI_STYLEGETEOLFILLED:
7367 return vs.styles[wParam].eolFilled ? 1 : 0;
7368 case SCI_STYLEGETSIZE:
7369 return vs.styles[wParam].size / SC_FONT_SIZE_MULTIPLIER;
7370 case SCI_STYLEGETSIZEFRACTIONAL:
7371 return vs.styles[wParam].size;
7372 case SCI_STYLEGETFONT:
7373 return StringResult(lParam, vs.styles[wParam].fontName);
7374 case SCI_STYLEGETUNDERLINE:
7375 return vs.styles[wParam].underline ? 1 : 0;
7376 case SCI_STYLEGETCASE:
7377 return static_cast<int>(vs.styles[wParam].caseForce);
7378 case SCI_STYLEGETCHARACTERSET:
7379 return vs.styles[wParam].characterSet;
7380 case SCI_STYLEGETVISIBLE:
7381 return vs.styles[wParam].visible ? 1 : 0;
7382 case SCI_STYLEGETCHANGEABLE:
7383 return vs.styles[wParam].changeable ? 1 : 0;
7384 case SCI_STYLEGETHOTSPOT:
7385 return vs.styles[wParam].hotspot ? 1 : 0;
7387 return 0;
7390 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
7391 const size_t len = val ? strlen(val) : 0;
7392 if (lParam) {
7393 char *ptr = CharPtrFromSPtr(lParam);
7394 if (val)
7395 memcpy(ptr, val, len+1);
7396 else
7397 *ptr = 0;
7399 return len; // Not including NUL
7402 sptr_t Editor::BytesResult(sptr_t lParam, const unsigned char *val, size_t len) {
7403 // No NUL termination: len is number of valid/displayed bytes
7404 if (lParam) {
7405 char *ptr = CharPtrFromSPtr(lParam);
7406 if (val)
7407 memcpy(ptr, val, len);
7408 else
7409 *ptr = 0;
7411 return val ? len : 0;
7414 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7415 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
7417 // Optional macro recording hook
7418 if (recordingMacro)
7419 NotifyMacroRecord(iMessage, wParam, lParam);
7421 switch (iMessage) {
7423 case SCI_GETTEXT: {
7424 if (lParam == 0)
7425 return pdoc->Length() + 1;
7426 if (wParam == 0)
7427 return 0;
7428 char *ptr = CharPtrFromSPtr(lParam);
7429 unsigned int iChar = 0;
7430 for (; iChar < wParam - 1; iChar++)
7431 ptr[iChar] = pdoc->CharAt(iChar);
7432 ptr[iChar] = '\0';
7433 return iChar;
7436 case SCI_SETTEXT: {
7437 if (lParam == 0)
7438 return 0;
7439 UndoGroup ug(pdoc);
7440 pdoc->DeleteChars(0, pdoc->Length());
7441 SetEmptySelection(0);
7442 pdoc->InsertCString(0, CharPtrFromSPtr(lParam));
7443 return 1;
7446 case SCI_GETTEXTLENGTH:
7447 return pdoc->Length();
7449 case SCI_CUT:
7450 Cut();
7451 SetLastXChosen();
7452 break;
7454 case SCI_COPY:
7455 Copy();
7456 break;
7458 case SCI_COPYALLOWLINE:
7459 CopyAllowLine();
7460 break;
7462 case SCI_VERTICALCENTRECARET:
7463 VerticalCentreCaret();
7464 break;
7466 case SCI_MOVESELECTEDLINESUP:
7467 MoveSelectedLinesUp();
7468 break;
7470 case SCI_MOVESELECTEDLINESDOWN:
7471 MoveSelectedLinesDown();
7472 break;
7474 case SCI_COPYRANGE:
7475 CopyRangeToClipboard(wParam, lParam);
7476 break;
7478 case SCI_COPYTEXT:
7479 CopyText(wParam, CharPtrFromSPtr(lParam));
7480 break;
7482 case SCI_PASTE:
7483 Paste();
7484 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
7485 SetLastXChosen();
7487 EnsureCaretVisible();
7488 break;
7490 case SCI_CLEAR:
7491 Clear();
7492 SetLastXChosen();
7493 EnsureCaretVisible();
7494 break;
7496 case SCI_UNDO:
7497 Undo();
7498 SetLastXChosen();
7499 break;
7501 case SCI_CANUNDO:
7502 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
7504 case SCI_EMPTYUNDOBUFFER:
7505 pdoc->DeleteUndoHistory();
7506 return 0;
7508 case SCI_GETFIRSTVISIBLELINE:
7509 return topLine;
7511 case SCI_SETFIRSTVISIBLELINE:
7512 ScrollTo(wParam);
7513 break;
7515 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
7516 int lineStart = pdoc->LineStart(wParam);
7517 int lineEnd = pdoc->LineStart(wParam + 1);
7518 if (lParam == 0) {
7519 return lineEnd - lineStart;
7521 char *ptr = CharPtrFromSPtr(lParam);
7522 int iPlace = 0;
7523 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
7524 ptr[iPlace++] = pdoc->CharAt(iChar);
7526 return iPlace;
7529 case SCI_GETLINECOUNT:
7530 if (pdoc->LinesTotal() == 0)
7531 return 1;
7532 else
7533 return pdoc->LinesTotal();
7535 case SCI_GETMODIFY:
7536 return !pdoc->IsSavePoint();
7538 case SCI_SETSEL: {
7539 int nStart = static_cast<int>(wParam);
7540 int nEnd = static_cast<int>(lParam);
7541 if (nEnd < 0)
7542 nEnd = pdoc->Length();
7543 if (nStart < 0)
7544 nStart = nEnd; // Remove selection
7545 InvalidateSelection(SelectionRange(nStart, nEnd));
7546 sel.Clear();
7547 sel.selType = Selection::selStream;
7548 SetSelection(nEnd, nStart);
7549 EnsureCaretVisible();
7551 break;
7553 case SCI_GETSELTEXT: {
7554 SelectionText selectedText;
7555 CopySelectionRange(&selectedText);
7556 if (lParam == 0) {
7557 return selectedText.LengthWithTerminator();
7558 } else {
7559 char *ptr = CharPtrFromSPtr(lParam);
7560 unsigned int iChar = 0;
7561 if (selectedText.Length()) {
7562 for (; iChar < selectedText.LengthWithTerminator(); iChar++)
7563 ptr[iChar] = selectedText.Data()[iChar];
7564 } else {
7565 ptr[0] = '\0';
7567 return iChar;
7571 case SCI_LINEFROMPOSITION:
7572 if (static_cast<int>(wParam) < 0)
7573 return 0;
7574 return pdoc->LineFromPosition(wParam);
7576 case SCI_POSITIONFROMLINE:
7577 if (static_cast<int>(wParam) < 0)
7578 wParam = pdoc->LineFromPosition(SelectionStart().Position());
7579 if (wParam == 0)
7580 return 0; // Even if there is no text, there is a first line that starts at 0
7581 if (static_cast<int>(wParam) > pdoc->LinesTotal())
7582 return -1;
7583 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
7584 // return -1;
7585 return pdoc->LineStart(wParam);
7587 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
7588 case SCI_LINELENGTH:
7589 if ((static_cast<int>(wParam) < 0) ||
7590 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
7591 return 0;
7592 return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
7594 case SCI_REPLACESEL: {
7595 if (lParam == 0)
7596 return 0;
7597 UndoGroup ug(pdoc);
7598 ClearSelection();
7599 char *replacement = CharPtrFromSPtr(lParam);
7600 pdoc->InsertCString(sel.MainCaret(), replacement);
7601 SetEmptySelection(sel.MainCaret() + istrlen(replacement));
7602 EnsureCaretVisible();
7604 break;
7606 case SCI_SETTARGETSTART:
7607 targetStart = wParam;
7608 break;
7610 case SCI_GETTARGETSTART:
7611 return targetStart;
7613 case SCI_SETTARGETEND:
7614 targetEnd = wParam;
7615 break;
7617 case SCI_GETTARGETEND:
7618 return targetEnd;
7620 case SCI_TARGETFROMSELECTION:
7621 if (sel.MainCaret() < sel.MainAnchor()) {
7622 targetStart = sel.MainCaret();
7623 targetEnd = sel.MainAnchor();
7624 } else {
7625 targetStart = sel.MainAnchor();
7626 targetEnd = sel.MainCaret();
7628 break;
7630 case SCI_REPLACETARGET:
7631 PLATFORM_ASSERT(lParam);
7632 return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam);
7634 case SCI_REPLACETARGETRE:
7635 PLATFORM_ASSERT(lParam);
7636 return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam);
7638 case SCI_SEARCHINTARGET:
7639 PLATFORM_ASSERT(lParam);
7640 return SearchInTarget(CharPtrFromSPtr(lParam), wParam);
7642 case SCI_SETSEARCHFLAGS:
7643 searchFlags = wParam;
7644 break;
7646 case SCI_GETSEARCHFLAGS:
7647 return searchFlags;
7649 case SCI_GETTAG:
7650 return GetTag(CharPtrFromSPtr(lParam), wParam);
7652 case SCI_POSITIONBEFORE:
7653 return pdoc->MovePositionOutsideChar(wParam - 1, -1, true);
7655 case SCI_POSITIONAFTER:
7656 return pdoc->MovePositionOutsideChar(wParam + 1, 1, true);
7658 case SCI_POSITIONRELATIVE:
7659 return Platform::Clamp(pdoc->GetRelativePosition(wParam, lParam), 0, pdoc->Length());
7661 case SCI_LINESCROLL:
7662 ScrollTo(topLine + lParam);
7663 HorizontalScrollTo(xOffset + static_cast<int>(wParam) * vs.spaceWidth);
7664 return 1;
7666 case SCI_SETXOFFSET:
7667 xOffset = wParam;
7668 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7669 SetHorizontalScrollPos();
7670 Redraw();
7671 break;
7673 case SCI_GETXOFFSET:
7674 return xOffset;
7676 case SCI_CHOOSECARETX:
7677 SetLastXChosen();
7678 break;
7680 case SCI_SCROLLCARET:
7681 EnsureCaretVisible();
7682 break;
7684 case SCI_SETREADONLY:
7685 pdoc->SetReadOnly(wParam != 0);
7686 return 1;
7688 case SCI_GETREADONLY:
7689 return pdoc->IsReadOnly();
7691 case SCI_CANPASTE:
7692 return CanPaste();
7694 case SCI_POINTXFROMPOSITION:
7695 if (lParam < 0) {
7696 return 0;
7697 } else {
7698 Point pt = LocationFromPosition(lParam);
7699 // Convert to view-relative
7700 return pt.x - vs.textStart + vs.fixedColumnWidth;
7703 case SCI_POINTYFROMPOSITION:
7704 if (lParam < 0) {
7705 return 0;
7706 } else {
7707 Point pt = LocationFromPosition(lParam);
7708 return pt.y;
7711 case SCI_FINDTEXT:
7712 return FindText(wParam, lParam);
7714 case SCI_GETTEXTRANGE: {
7715 if (lParam == 0)
7716 return 0;
7717 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7718 int cpMax = tr->chrg.cpMax;
7719 if (cpMax == -1)
7720 cpMax = pdoc->Length();
7721 PLATFORM_ASSERT(cpMax <= pdoc->Length());
7722 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
7723 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
7724 // Spec says copied text is terminated with a NUL
7725 tr->lpstrText[len] = '\0';
7726 return len; // Not including NUL
7729 case SCI_HIDESELECTION:
7730 hideSelection = wParam != 0;
7731 Redraw();
7732 break;
7734 case SCI_FORMATRANGE:
7735 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
7737 case SCI_GETMARGINLEFT:
7738 return vs.leftMarginWidth;
7740 case SCI_GETMARGINRIGHT:
7741 return vs.rightMarginWidth;
7743 case SCI_SETMARGINLEFT:
7744 lastXChosen += lParam - vs.leftMarginWidth;
7745 vs.leftMarginWidth = lParam;
7746 InvalidateStyleRedraw();
7747 break;
7749 case SCI_SETMARGINRIGHT:
7750 vs.rightMarginWidth = lParam;
7751 InvalidateStyleRedraw();
7752 break;
7754 // Control specific mesages
7756 case SCI_ADDTEXT: {
7757 if (lParam == 0)
7758 return 0;
7759 pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam);
7760 SetEmptySelection(sel.MainCaret() + wParam);
7761 return 0;
7764 case SCI_ADDSTYLEDTEXT:
7765 if (lParam)
7766 AddStyledText(CharPtrFromSPtr(lParam), wParam);
7767 return 0;
7769 case SCI_INSERTTEXT: {
7770 if (lParam == 0)
7771 return 0;
7772 int insertPos = wParam;
7773 if (static_cast<int>(wParam) == -1)
7774 insertPos = CurrentPosition();
7775 int newCurrent = CurrentPosition();
7776 char *sz = CharPtrFromSPtr(lParam);
7777 pdoc->InsertCString(insertPos, sz);
7778 if (newCurrent > insertPos)
7779 newCurrent += istrlen(sz);
7780 SetEmptySelection(newCurrent);
7781 return 0;
7784 case SCI_APPENDTEXT:
7785 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam);
7786 return 0;
7788 case SCI_CLEARALL:
7789 ClearAll();
7790 return 0;
7792 case SCI_DELETERANGE:
7793 pdoc->DeleteChars(wParam, lParam);
7794 return 0;
7796 case SCI_CLEARDOCUMENTSTYLE:
7797 ClearDocumentStyle();
7798 return 0;
7800 case SCI_SETUNDOCOLLECTION:
7801 pdoc->SetUndoCollection(wParam != 0);
7802 return 0;
7804 case SCI_GETUNDOCOLLECTION:
7805 return pdoc->IsCollectingUndo();
7807 case SCI_BEGINUNDOACTION:
7808 pdoc->BeginUndoAction();
7809 return 0;
7811 case SCI_ENDUNDOACTION:
7812 pdoc->EndUndoAction();
7813 return 0;
7815 case SCI_GETCARETPERIOD:
7816 return caret.period;
7818 case SCI_SETCARETPERIOD:
7819 caret.period = wParam;
7820 break;
7822 case SCI_GETWORDCHARS:
7823 return pdoc->GetCharsOfClass(CharClassify::ccWord, reinterpret_cast<unsigned char *>(lParam));
7825 case SCI_SETWORDCHARS: {
7826 pdoc->SetDefaultCharClasses(false);
7827 if (lParam == 0)
7828 return 0;
7829 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
7831 break;
7833 case SCI_GETWHITESPACECHARS:
7834 return pdoc->GetCharsOfClass(CharClassify::ccSpace, reinterpret_cast<unsigned char *>(lParam));
7836 case SCI_SETWHITESPACECHARS: {
7837 if (lParam == 0)
7838 return 0;
7839 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
7841 break;
7843 case SCI_GETPUNCTUATIONCHARS:
7844 return pdoc->GetCharsOfClass(CharClassify::ccPunctuation, reinterpret_cast<unsigned char *>(lParam));
7846 case SCI_SETPUNCTUATIONCHARS: {
7847 if (lParam == 0)
7848 return 0;
7849 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccPunctuation);
7851 break;
7853 case SCI_SETCHARSDEFAULT:
7854 pdoc->SetDefaultCharClasses(true);
7855 break;
7857 case SCI_GETLENGTH:
7858 return pdoc->Length();
7860 case SCI_ALLOCATE:
7861 pdoc->Allocate(wParam);
7862 break;
7864 case SCI_GETCHARAT:
7865 return pdoc->CharAt(wParam);
7867 case SCI_SETCURRENTPOS:
7868 if (sel.IsRectangular()) {
7869 sel.Rectangular().caret.SetPosition(wParam);
7870 SetRectangularRange();
7871 Redraw();
7872 } else {
7873 SetSelection(wParam, sel.MainAnchor());
7875 break;
7877 case SCI_GETCURRENTPOS:
7878 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
7880 case SCI_SETANCHOR:
7881 if (sel.IsRectangular()) {
7882 sel.Rectangular().anchor.SetPosition(wParam);
7883 SetRectangularRange();
7884 Redraw();
7885 } else {
7886 SetSelection(sel.MainCaret(), wParam);
7888 break;
7890 case SCI_GETANCHOR:
7891 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
7893 case SCI_SETSELECTIONSTART:
7894 SetSelection(Platform::Maximum(sel.MainCaret(), wParam), wParam);
7895 break;
7897 case SCI_GETSELECTIONSTART:
7898 return sel.LimitsForRectangularElseMain().start.Position();
7900 case SCI_SETSELECTIONEND:
7901 SetSelection(wParam, Platform::Minimum(sel.MainAnchor(), wParam));
7902 break;
7904 case SCI_GETSELECTIONEND:
7905 return sel.LimitsForRectangularElseMain().end.Position();
7907 case SCI_SETEMPTYSELECTION:
7908 SetEmptySelection(wParam);
7909 break;
7911 case SCI_SETPRINTMAGNIFICATION:
7912 printParameters.magnification = wParam;
7913 break;
7915 case SCI_GETPRINTMAGNIFICATION:
7916 return printParameters.magnification;
7918 case SCI_SETPRINTCOLOURMODE:
7919 printParameters.colourMode = wParam;
7920 break;
7922 case SCI_GETPRINTCOLOURMODE:
7923 return printParameters.colourMode;
7925 case SCI_SETPRINTWRAPMODE:
7926 printParameters.wrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
7927 break;
7929 case SCI_GETPRINTWRAPMODE:
7930 return printParameters.wrapState;
7932 case SCI_GETSTYLEAT:
7933 if (static_cast<int>(wParam) >= pdoc->Length())
7934 return 0;
7935 else
7936 return pdoc->StyleAt(wParam);
7938 case SCI_REDO:
7939 Redo();
7940 break;
7942 case SCI_SELECTALL:
7943 SelectAll();
7944 break;
7946 case SCI_SETSAVEPOINT:
7947 pdoc->SetSavePoint();
7948 break;
7950 case SCI_GETSTYLEDTEXT: {
7951 if (lParam == 0)
7952 return 0;
7953 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7954 int iPlace = 0;
7955 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
7956 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
7957 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
7959 tr->lpstrText[iPlace] = '\0';
7960 tr->lpstrText[iPlace + 1] = '\0';
7961 return iPlace;
7964 case SCI_CANREDO:
7965 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
7967 case SCI_MARKERLINEFROMHANDLE:
7968 return pdoc->LineFromHandle(wParam);
7970 case SCI_MARKERDELETEHANDLE:
7971 pdoc->DeleteMarkFromHandle(wParam);
7972 break;
7974 case SCI_GETVIEWWS:
7975 return vs.viewWhitespace;
7977 case SCI_SETVIEWWS:
7978 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
7979 Redraw();
7980 break;
7982 case SCI_GETWHITESPACESIZE:
7983 return vs.whitespaceSize;
7985 case SCI_SETWHITESPACESIZE:
7986 vs.whitespaceSize = static_cast<int>(wParam);
7987 Redraw();
7988 break;
7990 case SCI_POSITIONFROMPOINT:
7991 return PositionFromLocation(Point(wParam - vs.ExternalMarginWidth(), lParam),
7992 false, false);
7994 case SCI_POSITIONFROMPOINTCLOSE:
7995 return PositionFromLocation(Point(wParam - vs.ExternalMarginWidth(), lParam),
7996 true, false);
7998 case SCI_CHARPOSITIONFROMPOINT:
7999 return PositionFromLocation(Point(wParam - vs.ExternalMarginWidth(), lParam),
8000 false, true);
8002 case SCI_CHARPOSITIONFROMPOINTCLOSE:
8003 return PositionFromLocation(Point(wParam - vs.ExternalMarginWidth(), lParam),
8004 true, true);
8006 case SCI_GOTOLINE:
8007 GoToLine(wParam);
8008 break;
8010 case SCI_GOTOPOS:
8011 SetEmptySelection(wParam);
8012 EnsureCaretVisible();
8013 break;
8015 case SCI_GETCURLINE: {
8016 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
8017 int lineStart = pdoc->LineStart(lineCurrentPos);
8018 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
8019 if (lParam == 0) {
8020 return 1 + lineEnd - lineStart;
8022 PLATFORM_ASSERT(wParam > 0);
8023 char *ptr = CharPtrFromSPtr(lParam);
8024 unsigned int iPlace = 0;
8025 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
8026 ptr[iPlace++] = pdoc->CharAt(iChar);
8028 ptr[iPlace] = '\0';
8029 return sel.MainCaret() - lineStart;
8032 case SCI_GETENDSTYLED:
8033 return pdoc->GetEndStyled();
8035 case SCI_GETEOLMODE:
8036 return pdoc->eolMode;
8038 case SCI_SETEOLMODE:
8039 pdoc->eolMode = wParam;
8040 break;
8042 case SCI_SETLINEENDTYPESALLOWED:
8043 if (pdoc->SetLineEndTypesAllowed(wParam)) {
8044 cs.Clear();
8045 cs.InsertLines(0, pdoc->LinesTotal() - 1);
8046 SetAnnotationHeights(0, pdoc->LinesTotal());
8047 InvalidateStyleRedraw();
8049 break;
8051 case SCI_GETLINEENDTYPESALLOWED:
8052 return pdoc->GetLineEndTypesAllowed();
8054 case SCI_GETLINEENDTYPESACTIVE:
8055 return pdoc->GetLineEndTypesActive();
8057 case SCI_STARTSTYLING:
8058 pdoc->StartStyling(wParam, static_cast<char>(lParam));
8059 break;
8061 case SCI_SETSTYLING:
8062 pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
8063 break;
8065 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
8066 if (lParam == 0)
8067 return 0;
8068 pdoc->SetStyles(wParam, CharPtrFromSPtr(lParam));
8069 break;
8071 case SCI_SETBUFFEREDDRAW:
8072 bufferedDraw = wParam != 0;
8073 break;
8075 case SCI_GETBUFFEREDDRAW:
8076 return bufferedDraw;
8078 case SCI_GETTWOPHASEDRAW:
8079 return twoPhaseDraw;
8081 case SCI_SETTWOPHASEDRAW:
8082 twoPhaseDraw = wParam != 0;
8083 InvalidateStyleRedraw();
8084 break;
8086 case SCI_SETFONTQUALITY:
8087 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
8088 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
8089 InvalidateStyleRedraw();
8090 break;
8092 case SCI_GETFONTQUALITY:
8093 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
8095 case SCI_SETTABWIDTH:
8096 if (wParam > 0) {
8097 pdoc->tabInChars = wParam;
8098 if (pdoc->indentInChars == 0)
8099 pdoc->actualIndentInChars = pdoc->tabInChars;
8101 InvalidateStyleRedraw();
8102 break;
8104 case SCI_GETTABWIDTH:
8105 return pdoc->tabInChars;
8107 case SCI_SETINDENT:
8108 pdoc->indentInChars = wParam;
8109 if (pdoc->indentInChars != 0)
8110 pdoc->actualIndentInChars = pdoc->indentInChars;
8111 else
8112 pdoc->actualIndentInChars = pdoc->tabInChars;
8113 InvalidateStyleRedraw();
8114 break;
8116 case SCI_GETINDENT:
8117 return pdoc->indentInChars;
8119 case SCI_SETUSETABS:
8120 pdoc->useTabs = wParam != 0;
8121 InvalidateStyleRedraw();
8122 break;
8124 case SCI_GETUSETABS:
8125 return pdoc->useTabs;
8127 case SCI_SETLINEINDENTATION:
8128 pdoc->SetLineIndentation(wParam, lParam);
8129 break;
8131 case SCI_GETLINEINDENTATION:
8132 return pdoc->GetLineIndentation(wParam);
8134 case SCI_GETLINEINDENTPOSITION:
8135 return pdoc->GetLineIndentPosition(wParam);
8137 case SCI_SETTABINDENTS:
8138 pdoc->tabIndents = wParam != 0;
8139 break;
8141 case SCI_GETTABINDENTS:
8142 return pdoc->tabIndents;
8144 case SCI_SETBACKSPACEUNINDENTS:
8145 pdoc->backspaceUnindents = wParam != 0;
8146 break;
8148 case SCI_GETBACKSPACEUNINDENTS:
8149 return pdoc->backspaceUnindents;
8151 case SCI_SETMOUSEDWELLTIME:
8152 dwellDelay = wParam;
8153 ticksToDwell = dwellDelay;
8154 break;
8156 case SCI_GETMOUSEDWELLTIME:
8157 return dwellDelay;
8159 case SCI_WORDSTARTPOSITION:
8160 return pdoc->ExtendWordSelect(wParam, -1, lParam != 0);
8162 case SCI_WORDENDPOSITION:
8163 return pdoc->ExtendWordSelect(wParam, 1, lParam != 0);
8165 case SCI_SETWRAPMODE:
8166 if (vs.SetWrapState(wParam)) {
8167 xOffset = 0;
8168 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
8169 InvalidateStyleRedraw();
8170 ReconfigureScrollBars();
8172 break;
8174 case SCI_GETWRAPMODE:
8175 return vs.wrapState;
8177 case SCI_SETWRAPVISUALFLAGS:
8178 if (vs.SetWrapVisualFlags(wParam)) {
8179 InvalidateStyleRedraw();
8180 ReconfigureScrollBars();
8182 break;
8184 case SCI_GETWRAPVISUALFLAGS:
8185 return vs.wrapVisualFlags;
8187 case SCI_SETWRAPVISUALFLAGSLOCATION:
8188 if (vs.SetWrapVisualFlagsLocation(wParam)) {
8189 InvalidateStyleRedraw();
8191 break;
8193 case SCI_GETWRAPVISUALFLAGSLOCATION:
8194 return vs.wrapVisualFlagsLocation;
8196 case SCI_SETWRAPSTARTINDENT:
8197 if (vs.SetWrapVisualStartIndent(wParam)) {
8198 InvalidateStyleRedraw();
8199 ReconfigureScrollBars();
8201 break;
8203 case SCI_GETWRAPSTARTINDENT:
8204 return vs.wrapVisualStartIndent;
8206 case SCI_SETWRAPINDENTMODE:
8207 if (vs.SetWrapIndentMode(wParam)) {
8208 InvalidateStyleRedraw();
8209 ReconfigureScrollBars();
8211 break;
8213 case SCI_GETWRAPINDENTMODE:
8214 return vs.wrapIndentMode;
8216 case SCI_SETLAYOUTCACHE:
8217 llc.SetLevel(wParam);
8218 break;
8220 case SCI_GETLAYOUTCACHE:
8221 return llc.GetLevel();
8223 case SCI_SETPOSITIONCACHE:
8224 posCache.SetSize(wParam);
8225 break;
8227 case SCI_GETPOSITIONCACHE:
8228 return posCache.GetSize();
8230 case SCI_SETSCROLLWIDTH:
8231 PLATFORM_ASSERT(wParam > 0);
8232 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
8233 lineWidthMaxSeen = 0;
8234 scrollWidth = wParam;
8235 SetScrollBars();
8237 break;
8239 case SCI_GETSCROLLWIDTH:
8240 return scrollWidth;
8242 case SCI_SETSCROLLWIDTHTRACKING:
8243 trackLineWidth = wParam != 0;
8244 break;
8246 case SCI_GETSCROLLWIDTHTRACKING:
8247 return trackLineWidth;
8249 case SCI_LINESJOIN:
8250 LinesJoin();
8251 break;
8253 case SCI_LINESSPLIT:
8254 LinesSplit(wParam);
8255 break;
8257 case SCI_TEXTWIDTH:
8258 PLATFORM_ASSERT(wParam < vs.styles.size());
8259 PLATFORM_ASSERT(lParam);
8260 return TextWidth(wParam, CharPtrFromSPtr(lParam));
8262 case SCI_TEXTHEIGHT:
8263 return vs.lineHeight;
8265 case SCI_SETENDATLASTLINE:
8266 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
8267 if (endAtLastLine != (wParam != 0)) {
8268 endAtLastLine = wParam != 0;
8269 SetScrollBars();
8271 break;
8273 case SCI_GETENDATLASTLINE:
8274 return endAtLastLine;
8276 case SCI_SETCARETSTICKY:
8277 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
8278 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
8279 caretSticky = wParam;
8281 break;
8283 case SCI_GETCARETSTICKY:
8284 return caretSticky;
8286 case SCI_TOGGLECARETSTICKY:
8287 caretSticky = !caretSticky;
8288 break;
8290 case SCI_GETCOLUMN:
8291 return pdoc->GetColumn(wParam);
8293 case SCI_FINDCOLUMN:
8294 return pdoc->FindColumn(wParam, lParam);
8296 case SCI_SETHSCROLLBAR :
8297 if (horizontalScrollBarVisible != (wParam != 0)) {
8298 horizontalScrollBarVisible = wParam != 0;
8299 SetScrollBars();
8300 ReconfigureScrollBars();
8302 break;
8304 case SCI_GETHSCROLLBAR:
8305 return horizontalScrollBarVisible;
8307 case SCI_SETVSCROLLBAR:
8308 if (verticalScrollBarVisible != (wParam != 0)) {
8309 verticalScrollBarVisible = wParam != 0;
8310 SetScrollBars();
8311 ReconfigureScrollBars();
8312 if (verticalScrollBarVisible)
8313 SetVerticalScrollPos();
8315 break;
8317 case SCI_GETVSCROLLBAR:
8318 return verticalScrollBarVisible;
8320 case SCI_SETINDENTATIONGUIDES:
8321 vs.viewIndentationGuides = IndentView(wParam);
8322 Redraw();
8323 break;
8325 case SCI_GETINDENTATIONGUIDES:
8326 return vs.viewIndentationGuides;
8328 case SCI_SETHIGHLIGHTGUIDE:
8329 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
8330 highlightGuideColumn = wParam;
8331 Redraw();
8333 break;
8335 case SCI_GETHIGHLIGHTGUIDE:
8336 return highlightGuideColumn;
8338 case SCI_GETLINEENDPOSITION:
8339 return pdoc->LineEnd(wParam);
8341 case SCI_SETCODEPAGE:
8342 if (ValidCodePage(wParam)) {
8343 if (pdoc->SetDBCSCodePage(wParam)) {
8344 cs.Clear();
8345 cs.InsertLines(0, pdoc->LinesTotal() - 1);
8346 SetAnnotationHeights(0, pdoc->LinesTotal());
8347 InvalidateStyleRedraw();
8348 SetRepresentations();
8351 break;
8353 case SCI_GETCODEPAGE:
8354 return pdoc->dbcsCodePage;
8356 #ifdef INCLUDE_DEPRECATED_FEATURES
8357 case SCI_SETUSEPALETTE:
8358 InvalidateStyleRedraw();
8359 break;
8361 case SCI_GETUSEPALETTE:
8362 return 0;
8363 #endif
8365 // Marker definition and setting
8366 case SCI_MARKERDEFINE:
8367 if (wParam <= MARKER_MAX) {
8368 vs.markers[wParam].markType = lParam;
8369 vs.CalcLargestMarkerHeight();
8371 InvalidateStyleData();
8372 RedrawSelMargin();
8373 break;
8375 case SCI_MARKERSYMBOLDEFINED:
8376 if (wParam <= MARKER_MAX)
8377 return vs.markers[wParam].markType;
8378 else
8379 return 0;
8381 case SCI_MARKERSETFORE:
8382 if (wParam <= MARKER_MAX)
8383 vs.markers[wParam].fore = ColourDesired(lParam);
8384 InvalidateStyleData();
8385 RedrawSelMargin();
8386 break;
8387 case SCI_MARKERSETBACKSELECTED:
8388 if (wParam <= MARKER_MAX)
8389 vs.markers[wParam].backSelected = ColourDesired(lParam);
8390 InvalidateStyleData();
8391 RedrawSelMargin();
8392 break;
8393 case SCI_MARKERENABLEHIGHLIGHT:
8394 highlightDelimiter.isEnabled = wParam == 1;
8395 RedrawSelMargin();
8396 break;
8397 case SCI_MARKERSETBACK:
8398 if (wParam <= MARKER_MAX)
8399 vs.markers[wParam].back = ColourDesired(lParam);
8400 InvalidateStyleData();
8401 RedrawSelMargin();
8402 break;
8403 case SCI_MARKERSETALPHA:
8404 if (wParam <= MARKER_MAX)
8405 vs.markers[wParam].alpha = lParam;
8406 InvalidateStyleRedraw();
8407 break;
8408 case SCI_MARKERADD: {
8409 int markerID = pdoc->AddMark(wParam, lParam);
8410 return markerID;
8412 case SCI_MARKERADDSET:
8413 if (lParam != 0)
8414 pdoc->AddMarkSet(wParam, lParam);
8415 break;
8417 case SCI_MARKERDELETE:
8418 pdoc->DeleteMark(wParam, lParam);
8419 break;
8421 case SCI_MARKERDELETEALL:
8422 pdoc->DeleteAllMarks(static_cast<int>(wParam));
8423 break;
8425 case SCI_MARKERGET:
8426 return pdoc->GetMark(wParam);
8428 case SCI_MARKERNEXT:
8429 return pdoc->MarkerNext(wParam, lParam);
8431 case SCI_MARKERPREVIOUS: {
8432 for (int iLine = wParam; iLine >= 0; iLine--) {
8433 if ((pdoc->GetMark(iLine) & lParam) != 0)
8434 return iLine;
8437 return -1;
8439 case SCI_MARKERDEFINEPIXMAP:
8440 if (wParam <= MARKER_MAX) {
8441 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
8442 vs.CalcLargestMarkerHeight();
8444 InvalidateStyleData();
8445 RedrawSelMargin();
8446 break;
8448 case SCI_RGBAIMAGESETWIDTH:
8449 sizeRGBAImage.x = wParam;
8450 break;
8452 case SCI_RGBAIMAGESETHEIGHT:
8453 sizeRGBAImage.y = wParam;
8454 break;
8456 case SCI_RGBAIMAGESETSCALE:
8457 scaleRGBAImage = wParam;
8458 break;
8460 case SCI_MARKERDEFINERGBAIMAGE:
8461 if (wParam <= MARKER_MAX) {
8462 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0, reinterpret_cast<unsigned char *>(lParam));
8463 vs.CalcLargestMarkerHeight();
8465 InvalidateStyleData();
8466 RedrawSelMargin();
8467 break;
8469 case SCI_SETMARGINTYPEN:
8470 if (ValidMargin(wParam)) {
8471 vs.ms[wParam].style = lParam;
8472 InvalidateStyleRedraw();
8474 break;
8476 case SCI_GETMARGINTYPEN:
8477 if (ValidMargin(wParam))
8478 return vs.ms[wParam].style;
8479 else
8480 return 0;
8482 case SCI_SETMARGINWIDTHN:
8483 if (ValidMargin(wParam)) {
8484 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
8485 if (vs.ms[wParam].width != lParam) {
8486 lastXChosen += lParam - vs.ms[wParam].width;
8487 vs.ms[wParam].width = lParam;
8488 InvalidateStyleRedraw();
8491 break;
8493 case SCI_GETMARGINWIDTHN:
8494 if (ValidMargin(wParam))
8495 return vs.ms[wParam].width;
8496 else
8497 return 0;
8499 case SCI_SETMARGINMASKN:
8500 if (ValidMargin(wParam)) {
8501 vs.ms[wParam].mask = lParam;
8502 InvalidateStyleRedraw();
8504 break;
8506 case SCI_GETMARGINMASKN:
8507 if (ValidMargin(wParam))
8508 return vs.ms[wParam].mask;
8509 else
8510 return 0;
8512 case SCI_SETMARGINSENSITIVEN:
8513 if (ValidMargin(wParam)) {
8514 vs.ms[wParam].sensitive = lParam != 0;
8515 InvalidateStyleRedraw();
8517 break;
8519 case SCI_GETMARGINSENSITIVEN:
8520 if (ValidMargin(wParam))
8521 return vs.ms[wParam].sensitive ? 1 : 0;
8522 else
8523 return 0;
8525 case SCI_SETMARGINCURSORN:
8526 if (ValidMargin(wParam))
8527 vs.ms[wParam].cursor = lParam;
8528 break;
8530 case SCI_GETMARGINCURSORN:
8531 if (ValidMargin(wParam))
8532 return vs.ms[wParam].cursor;
8533 else
8534 return 0;
8536 case SCI_STYLECLEARALL:
8537 vs.ClearStyles();
8538 InvalidateStyleRedraw();
8539 break;
8541 case SCI_STYLESETFORE:
8542 case SCI_STYLESETBACK:
8543 case SCI_STYLESETBOLD:
8544 case SCI_STYLESETWEIGHT:
8545 case SCI_STYLESETITALIC:
8546 case SCI_STYLESETEOLFILLED:
8547 case SCI_STYLESETSIZE:
8548 case SCI_STYLESETSIZEFRACTIONAL:
8549 case SCI_STYLESETFONT:
8550 case SCI_STYLESETUNDERLINE:
8551 case SCI_STYLESETCASE:
8552 case SCI_STYLESETCHARACTERSET:
8553 case SCI_STYLESETVISIBLE:
8554 case SCI_STYLESETCHANGEABLE:
8555 case SCI_STYLESETHOTSPOT:
8556 StyleSetMessage(iMessage, wParam, lParam);
8557 break;
8559 case SCI_STYLEGETFORE:
8560 case SCI_STYLEGETBACK:
8561 case SCI_STYLEGETBOLD:
8562 case SCI_STYLEGETWEIGHT:
8563 case SCI_STYLEGETITALIC:
8564 case SCI_STYLEGETEOLFILLED:
8565 case SCI_STYLEGETSIZE:
8566 case SCI_STYLEGETSIZEFRACTIONAL:
8567 case SCI_STYLEGETFONT:
8568 case SCI_STYLEGETUNDERLINE:
8569 case SCI_STYLEGETCASE:
8570 case SCI_STYLEGETCHARACTERSET:
8571 case SCI_STYLEGETVISIBLE:
8572 case SCI_STYLEGETCHANGEABLE:
8573 case SCI_STYLEGETHOTSPOT:
8574 return StyleGetMessage(iMessage, wParam, lParam);
8576 case SCI_STYLERESETDEFAULT:
8577 vs.ResetDefaultStyle();
8578 InvalidateStyleRedraw();
8579 break;
8580 case SCI_SETSTYLEBITS:
8581 vs.EnsureStyle((1 << wParam) - 1);
8582 pdoc->SetStylingBits(wParam);
8583 break;
8585 case SCI_GETSTYLEBITS:
8586 return pdoc->stylingBits;
8588 case SCI_SETLINESTATE:
8589 return pdoc->SetLineState(wParam, lParam);
8591 case SCI_GETLINESTATE:
8592 return pdoc->GetLineState(wParam);
8594 case SCI_GETMAXLINESTATE:
8595 return pdoc->GetMaxLineState();
8597 case SCI_GETCARETLINEVISIBLE:
8598 return vs.showCaretLineBackground;
8599 case SCI_SETCARETLINEVISIBLE:
8600 vs.showCaretLineBackground = wParam != 0;
8601 InvalidateStyleRedraw();
8602 break;
8603 case SCI_GETCARETLINEVISIBLEALWAYS:
8604 return vs.alwaysShowCaretLineBackground;
8605 case SCI_SETCARETLINEVISIBLEALWAYS:
8606 vs.alwaysShowCaretLineBackground = wParam != 0;
8607 InvalidateStyleRedraw();
8608 break;
8610 case SCI_GETCARETLINEBACK:
8611 return vs.caretLineBackground.AsLong();
8612 case SCI_SETCARETLINEBACK:
8613 vs.caretLineBackground = wParam;
8614 InvalidateStyleRedraw();
8615 break;
8616 case SCI_GETCARETLINEBACKALPHA:
8617 return vs.caretLineAlpha;
8618 case SCI_SETCARETLINEBACKALPHA:
8619 vs.caretLineAlpha = wParam;
8620 InvalidateStyleRedraw();
8621 break;
8623 // Folding messages
8625 case SCI_VISIBLEFROMDOCLINE:
8626 return cs.DisplayFromDoc(wParam);
8628 case SCI_DOCLINEFROMVISIBLE:
8629 return cs.DocFromDisplay(wParam);
8631 case SCI_WRAPCOUNT:
8632 return WrapCount(wParam);
8634 case SCI_SETFOLDLEVEL: {
8635 int prev = pdoc->SetLevel(wParam, lParam);
8636 if (prev != lParam)
8637 RedrawSelMargin();
8638 return prev;
8641 case SCI_GETFOLDLEVEL:
8642 return pdoc->GetLevel(wParam);
8644 case SCI_GETLASTCHILD:
8645 return pdoc->GetLastChild(wParam, lParam);
8647 case SCI_GETFOLDPARENT:
8648 return pdoc->GetFoldParent(wParam);
8650 case SCI_SHOWLINES:
8651 cs.SetVisible(wParam, lParam, true);
8652 SetScrollBars();
8653 Redraw();
8654 break;
8656 case SCI_HIDELINES:
8657 if (wParam > 0)
8658 cs.SetVisible(wParam, lParam, false);
8659 SetScrollBars();
8660 Redraw();
8661 break;
8663 case SCI_GETLINEVISIBLE:
8664 return cs.GetVisible(wParam);
8666 case SCI_GETALLLINESVISIBLE:
8667 return cs.HiddenLines() ? 0 : 1;
8669 case SCI_SETFOLDEXPANDED:
8670 SetFoldExpanded(wParam, lParam != 0);
8671 break;
8673 case SCI_GETFOLDEXPANDED:
8674 return cs.GetExpanded(wParam);
8676 case SCI_SETAUTOMATICFOLD:
8677 foldAutomatic = wParam;
8678 break;
8680 case SCI_GETAUTOMATICFOLD:
8681 return foldAutomatic;
8683 case SCI_SETFOLDFLAGS:
8684 foldFlags = wParam;
8685 Redraw();
8686 break;
8688 case SCI_TOGGLEFOLD:
8689 FoldLine(wParam, SC_FOLDACTION_TOGGLE);
8690 break;
8692 case SCI_FOLDLINE:
8693 FoldLine(wParam, lParam);
8694 break;
8696 case SCI_FOLDCHILDREN:
8697 FoldExpand(wParam, lParam, pdoc->GetLevel(wParam));
8698 break;
8700 case SCI_FOLDALL:
8701 FoldAll(wParam);
8702 break;
8704 case SCI_EXPANDCHILDREN:
8705 FoldExpand(wParam, SC_FOLDACTION_EXPAND, lParam);
8706 break;
8708 case SCI_CONTRACTEDFOLDNEXT:
8709 return ContractedFoldNext(wParam);
8711 case SCI_ENSUREVISIBLE:
8712 EnsureLineVisible(wParam, false);
8713 break;
8715 case SCI_ENSUREVISIBLEENFORCEPOLICY:
8716 EnsureLineVisible(wParam, true);
8717 break;
8719 case SCI_SCROLLRANGE:
8720 ScrollRange(SelectionRange(lParam, wParam));
8721 break;
8723 case SCI_SEARCHANCHOR:
8724 SearchAnchor();
8725 break;
8727 case SCI_SEARCHNEXT:
8728 case SCI_SEARCHPREV:
8729 return SearchText(iMessage, wParam, lParam);
8731 case SCI_SETXCARETPOLICY:
8732 caretXPolicy = wParam;
8733 caretXSlop = lParam;
8734 break;
8736 case SCI_SETYCARETPOLICY:
8737 caretYPolicy = wParam;
8738 caretYSlop = lParam;
8739 break;
8741 case SCI_SETVISIBLEPOLICY:
8742 visiblePolicy = wParam;
8743 visibleSlop = lParam;
8744 break;
8746 case SCI_LINESONSCREEN:
8747 return LinesOnScreen();
8749 case SCI_SETSELFORE:
8750 vs.selColours.fore = ColourOptional(wParam, lParam);
8751 vs.selAdditionalForeground = ColourDesired(lParam);
8752 InvalidateStyleRedraw();
8753 break;
8755 case SCI_SETSELBACK:
8756 vs.selColours.back = ColourOptional(wParam, lParam);
8757 vs.selAdditionalBackground = ColourDesired(lParam);
8758 InvalidateStyleRedraw();
8759 break;
8761 case SCI_SETSELALPHA:
8762 vs.selAlpha = wParam;
8763 vs.selAdditionalAlpha = wParam;
8764 InvalidateStyleRedraw();
8765 break;
8767 case SCI_GETSELALPHA:
8768 return vs.selAlpha;
8770 case SCI_GETSELEOLFILLED:
8771 return vs.selEOLFilled;
8773 case SCI_SETSELEOLFILLED:
8774 vs.selEOLFilled = wParam != 0;
8775 InvalidateStyleRedraw();
8776 break;
8778 case SCI_SETWHITESPACEFORE:
8779 vs.whitespaceColours.fore = ColourOptional(wParam, lParam);
8780 InvalidateStyleRedraw();
8781 break;
8783 case SCI_SETWHITESPACEBACK:
8784 vs.whitespaceColours.back = ColourOptional(wParam, lParam);
8785 InvalidateStyleRedraw();
8786 break;
8788 case SCI_SETCARETFORE:
8789 vs.caretcolour = ColourDesired(wParam);
8790 InvalidateStyleRedraw();
8791 break;
8793 case SCI_GETCARETFORE:
8794 return vs.caretcolour.AsLong();
8796 case SCI_SETCARETSTYLE:
8797 if (wParam <= CARETSTYLE_BLOCK)
8798 vs.caretStyle = wParam;
8799 else
8800 /* Default to the line caret */
8801 vs.caretStyle = CARETSTYLE_LINE;
8802 InvalidateStyleRedraw();
8803 break;
8805 case SCI_GETCARETSTYLE:
8806 return vs.caretStyle;
8808 case SCI_SETCARETWIDTH:
8809 if (static_cast<int>(wParam) <= 0)
8810 vs.caretWidth = 0;
8811 else if (wParam >= 3)
8812 vs.caretWidth = 3;
8813 else
8814 vs.caretWidth = wParam;
8815 InvalidateStyleRedraw();
8816 break;
8818 case SCI_GETCARETWIDTH:
8819 return vs.caretWidth;
8821 case SCI_ASSIGNCMDKEY:
8822 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8823 Platform::HighShortFromLong(wParam), lParam);
8824 break;
8826 case SCI_CLEARCMDKEY:
8827 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8828 Platform::HighShortFromLong(wParam), SCI_NULL);
8829 break;
8831 case SCI_CLEARALLCMDKEYS:
8832 kmap.Clear();
8833 break;
8835 case SCI_INDICSETSTYLE:
8836 if (wParam <= INDIC_MAX) {
8837 vs.indicators[wParam].style = lParam;
8838 InvalidateStyleRedraw();
8840 break;
8842 case SCI_INDICGETSTYLE:
8843 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
8845 case SCI_INDICSETFORE:
8846 if (wParam <= INDIC_MAX) {
8847 vs.indicators[wParam].fore = ColourDesired(lParam);
8848 InvalidateStyleRedraw();
8850 break;
8852 case SCI_INDICGETFORE:
8853 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.AsLong() : 0;
8855 case SCI_INDICSETUNDER:
8856 if (wParam <= INDIC_MAX) {
8857 vs.indicators[wParam].under = lParam != 0;
8858 InvalidateStyleRedraw();
8860 break;
8862 case SCI_INDICGETUNDER:
8863 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
8865 case SCI_INDICSETALPHA:
8866 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8867 vs.indicators[wParam].fillAlpha = lParam;
8868 InvalidateStyleRedraw();
8870 break;
8872 case SCI_INDICGETALPHA:
8873 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
8875 case SCI_INDICSETOUTLINEALPHA:
8876 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8877 vs.indicators[wParam].outlineAlpha = lParam;
8878 InvalidateStyleRedraw();
8880 break;
8882 case SCI_INDICGETOUTLINEALPHA:
8883 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
8885 case SCI_SETINDICATORCURRENT:
8886 pdoc->decorations.SetCurrentIndicator(wParam);
8887 break;
8888 case SCI_GETINDICATORCURRENT:
8889 return pdoc->decorations.GetCurrentIndicator();
8890 case SCI_SETINDICATORVALUE:
8891 pdoc->decorations.SetCurrentValue(wParam);
8892 break;
8893 case SCI_GETINDICATORVALUE:
8894 return pdoc->decorations.GetCurrentValue();
8896 case SCI_INDICATORFILLRANGE:
8897 pdoc->DecorationFillRange(wParam, pdoc->decorations.GetCurrentValue(), lParam);
8898 break;
8900 case SCI_INDICATORCLEARRANGE:
8901 pdoc->DecorationFillRange(wParam, 0, lParam);
8902 break;
8904 case SCI_INDICATORALLONFOR:
8905 return pdoc->decorations.AllOnFor(wParam);
8907 case SCI_INDICATORVALUEAT:
8908 return pdoc->decorations.ValueAt(wParam, lParam);
8910 case SCI_INDICATORSTART:
8911 return pdoc->decorations.Start(wParam, lParam);
8913 case SCI_INDICATOREND:
8914 return pdoc->decorations.End(wParam, lParam);
8916 case SCI_LINEDOWN:
8917 case SCI_LINEDOWNEXTEND:
8918 case SCI_PARADOWN:
8919 case SCI_PARADOWNEXTEND:
8920 case SCI_LINEUP:
8921 case SCI_LINEUPEXTEND:
8922 case SCI_PARAUP:
8923 case SCI_PARAUPEXTEND:
8924 case SCI_CHARLEFT:
8925 case SCI_CHARLEFTEXTEND:
8926 case SCI_CHARRIGHT:
8927 case SCI_CHARRIGHTEXTEND:
8928 case SCI_WORDLEFT:
8929 case SCI_WORDLEFTEXTEND:
8930 case SCI_WORDRIGHT:
8931 case SCI_WORDRIGHTEXTEND:
8932 case SCI_WORDLEFTEND:
8933 case SCI_WORDLEFTENDEXTEND:
8934 case SCI_WORDRIGHTEND:
8935 case SCI_WORDRIGHTENDEXTEND:
8936 case SCI_HOME:
8937 case SCI_HOMEEXTEND:
8938 case SCI_LINEEND:
8939 case SCI_LINEENDEXTEND:
8940 case SCI_HOMEWRAP:
8941 case SCI_HOMEWRAPEXTEND:
8942 case SCI_LINEENDWRAP:
8943 case SCI_LINEENDWRAPEXTEND:
8944 case SCI_DOCUMENTSTART:
8945 case SCI_DOCUMENTSTARTEXTEND:
8946 case SCI_DOCUMENTEND:
8947 case SCI_DOCUMENTENDEXTEND:
8948 case SCI_SCROLLTOSTART:
8949 case SCI_SCROLLTOEND:
8951 case SCI_STUTTEREDPAGEUP:
8952 case SCI_STUTTEREDPAGEUPEXTEND:
8953 case SCI_STUTTEREDPAGEDOWN:
8954 case SCI_STUTTEREDPAGEDOWNEXTEND:
8956 case SCI_PAGEUP:
8957 case SCI_PAGEUPEXTEND:
8958 case SCI_PAGEDOWN:
8959 case SCI_PAGEDOWNEXTEND:
8960 case SCI_EDITTOGGLEOVERTYPE:
8961 case SCI_CANCEL:
8962 case SCI_DELETEBACK:
8963 case SCI_TAB:
8964 case SCI_BACKTAB:
8965 case SCI_NEWLINE:
8966 case SCI_FORMFEED:
8967 case SCI_VCHOME:
8968 case SCI_VCHOMEEXTEND:
8969 case SCI_VCHOMEWRAP:
8970 case SCI_VCHOMEWRAPEXTEND:
8971 case SCI_VCHOMEDISPLAY:
8972 case SCI_VCHOMEDISPLAYEXTEND:
8973 case SCI_ZOOMIN:
8974 case SCI_ZOOMOUT:
8975 case SCI_DELWORDLEFT:
8976 case SCI_DELWORDRIGHT:
8977 case SCI_DELWORDRIGHTEND:
8978 case SCI_DELLINELEFT:
8979 case SCI_DELLINERIGHT:
8980 case SCI_LINECOPY:
8981 case SCI_LINECUT:
8982 case SCI_LINEDELETE:
8983 case SCI_LINETRANSPOSE:
8984 case SCI_LINEDUPLICATE:
8985 case SCI_LOWERCASE:
8986 case SCI_UPPERCASE:
8987 case SCI_LINESCROLLDOWN:
8988 case SCI_LINESCROLLUP:
8989 case SCI_WORDPARTLEFT:
8990 case SCI_WORDPARTLEFTEXTEND:
8991 case SCI_WORDPARTRIGHT:
8992 case SCI_WORDPARTRIGHTEXTEND:
8993 case SCI_DELETEBACKNOTLINE:
8994 case SCI_HOMEDISPLAY:
8995 case SCI_HOMEDISPLAYEXTEND:
8996 case SCI_LINEENDDISPLAY:
8997 case SCI_LINEENDDISPLAYEXTEND:
8998 case SCI_LINEDOWNRECTEXTEND:
8999 case SCI_LINEUPRECTEXTEND:
9000 case SCI_CHARLEFTRECTEXTEND:
9001 case SCI_CHARRIGHTRECTEXTEND:
9002 case SCI_HOMERECTEXTEND:
9003 case SCI_VCHOMERECTEXTEND:
9004 case SCI_LINEENDRECTEXTEND:
9005 case SCI_PAGEUPRECTEXTEND:
9006 case SCI_PAGEDOWNRECTEXTEND:
9007 case SCI_SELECTIONDUPLICATE:
9008 return KeyCommand(iMessage);
9010 case SCI_BRACEHIGHLIGHT:
9011 SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
9012 break;
9014 case SCI_BRACEHIGHLIGHTINDICATOR:
9015 if (lParam >= 0 && lParam <= INDIC_MAX) {
9016 vs.braceHighlightIndicatorSet = wParam != 0;
9017 vs.braceHighlightIndicator = lParam;
9019 break;
9021 case SCI_BRACEBADLIGHT:
9022 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
9023 break;
9025 case SCI_BRACEBADLIGHTINDICATOR:
9026 if (lParam >= 0 && lParam <= INDIC_MAX) {
9027 vs.braceBadLightIndicatorSet = wParam != 0;
9028 vs.braceBadLightIndicator = lParam;
9030 break;
9032 case SCI_BRACEMATCH:
9033 // wParam is position of char to find brace for,
9034 // lParam is maximum amount of text to restyle to find it
9035 return pdoc->BraceMatch(wParam, lParam);
9037 case SCI_GETVIEWEOL:
9038 return vs.viewEOL;
9040 case SCI_SETVIEWEOL:
9041 vs.viewEOL = wParam != 0;
9042 InvalidateStyleRedraw();
9043 break;
9045 case SCI_SETZOOM:
9046 vs.zoomLevel = wParam;
9047 InvalidateStyleRedraw();
9048 NotifyZoom();
9049 break;
9051 case SCI_GETZOOM:
9052 return vs.zoomLevel;
9054 case SCI_GETEDGECOLUMN:
9055 return vs.theEdge;
9057 case SCI_SETEDGECOLUMN:
9058 vs.theEdge = wParam;
9059 InvalidateStyleRedraw();
9060 break;
9062 case SCI_GETEDGEMODE:
9063 return vs.edgeState;
9065 case SCI_SETEDGEMODE:
9066 vs.edgeState = wParam;
9067 InvalidateStyleRedraw();
9068 break;
9070 case SCI_GETEDGECOLOUR:
9071 return vs.edgecolour.AsLong();
9073 case SCI_SETEDGECOLOUR:
9074 vs.edgecolour = ColourDesired(wParam);
9075 InvalidateStyleRedraw();
9076 break;
9078 case SCI_GETDOCPOINTER:
9079 return reinterpret_cast<sptr_t>(pdoc);
9081 case SCI_SETDOCPOINTER:
9082 CancelModes();
9083 SetDocPointer(reinterpret_cast<Document *>(lParam));
9084 return 0;
9086 case SCI_CREATEDOCUMENT: {
9087 Document *doc = new Document();
9088 doc->AddRef();
9089 return reinterpret_cast<sptr_t>(doc);
9092 case SCI_ADDREFDOCUMENT:
9093 (reinterpret_cast<Document *>(lParam))->AddRef();
9094 break;
9096 case SCI_RELEASEDOCUMENT:
9097 (reinterpret_cast<Document *>(lParam))->Release();
9098 break;
9100 case SCI_CREATELOADER: {
9101 Document *doc = new Document();
9102 doc->AddRef();
9103 doc->Allocate(wParam);
9104 doc->SetUndoCollection(false);
9105 return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
9108 case SCI_SETMODEVENTMASK:
9109 modEventMask = wParam;
9110 return 0;
9112 case SCI_GETMODEVENTMASK:
9113 return modEventMask;
9115 case SCI_CONVERTEOLS:
9116 pdoc->ConvertLineEnds(wParam);
9117 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
9118 return 0;
9120 case SCI_SETLENGTHFORENCODE:
9121 lengthForEncode = wParam;
9122 return 0;
9124 case SCI_SELECTIONISRECTANGLE:
9125 return sel.selType == Selection::selRectangle ? 1 : 0;
9127 case SCI_SETSELECTIONMODE: {
9128 switch (wParam) {
9129 case SC_SEL_STREAM:
9130 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
9131 sel.selType = Selection::selStream;
9132 break;
9133 case SC_SEL_RECTANGLE:
9134 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
9135 sel.selType = Selection::selRectangle;
9136 break;
9137 case SC_SEL_LINES:
9138 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
9139 sel.selType = Selection::selLines;
9140 break;
9141 case SC_SEL_THIN:
9142 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
9143 sel.selType = Selection::selThin;
9144 break;
9145 default:
9146 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
9147 sel.selType = Selection::selStream;
9149 InvalidateSelection(sel.RangeMain(), true);
9150 break;
9152 case SCI_GETSELECTIONMODE:
9153 switch (sel.selType) {
9154 case Selection::selStream:
9155 return SC_SEL_STREAM;
9156 case Selection::selRectangle:
9157 return SC_SEL_RECTANGLE;
9158 case Selection::selLines:
9159 return SC_SEL_LINES;
9160 case Selection::selThin:
9161 return SC_SEL_THIN;
9162 default: // ?!
9163 return SC_SEL_STREAM;
9165 case SCI_GETLINESELSTARTPOSITION:
9166 case SCI_GETLINESELENDPOSITION: {
9167 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(wParam)),
9168 SelectionPosition(pdoc->LineEnd(wParam)));
9169 for (size_t r=0; r<sel.Count(); r++) {
9170 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
9171 if (portion.start.IsValid()) {
9172 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
9175 return INVALID_POSITION;
9178 case SCI_SETOVERTYPE:
9179 inOverstrike = wParam != 0;
9180 break;
9182 case SCI_GETOVERTYPE:
9183 return inOverstrike ? 1 : 0;
9185 case SCI_SETFOCUS:
9186 SetFocusState(wParam != 0);
9187 break;
9189 case SCI_GETFOCUS:
9190 return hasFocus;
9192 case SCI_SETSTATUS:
9193 errorStatus = wParam;
9194 break;
9196 case SCI_GETSTATUS:
9197 return errorStatus;
9199 case SCI_SETMOUSEDOWNCAPTURES:
9200 mouseDownCaptures = wParam != 0;
9201 break;
9203 case SCI_GETMOUSEDOWNCAPTURES:
9204 return mouseDownCaptures;
9206 case SCI_SETCURSOR:
9207 cursorMode = wParam;
9208 DisplayCursor(Window::cursorText);
9209 break;
9211 case SCI_GETCURSOR:
9212 return cursorMode;
9214 case SCI_SETCONTROLCHARSYMBOL:
9215 vs.controlCharSymbol = wParam;
9216 InvalidateStyleRedraw();
9217 break;
9219 case SCI_GETCONTROLCHARSYMBOL:
9220 return vs.controlCharSymbol;
9222 case SCI_SETREPRESENTATION:
9223 reprs.SetRepresentation(reinterpret_cast<const char *>(wParam), CharPtrFromSPtr(lParam));
9224 break;
9226 case SCI_GETREPRESENTATION: {
9227 Representation *repr = reprs.RepresentationFromCharacter(
9228 reinterpret_cast<const char *>(wParam), UTF8MaxBytes);
9229 if (repr) {
9230 return StringResult(lParam, repr->stringRep.c_str());
9232 return 0;
9235 case SCI_CLEARREPRESENTATION:
9236 reprs.ClearRepresentation(reinterpret_cast<const char *>(wParam));
9237 break;
9239 case SCI_STARTRECORD:
9240 recordingMacro = true;
9241 return 0;
9243 case SCI_STOPRECORD:
9244 recordingMacro = false;
9245 return 0;
9247 case SCI_MOVECARETINSIDEVIEW:
9248 MoveCaretInsideView();
9249 break;
9251 case SCI_SETFOLDMARGINCOLOUR:
9252 vs.foldmarginColour = ColourOptional(wParam, lParam);
9253 InvalidateStyleRedraw();
9254 break;
9256 case SCI_SETFOLDMARGINHICOLOUR:
9257 vs.foldmarginHighlightColour = ColourOptional(wParam, lParam);
9258 InvalidateStyleRedraw();
9259 break;
9261 case SCI_SETHOTSPOTACTIVEFORE:
9262 vs.hotspotColours.fore = ColourOptional(wParam, lParam);
9263 InvalidateStyleRedraw();
9264 break;
9266 case SCI_GETHOTSPOTACTIVEFORE:
9267 return vs.hotspotColours.fore.AsLong();
9269 case SCI_SETHOTSPOTACTIVEBACK:
9270 vs.hotspotColours.back = ColourOptional(wParam, lParam);
9271 InvalidateStyleRedraw();
9272 break;
9274 case SCI_GETHOTSPOTACTIVEBACK:
9275 return vs.hotspotColours.back.AsLong();
9277 case SCI_SETHOTSPOTACTIVEUNDERLINE:
9278 vs.hotspotUnderline = wParam != 0;
9279 InvalidateStyleRedraw();
9280 break;
9282 case SCI_GETHOTSPOTACTIVEUNDERLINE:
9283 return vs.hotspotUnderline ? 1 : 0;
9285 case SCI_SETHOTSPOTSINGLELINE:
9286 vs.hotspotSingleLine = wParam != 0;
9287 InvalidateStyleRedraw();
9288 break;
9290 case SCI_GETHOTSPOTSINGLELINE:
9291 return vs.hotspotSingleLine ? 1 : 0;
9293 case SCI_SETPASTECONVERTENDINGS:
9294 convertPastes = wParam != 0;
9295 break;
9297 case SCI_GETPASTECONVERTENDINGS:
9298 return convertPastes ? 1 : 0;
9300 case SCI_GETCHARACTERPOINTER:
9301 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
9303 case SCI_GETRANGEPOINTER:
9304 return reinterpret_cast<sptr_t>(pdoc->RangePointer(wParam, lParam));
9306 case SCI_GETGAPPOSITION:
9307 return pdoc->GapPosition();
9309 case SCI_SETEXTRAASCENT:
9310 vs.extraAscent = wParam;
9311 InvalidateStyleRedraw();
9312 break;
9314 case SCI_GETEXTRAASCENT:
9315 return vs.extraAscent;
9317 case SCI_SETEXTRADESCENT:
9318 vs.extraDescent = wParam;
9319 InvalidateStyleRedraw();
9320 break;
9322 case SCI_GETEXTRADESCENT:
9323 return vs.extraDescent;
9325 case SCI_MARGINSETSTYLEOFFSET:
9326 vs.marginStyleOffset = wParam;
9327 InvalidateStyleRedraw();
9328 break;
9330 case SCI_MARGINGETSTYLEOFFSET:
9331 return vs.marginStyleOffset;
9333 case SCI_SETMARGINOPTIONS:
9334 marginOptions = wParam;
9335 break;
9337 case SCI_GETMARGINOPTIONS:
9338 return marginOptions;
9340 case SCI_MARGINSETTEXT:
9341 pdoc->MarginSetText(wParam, CharPtrFromSPtr(lParam));
9342 break;
9344 case SCI_MARGINGETTEXT: {
9345 const StyledText st = pdoc->MarginStyledText(wParam);
9346 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
9349 case SCI_MARGINSETSTYLE:
9350 pdoc->MarginSetStyle(wParam, lParam);
9351 break;
9353 case SCI_MARGINGETSTYLE: {
9354 const StyledText st = pdoc->MarginStyledText(wParam);
9355 return st.style;
9358 case SCI_MARGINSETSTYLES:
9359 pdoc->MarginSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
9360 break;
9362 case SCI_MARGINGETSTYLES: {
9363 const StyledText st = pdoc->MarginStyledText(wParam);
9364 return BytesResult(lParam, st.styles, st.length);
9367 case SCI_MARGINTEXTCLEARALL:
9368 pdoc->MarginClearAll();
9369 break;
9371 case SCI_ANNOTATIONSETTEXT:
9372 pdoc->AnnotationSetText(wParam, CharPtrFromSPtr(lParam));
9373 break;
9375 case SCI_ANNOTATIONGETTEXT: {
9376 const StyledText st = pdoc->AnnotationStyledText(wParam);
9377 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
9380 case SCI_ANNOTATIONGETSTYLE: {
9381 const StyledText st = pdoc->AnnotationStyledText(wParam);
9382 return st.style;
9385 case SCI_ANNOTATIONSETSTYLE:
9386 pdoc->AnnotationSetStyle(wParam, lParam);
9387 break;
9389 case SCI_ANNOTATIONSETSTYLES:
9390 pdoc->AnnotationSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
9391 break;
9393 case SCI_ANNOTATIONGETSTYLES: {
9394 const StyledText st = pdoc->AnnotationStyledText(wParam);
9395 return BytesResult(lParam, st.styles, st.length);
9398 case SCI_ANNOTATIONGETLINES:
9399 return pdoc->AnnotationLines(wParam);
9401 case SCI_ANNOTATIONCLEARALL:
9402 pdoc->AnnotationClearAll();
9403 break;
9405 case SCI_ANNOTATIONSETVISIBLE:
9406 SetAnnotationVisible(wParam);
9407 break;
9409 case SCI_ANNOTATIONGETVISIBLE:
9410 return vs.annotationVisible;
9412 case SCI_ANNOTATIONSETSTYLEOFFSET:
9413 vs.annotationStyleOffset = wParam;
9414 InvalidateStyleRedraw();
9415 break;
9417 case SCI_ANNOTATIONGETSTYLEOFFSET:
9418 return vs.annotationStyleOffset;
9420 case SCI_RELEASEALLEXTENDEDSTYLES:
9421 vs.ReleaseAllExtendedStyles();
9422 break;
9424 case SCI_ALLOCATEEXTENDEDSTYLES:
9425 return vs.AllocateExtendedStyles(wParam);
9427 case SCI_ADDUNDOACTION:
9428 pdoc->AddUndoAction(wParam, lParam & UNDO_MAY_COALESCE);
9429 break;
9431 case SCI_SETMOUSESELECTIONRECTANGULARSWITCH:
9432 mouseSelectionRectangularSwitch = wParam != 0;
9433 break;
9435 case SCI_GETMOUSESELECTIONRECTANGULARSWITCH:
9436 return mouseSelectionRectangularSwitch;
9438 case SCI_SETMULTIPLESELECTION:
9439 multipleSelection = wParam != 0;
9440 InvalidateCaret();
9441 break;
9443 case SCI_GETMULTIPLESELECTION:
9444 return multipleSelection;
9446 case SCI_SETADDITIONALSELECTIONTYPING:
9447 additionalSelectionTyping = wParam != 0;
9448 InvalidateCaret();
9449 break;
9451 case SCI_GETADDITIONALSELECTIONTYPING:
9452 return additionalSelectionTyping;
9454 case SCI_SETMULTIPASTE:
9455 multiPasteMode = wParam;
9456 break;
9458 case SCI_GETMULTIPASTE:
9459 return multiPasteMode;
9461 case SCI_SETADDITIONALCARETSBLINK:
9462 additionalCaretsBlink = wParam != 0;
9463 InvalidateCaret();
9464 break;
9466 case SCI_GETADDITIONALCARETSBLINK:
9467 return additionalCaretsBlink;
9469 case SCI_SETADDITIONALCARETSVISIBLE:
9470 additionalCaretsVisible = wParam != 0;
9471 InvalidateCaret();
9472 break;
9474 case SCI_GETADDITIONALCARETSVISIBLE:
9475 return additionalCaretsVisible;
9477 case SCI_GETSELECTIONS:
9478 return sel.Count();
9480 case SCI_GETSELECTIONEMPTY:
9481 return sel.Empty();
9483 case SCI_CLEARSELECTIONS:
9484 sel.Clear();
9485 Redraw();
9486 break;
9488 case SCI_SETSELECTION:
9489 sel.SetSelection(SelectionRange(wParam, lParam));
9490 Redraw();
9491 break;
9493 case SCI_ADDSELECTION:
9494 sel.AddSelection(SelectionRange(wParam, lParam));
9495 Redraw();
9496 break;
9498 case SCI_DROPSELECTIONN:
9499 sel.DropSelection(wParam);
9500 Redraw();
9501 break;
9503 case SCI_SETMAINSELECTION:
9504 sel.SetMain(wParam);
9505 Redraw();
9506 break;
9508 case SCI_GETMAINSELECTION:
9509 return sel.Main();
9511 case SCI_SETSELECTIONNCARET:
9512 sel.Range(wParam).caret.SetPosition(lParam);
9513 Redraw();
9514 break;
9516 case SCI_GETSELECTIONNCARET:
9517 return sel.Range(wParam).caret.Position();
9519 case SCI_SETSELECTIONNANCHOR:
9520 sel.Range(wParam).anchor.SetPosition(lParam);
9521 Redraw();
9522 break;
9523 case SCI_GETSELECTIONNANCHOR:
9524 return sel.Range(wParam).anchor.Position();
9526 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
9527 sel.Range(wParam).caret.SetVirtualSpace(lParam);
9528 Redraw();
9529 break;
9531 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
9532 return sel.Range(wParam).caret.VirtualSpace();
9534 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
9535 sel.Range(wParam).anchor.SetVirtualSpace(lParam);
9536 Redraw();
9537 break;
9539 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
9540 return sel.Range(wParam).anchor.VirtualSpace();
9542 case SCI_SETSELECTIONNSTART:
9543 sel.Range(wParam).anchor.SetPosition(lParam);
9544 Redraw();
9545 break;
9547 case SCI_GETSELECTIONNSTART:
9548 return sel.Range(wParam).Start().Position();
9550 case SCI_SETSELECTIONNEND:
9551 sel.Range(wParam).caret.SetPosition(lParam);
9552 Redraw();
9553 break;
9555 case SCI_GETSELECTIONNEND:
9556 return sel.Range(wParam).End().Position();
9558 case SCI_SETRECTANGULARSELECTIONCARET:
9559 if (!sel.IsRectangular())
9560 sel.Clear();
9561 sel.selType = Selection::selRectangle;
9562 sel.Rectangular().caret.SetPosition(wParam);
9563 SetRectangularRange();
9564 Redraw();
9565 break;
9567 case SCI_GETRECTANGULARSELECTIONCARET:
9568 return sel.Rectangular().caret.Position();
9570 case SCI_SETRECTANGULARSELECTIONANCHOR:
9571 if (!sel.IsRectangular())
9572 sel.Clear();
9573 sel.selType = Selection::selRectangle;
9574 sel.Rectangular().anchor.SetPosition(wParam);
9575 SetRectangularRange();
9576 Redraw();
9577 break;
9579 case SCI_GETRECTANGULARSELECTIONANCHOR:
9580 return sel.Rectangular().anchor.Position();
9582 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9583 if (!sel.IsRectangular())
9584 sel.Clear();
9585 sel.selType = Selection::selRectangle;
9586 sel.Rectangular().caret.SetVirtualSpace(wParam);
9587 SetRectangularRange();
9588 Redraw();
9589 break;
9591 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9592 return sel.Rectangular().caret.VirtualSpace();
9594 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9595 if (!sel.IsRectangular())
9596 sel.Clear();
9597 sel.selType = Selection::selRectangle;
9598 sel.Rectangular().anchor.SetVirtualSpace(wParam);
9599 SetRectangularRange();
9600 Redraw();
9601 break;
9603 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9604 return sel.Rectangular().anchor.VirtualSpace();
9606 case SCI_SETVIRTUALSPACEOPTIONS:
9607 virtualSpaceOptions = wParam;
9608 break;
9610 case SCI_GETVIRTUALSPACEOPTIONS:
9611 return virtualSpaceOptions;
9613 case SCI_SETADDITIONALSELFORE:
9614 vs.selAdditionalForeground = ColourDesired(wParam);
9615 InvalidateStyleRedraw();
9616 break;
9618 case SCI_SETADDITIONALSELBACK:
9619 vs.selAdditionalBackground = ColourDesired(wParam);
9620 InvalidateStyleRedraw();
9621 break;
9623 case SCI_SETADDITIONALSELALPHA:
9624 vs.selAdditionalAlpha = wParam;
9625 InvalidateStyleRedraw();
9626 break;
9628 case SCI_GETADDITIONALSELALPHA:
9629 return vs.selAdditionalAlpha;
9631 case SCI_SETADDITIONALCARETFORE:
9632 vs.additionalCaretColour = ColourDesired(wParam);
9633 InvalidateStyleRedraw();
9634 break;
9636 case SCI_GETADDITIONALCARETFORE:
9637 return vs.additionalCaretColour.AsLong();
9639 case SCI_ROTATESELECTION:
9640 sel.RotateMain();
9641 InvalidateSelection(sel.RangeMain(), true);
9642 break;
9644 case SCI_SWAPMAINANCHORCARET:
9645 InvalidateSelection(sel.RangeMain());
9646 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
9647 break;
9649 case SCI_CHANGELEXERSTATE:
9650 pdoc->ChangeLexerState(wParam, lParam);
9651 break;
9653 case SCI_SETIDENTIFIER:
9654 SetCtrlID(wParam);
9655 break;
9657 case SCI_GETIDENTIFIER:
9658 return GetCtrlID();
9660 case SCI_SETTECHNOLOGY:
9661 // No action by default
9662 break;
9664 case SCI_GETTECHNOLOGY:
9665 return technology;
9667 case SCI_COUNTCHARACTERS:
9668 return pdoc->CountCharacters(wParam, lParam);
9670 default:
9671 return DefWndProc(iMessage, wParam, lParam);
9673 //Platform::DebugPrintf("end wnd proc\n");
9674 return 0l;