1 // Scintilla source code edit control
3 ** Main code for the edit control.
5 // Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
18 // With Borland C++ 5.5, including <string> includes Windows.h leading to defining
19 // FindText to FindTextA which makes calls here to Document::FindText fail.
28 #include "Scintilla.h"
30 #include "SplitVector.h"
31 #include "Partitioning.h"
32 #include "RunStyles.h"
33 #include "ContractionState.h"
34 #include "CellBuffer.h"
36 #include "Indicator.h"
38 #include "LineMarker.h"
40 #include "ViewStyle.h"
41 #include "CharClassify.h"
42 #include "Decoration.h"
44 #include "Selection.h"
45 #include "PositionCache.h"
49 using namespace Scintilla
;
53 return whether this modification represents an operation that
54 may reasonably be deferred (not done now OR [possibly] at all)
56 static bool CanDeferToLastStep(const DocModification
&mh
) {
57 if (mh
.modificationType
& (SC_MOD_BEFOREINSERT
| SC_MOD_BEFOREDELETE
))
58 return true; // CAN skip
59 if (!(mh
.modificationType
& (SC_PERFORMED_UNDO
| SC_PERFORMED_REDO
)))
60 return false; // MUST do
61 if (mh
.modificationType
& SC_MULTISTEPUNDOREDO
)
62 return true; // CAN skip
63 return false; // PRESUMABLY must do
66 static bool CanEliminate(const DocModification
&mh
) {
68 (mh
.modificationType
& (SC_MOD_BEFOREINSERT
| SC_MOD_BEFOREDELETE
)) != 0;
72 return whether this modification represents the FINAL step
73 in a [possibly lengthy] multi-step Undo/Redo sequence
75 static bool IsLastStep(const DocModification
&mh
) {
77 (mh
.modificationType
& (SC_PERFORMED_UNDO
| SC_PERFORMED_REDO
)) != 0
78 && (mh
.modificationType
& SC_MULTISTEPUNDOREDO
) != 0
79 && (mh
.modificationType
& SC_LASTSTEPINUNDOREDO
) != 0
80 && (mh
.modificationType
& SC_MULTILINEUNDOREDO
) != 0;
84 active(false), on(false), period(500) {}
87 ticking(false), ticksToWait(0), tickerID(0) {}
90 state(false), idlerID(0) {}
92 static inline bool IsControlCharacter(int ch
) {
93 // iscntrl returns true for lots of chars > 127 which are displayable
94 return ch
>= 0 && ch
< ' ';
102 printMagnification
= 0;
103 printColourMode
= SC_PRINT_NORMAL
;
104 printWrapState
= eWrapWord
;
105 cursorMode
= SC_CURSORNORMAL
;
106 controlCharSymbol
= 0; /* Draw the control characters */
109 hideSelection
= false;
110 inOverstrike
= false;
112 mouseDownCaptures
= true;
118 dwellDelay
= SC_TIME_FOREVER
;
119 ticksToDwell
= SC_TIME_FOREVER
;
124 dropWentOutside
= false;
125 posDrag
= SelectionPosition(invalidPosition
);
126 posDrop
= SelectionPosition(invalidPosition
);
127 selectionType
= selChar
;
131 originalAnchorPos
= 0;
133 primarySelection
= true;
135 caretXPolicy
= CARET_SLOP
| CARET_EVEN
;
138 caretYPolicy
= CARET_EVEN
;
145 horizontalScrollBarVisible
= true;
147 trackLineWidth
= false;
148 lineWidthMaxSeen
= 0;
149 verticalScrollBarVisible
= true;
150 endAtLastLine
= true;
152 multipleSelection
= false;
153 additionalSelectionTyping
= false;
154 multiPasteMode
= SC_MULTIPASTE_ONCE
;
155 additionalCaretsBlink
= true;
156 additionalCaretsVisible
= true;
157 virtualSpaceOptions
= SCVS_NONE
;
159 pixmapLine
= Surface::Allocate();
160 pixmapSelMargin
= Surface::Allocate();
161 pixmapSelPattern
= Surface::Allocate();
162 pixmapIndentGuide
= Surface::Allocate();
163 pixmapIndentGuideHighlight
= Surface::Allocate();
172 lengthForEncode
= -1;
175 braces
[0] = invalidPosition
;
176 braces
[1] = invalidPosition
;
177 bracesMatchStyle
= STYLE_BRACEBAD
;
178 highlightGuideColumn
= 0;
182 paintState
= notPainting
;
184 modEventMask
= SC_MODEVENTMASKALL
;
186 pdoc
= new Document();
188 pdoc
->AddWatcher(this, 0);
190 recordingMacro
= false;
193 wrapState
= eWrapNone
;
194 wrapWidth
= LineLayout::wrapWidthInfinite
;
195 wrapStart
= wrapLineLarge
;
196 wrapEnd
= wrapLineLarge
;
198 wrapVisualFlagsLocation
= 0;
199 wrapVisualStartIndent
= 0;
200 wrapIndentMode
= SC_WRAPINDENT_FIXED
;
203 convertPastes
= true;
208 llc
.SetLevel(LineLayoutCache::llcCaret
);
209 posCache
.SetSize(0x400);
213 pdoc
->RemoveWatcher(this, 0);
218 delete pixmapSelMargin
;
219 delete pixmapSelPattern
;
220 delete pixmapIndentGuide
;
221 delete pixmapIndentGuideHighlight
;
224 void Editor::Finalise() {
229 void Editor::DropGraphics() {
230 pixmapLine
->Release();
231 pixmapSelMargin
->Release();
232 pixmapSelPattern
->Release();
233 pixmapIndentGuide
->Release();
234 pixmapIndentGuideHighlight
->Release();
237 void Editor::InvalidateStyleData() {
241 llc
.Invalidate(LineLayout::llInvalid
);
245 void Editor::InvalidateStyleRedraw() {
247 InvalidateStyleData();
251 void Editor::RefreshColourPalette(Palette
&pal
, bool want
) {
252 vs
.RefreshColourPalette(pal
, want
);
255 void Editor::RefreshStyleData() {
258 AutoSurface
surface(this);
260 vs
.Refresh(*surface
);
261 RefreshColourPalette(palette
, true);
262 palette
.Allocate(wMain
);
263 RefreshColourPalette(palette
, false);
265 if (wrapIndentMode
== SC_WRAPINDENT_INDENT
) {
266 wrapAddIndent
= pdoc
->IndentSize() * vs
.spaceWidth
;
267 } else if (wrapIndentMode
== SC_WRAPINDENT_SAME
) {
269 } else { //SC_WRAPINDENT_FIXED
270 wrapAddIndent
= wrapVisualStartIndent
* vs
.aveCharWidth
;
271 if ((wrapVisualFlags
& SC_WRAPVISUALFLAG_START
) && (wrapAddIndent
<= 0))
272 wrapAddIndent
= vs
.aveCharWidth
; // must indent to show start visual
275 SetRectangularRange();
279 PRectangle
Editor::GetClientRectangle() {
280 return wMain
.GetClientPosition();
283 PRectangle
Editor::GetTextRectangle() {
284 PRectangle rc
= GetClientRectangle();
285 rc
.left
+= vs
.fixedColumnWidth
;
286 rc
.right
-= vs
.rightMarginWidth
;
290 int Editor::LinesOnScreen() {
291 PRectangle rcClient
= GetClientRectangle();
292 int htClient
= rcClient
.bottom
- rcClient
.top
;
293 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
294 return htClient
/ vs
.lineHeight
;
297 int Editor::LinesToScroll() {
298 int retVal
= LinesOnScreen() - 1;
305 int Editor::MaxScrollPos() {
306 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
307 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
308 int retVal
= cs
.LinesDisplayed();
310 retVal
-= LinesOnScreen();
321 const char *ControlCharacterString(unsigned char ch
) {
322 const char *reps
[] = {
323 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
324 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
325 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
326 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
328 if (ch
< (sizeof(reps
) / sizeof(reps
[0]))) {
336 * Convenience class to ensure LineLayout objects are always disposed.
338 class AutoLineLayout
{
339 LineLayoutCache
&llc
;
341 AutoLineLayout
&operator=(const AutoLineLayout
&);
343 AutoLineLayout(LineLayoutCache
&llc_
, LineLayout
*ll_
) : llc(llc_
), ll(ll_
) {}
348 LineLayout
*operator->() const {
351 operator LineLayout
*() const {
354 void Set(LineLayout
*ll_
) {
360 SelectionPosition
Editor::ClampPositionIntoDocument(SelectionPosition sp
) const {
361 if (sp
.Position() < 0) {
362 return SelectionPosition(0);
363 } else if (sp
.Position() > pdoc
->Length()) {
364 return SelectionPosition(pdoc
->Length());
366 // If not at end of line then set offset to 0
367 if (!pdoc
->IsLineEndPosition(sp
.Position()))
368 sp
.SetVirtualSpace(0);
373 Point
Editor::LocationFromPosition(SelectionPosition pos
) {
376 if (pos
.Position() == INVALID_POSITION
)
378 int line
= pdoc
->LineFromPosition(pos
.Position());
379 int lineVisible
= cs
.DisplayFromDoc(line
);
380 //Platform::DebugPrintf("line=%d\n", line);
381 AutoSurface
surface(this);
382 AutoLineLayout
ll(llc
, RetrieveLineLayout(line
));
384 // -1 because of adding in for visible lines in following loop.
385 pt
.y
= (lineVisible
- topLine
- 1) * vs
.lineHeight
;
387 unsigned int posLineStart
= pdoc
->LineStart(line
);
388 LayoutLine(line
, surface
, vs
, ll
, wrapWidth
);
389 int posInLine
= pos
.Position() - posLineStart
;
390 // In case of very long line put x at arbitrary large position
391 if (posInLine
> ll
->maxLineLength
) {
392 pt
.x
= ll
->positions
[ll
->maxLineLength
] - ll
->positions
[ll
->LineStart(ll
->lines
)];
395 for (int subLine
= 0; subLine
< ll
->lines
; subLine
++) {
396 if ((posInLine
>= ll
->LineStart(subLine
)) && (posInLine
<= ll
->LineStart(subLine
+ 1))) {
397 pt
.x
= ll
->positions
[posInLine
] - ll
->positions
[ll
->LineStart(subLine
)];
398 if (ll
->wrapIndent
!= 0) {
399 int lineStart
= ll
->LineStart(subLine
);
400 if (lineStart
!= 0) // Wrapped
401 pt
.x
+= ll
->wrapIndent
;
404 if (posInLine
>= ll
->LineStart(subLine
)) {
405 pt
.y
+= vs
.lineHeight
;
408 pt
.x
+= vs
.fixedColumnWidth
- xOffset
;
410 pt
.x
+= pos
.VirtualSpace() * static_cast<int>(vs
.styles
[ll
->EndLineStyle()].spaceWidth
);
414 Point
Editor::LocationFromPosition(int pos
) {
415 return LocationFromPosition(SelectionPosition(pos
));
418 int Editor::XFromPosition(int pos
) {
419 Point pt
= LocationFromPosition(pos
);
420 return pt
.x
- vs
.fixedColumnWidth
+ xOffset
;
423 int Editor::XFromPosition(SelectionPosition sp
) {
424 Point pt
= LocationFromPosition(sp
);
425 return pt
.x
- vs
.fixedColumnWidth
+ xOffset
;
428 int Editor::LineFromLocation(Point pt
) {
429 return cs
.DocFromDisplay(pt
.y
/ vs
.lineHeight
+ topLine
);
432 void Editor::SetTopLine(int topLineNew
) {
433 topLine
= topLineNew
;
434 posTopLine
= pdoc
->LineStart(cs
.DocFromDisplay(topLine
));
437 SelectionPosition
Editor::SPositionFromLocation(Point pt
, bool canReturnInvalid
, bool charPosition
, bool virtualSpace
) {
439 if (canReturnInvalid
) {
440 PRectangle rcClient
= GetTextRectangle();
441 if (!rcClient
.Contains(pt
))
442 return SelectionPosition(INVALID_POSITION
);
443 if (pt
.x
< vs
.fixedColumnWidth
)
444 return SelectionPosition(INVALID_POSITION
);
446 return SelectionPosition(INVALID_POSITION
);
448 pt
.x
= pt
.x
- vs
.fixedColumnWidth
+ xOffset
;
449 int visibleLine
= pt
.y
/ vs
.lineHeight
+ topLine
;
450 if (pt
.y
< 0) { // Division rounds towards 0
451 visibleLine
= (pt
.y
- (vs
.lineHeight
- 1)) / vs
.lineHeight
+ topLine
;
453 if (!canReturnInvalid
&& (visibleLine
< 0))
455 int lineDoc
= cs
.DocFromDisplay(visibleLine
);
456 if (canReturnInvalid
&& (lineDoc
< 0))
457 return SelectionPosition(INVALID_POSITION
);
458 if (lineDoc
>= pdoc
->LinesTotal())
459 return SelectionPosition(canReturnInvalid
? INVALID_POSITION
: pdoc
->Length());
460 unsigned int posLineStart
= pdoc
->LineStart(lineDoc
);
461 SelectionPosition
retVal(canReturnInvalid
? INVALID_POSITION
: static_cast<int>(posLineStart
));
462 AutoSurface
surface(this);
463 AutoLineLayout
ll(llc
, RetrieveLineLayout(lineDoc
));
465 LayoutLine(lineDoc
, surface
, vs
, ll
, wrapWidth
);
466 int lineStartSet
= cs
.DisplayFromDoc(lineDoc
);
467 int subLine
= visibleLine
- lineStartSet
;
468 if (subLine
< ll
->lines
) {
469 int lineStart
= ll
->LineStart(subLine
);
470 int lineEnd
= ll
->LineLastVisible(subLine
);
471 int subLineStart
= ll
->positions
[lineStart
];
473 if (ll
->wrapIndent
!= 0) {
474 if (lineStart
!= 0) // Wrapped
475 pt
.x
-= ll
->wrapIndent
;
477 int i
= ll
->FindBefore(pt
.x
+ subLineStart
, lineStart
, lineEnd
);
478 while (i
< lineEnd
) {
480 if ((pt
.x
+ subLineStart
) < (ll
->positions
[i
+ 1])) {
481 return SelectionPosition(pdoc
->MovePositionOutsideChar(i
+ posLineStart
, 1));
484 if ((pt
.x
+ subLineStart
) < ((ll
->positions
[i
] + ll
->positions
[i
+ 1]) / 2)) {
485 return SelectionPosition(pdoc
->MovePositionOutsideChar(i
+ posLineStart
, 1));
491 const int spaceWidth
= static_cast<int>(vs
.styles
[ll
->EndLineStyle()].spaceWidth
);
492 int spaceOffset
= (pt
.x
+ subLineStart
- ll
->positions
[lineEnd
] + spaceWidth
/ 2) /
494 return SelectionPosition(lineEnd
+ posLineStart
, spaceOffset
);
495 } else if (canReturnInvalid
) {
496 if (pt
.x
< (ll
->positions
[lineEnd
] - subLineStart
)) {
497 return SelectionPosition(pdoc
->MovePositionOutsideChar(lineEnd
+ posLineStart
, 1));
500 return SelectionPosition(lineEnd
+ posLineStart
);
503 if (!canReturnInvalid
)
504 return SelectionPosition(ll
->numCharsInLine
+ posLineStart
);
509 int Editor::PositionFromLocation(Point pt
, bool canReturnInvalid
, bool charPosition
) {
510 return SPositionFromLocation(pt
, canReturnInvalid
, charPosition
, false).Position();
514 * Find the document position corresponding to an x coordinate on a particular document line.
515 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
517 int Editor::PositionFromLineX(int lineDoc
, int x
) {
519 if (lineDoc
>= pdoc
->LinesTotal())
520 return pdoc
->Length();
521 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
522 AutoSurface
surface(this);
523 AutoLineLayout
ll(llc
, RetrieveLineLayout(lineDoc
));
526 unsigned int posLineStart
= pdoc
->LineStart(lineDoc
);
527 LayoutLine(lineDoc
, surface
, vs
, ll
, wrapWidth
);
528 retVal
= ll
->numCharsBeforeEOL
+ posLineStart
;
530 int lineStart
= ll
->LineStart(subLine
);
531 int lineEnd
= ll
->LineLastVisible(subLine
);
532 int subLineStart
= ll
->positions
[lineStart
];
534 if (ll
->wrapIndent
!= 0) {
535 if (lineStart
!= 0) // Wrapped
538 int i
= ll
->FindBefore(x
+ subLineStart
, lineStart
, lineEnd
);
539 while (i
< lineEnd
) {
540 if ((x
+ subLineStart
) < ((ll
->positions
[i
] + ll
->positions
[i
+ 1]) / 2)) {
541 retVal
= pdoc
->MovePositionOutsideChar(i
+ posLineStart
, 1);
551 * Find the document position corresponding to an x coordinate on a particular document line.
552 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
554 SelectionPosition
Editor::SPositionFromLineX(int lineDoc
, int x
) {
556 if (lineDoc
>= pdoc
->LinesTotal())
557 return SelectionPosition(pdoc
->Length());
558 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
559 AutoSurface
surface(this);
560 AutoLineLayout
ll(llc
, RetrieveLineLayout(lineDoc
));
563 unsigned int posLineStart
= pdoc
->LineStart(lineDoc
);
564 LayoutLine(lineDoc
, surface
, vs
, ll
, wrapWidth
);
566 int lineStart
= ll
->LineStart(subLine
);
567 int lineEnd
= ll
->LineLastVisible(subLine
);
568 int subLineStart
= ll
->positions
[lineStart
];
570 if (ll
->wrapIndent
!= 0) {
571 if (lineStart
!= 0) // Wrapped
574 int i
= ll
->FindBefore(x
+ subLineStart
, lineStart
, lineEnd
);
575 while (i
< lineEnd
) {
576 if ((x
+ subLineStart
) < ((ll
->positions
[i
] + ll
->positions
[i
+ 1]) / 2)) {
577 retVal
= pdoc
->MovePositionOutsideChar(i
+ posLineStart
, 1);
578 return SelectionPosition(retVal
);
582 const int spaceWidth
= static_cast<int>(vs
.styles
[ll
->EndLineStyle()].spaceWidth
);
583 int spaceOffset
= (x
+ subLineStart
- ll
->positions
[lineEnd
] + spaceWidth
/ 2) / spaceWidth
;
584 return SelectionPosition(lineEnd
+ posLineStart
, spaceOffset
);
586 return SelectionPosition(retVal
);
590 * If painting then abandon the painting because a wider redraw is needed.
591 * @return true if calling code should stop drawing.
593 bool Editor::AbandonPaint() {
594 if ((paintState
== painting
) && !paintingAllText
) {
595 paintState
= paintAbandoned
;
597 return paintState
== paintAbandoned
;
600 void Editor::RedrawRect(PRectangle rc
) {
601 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
603 // Clip the redraw rectangle into the client area
604 PRectangle rcClient
= GetClientRectangle();
605 if (rc
.top
< rcClient
.top
)
606 rc
.top
= rcClient
.top
;
607 if (rc
.bottom
> rcClient
.bottom
)
608 rc
.bottom
= rcClient
.bottom
;
609 if (rc
.left
< rcClient
.left
)
610 rc
.left
= rcClient
.left
;
611 if (rc
.right
> rcClient
.right
)
612 rc
.right
= rcClient
.right
;
614 if ((rc
.bottom
> rc
.top
) && (rc
.right
> rc
.left
)) {
615 wMain
.InvalidateRectangle(rc
);
619 void Editor::Redraw() {
620 //Platform::DebugPrintf("Redraw all\n");
621 PRectangle rcClient
= GetClientRectangle();
622 wMain
.InvalidateRectangle(rcClient
);
623 //wMain.InvalidateAll();
626 void Editor::RedrawSelMargin(int line
, bool allAfter
) {
627 if (!AbandonPaint()) {
631 PRectangle rcSelMargin
= GetClientRectangle();
632 rcSelMargin
.right
= vs
.fixedColumnWidth
;
634 int position
= pdoc
->LineStart(line
);
635 PRectangle rcLine
= RectangleFromRange(position
, position
);
636 rcSelMargin
.top
= rcLine
.top
;
638 rcSelMargin
.bottom
= rcLine
.bottom
;
640 wMain
.InvalidateRectangle(rcSelMargin
);
645 PRectangle
Editor::RectangleFromRange(int start
, int end
) {
652 int minLine
= cs
.DisplayFromDoc(pdoc
->LineFromPosition(minPos
));
653 int lineDocMax
= pdoc
->LineFromPosition(maxPos
);
654 int maxLine
= cs
.DisplayFromDoc(lineDocMax
) + cs
.GetHeight(lineDocMax
) - 1;
655 PRectangle rcClient
= GetTextRectangle();
657 rc
.left
= vs
.fixedColumnWidth
;
658 rc
.top
= (minLine
- topLine
) * vs
.lineHeight
;
661 rc
.right
= rcClient
.right
;
662 rc
.bottom
= (maxLine
- topLine
+ 1) * vs
.lineHeight
;
663 // Ensure PRectangle is within 16 bit space
664 rc
.top
= Platform::Clamp(rc
.top
, -32000, 32000);
665 rc
.bottom
= Platform::Clamp(rc
.bottom
, -32000, 32000);
670 void Editor::InvalidateRange(int start
, int end
) {
671 RedrawRect(RectangleFromRange(start
, end
));
674 int Editor::CurrentPosition() {
675 return sel
.MainCaret();
678 bool Editor::SelectionEmpty() {
682 SelectionPosition
Editor::SelectionStart() {
683 return sel
.RangeMain().Start();
686 SelectionPosition
Editor::SelectionEnd() {
687 return sel
.RangeMain().End();
690 void Editor::SetRectangularRange() {
691 if (sel
.IsRectangular()) {
692 int xAnchor
= XFromPosition(sel
.Rectangular().anchor
);
693 int xCaret
= XFromPosition(sel
.Rectangular().caret
);
694 if (sel
.selType
== Selection::selThin
) {
697 int lineAnchor
= pdoc
->LineFromPosition(sel
.Rectangular().anchor
.Position());
698 int lineCaret
= pdoc
->LineFromPosition(sel
.Rectangular().caret
.Position());
699 int increment
= (lineCaret
> lineAnchor
) ? 1 : -1;
700 for (int line
=lineAnchor
; line
!= lineCaret
+increment
; line
+= increment
) {
701 SelectionRange
range(SPositionFromLineX(line
, xCaret
), SPositionFromLineX(line
, xAnchor
));
702 if ((virtualSpaceOptions
& SCVS_RECTANGULARSELECTION
) == 0)
703 range
.ClearVirtualSpace();
704 if (line
== lineAnchor
)
705 sel
.SetSelection(range
);
707 sel
.AddSelection(range
);
712 void Editor::ThinRectangularRange() {
713 if (sel
.IsRectangular()) {
714 sel
.selType
= Selection::selThin
;
715 if (sel
.Rectangular().caret
< sel
.Rectangular().anchor
) {
716 sel
.Rectangular() = SelectionRange(sel
.Range(sel
.Count()-1).caret
, sel
.Range(0).anchor
);
718 sel
.Rectangular() = SelectionRange(sel
.Range(sel
.Count()-1).anchor
, sel
.Range(0).caret
);
720 SetRectangularRange();
724 void Editor::InvalidateSelection(SelectionRange newMain
, bool invalidateWholeSelection
) {
725 if (sel
.Count() > 1 || !(sel
.RangeMain().anchor
== newMain
.anchor
) || sel
.IsRectangular()) {
726 invalidateWholeSelection
= true;
728 int firstAffected
= Platform::Minimum(sel
.RangeMain().Start().Position(), newMain
.Start().Position());
729 // +1 for lastAffected ensures caret repainted
730 int lastAffected
= Platform::Maximum(newMain
.caret
.Position()+1, newMain
.anchor
.Position());
731 lastAffected
= Platform::Maximum(lastAffected
, sel
.RangeMain().End().Position());
732 if (invalidateWholeSelection
) {
733 for (size_t r
=0; r
<sel
.Count(); r
++) {
734 firstAffected
= Platform::Minimum(firstAffected
, sel
.Range(r
).caret
.Position());
735 firstAffected
= Platform::Minimum(firstAffected
, sel
.Range(r
).anchor
.Position());
736 lastAffected
= Platform::Maximum(lastAffected
, sel
.Range(r
).caret
.Position()+1);
737 lastAffected
= Platform::Maximum(lastAffected
, sel
.Range(r
).anchor
.Position());
741 InvalidateRange(firstAffected
, lastAffected
);
744 void Editor::SetSelection(SelectionPosition currentPos_
, SelectionPosition anchor_
) {
745 SelectionRange
rangeNew(ClampPositionIntoDocument(currentPos_
),
746 ClampPositionIntoDocument(anchor_
));
747 if (sel
.Count() > 1 || !(sel
.RangeMain() == rangeNew
)) {
748 InvalidateSelection(rangeNew
);
750 sel
.RangeMain() = rangeNew
;
751 SetRectangularRange();
755 void Editor::SetSelection(int currentPos_
, int anchor_
) {
756 SetSelection(SelectionPosition(currentPos_
), SelectionPosition(anchor_
));
759 // Just move the caret on the main selection
760 void Editor::SetSelection(SelectionPosition currentPos_
) {
761 currentPos_
= ClampPositionIntoDocument(currentPos_
);
762 if (sel
.Count() > 1 || !(sel
.RangeMain().caret
== currentPos_
)) {
763 InvalidateSelection(SelectionRange(currentPos_
));
765 if (sel
.IsRectangular()) {
767 SelectionRange(SelectionPosition(currentPos_
), sel
.Rectangular().anchor
);
768 SetRectangularRange();
771 SelectionRange(SelectionPosition(currentPos_
), sel
.RangeMain().anchor
);
776 void Editor::SetSelection(int currentPos_
) {
777 SetSelection(SelectionPosition(currentPos_
));
780 void Editor::SetEmptySelection(SelectionPosition currentPos_
) {
781 SelectionRange
rangeNew(ClampPositionIntoDocument(currentPos_
));
782 if (sel
.Count() > 1 || !(sel
.RangeMain() == rangeNew
)) {
783 InvalidateSelection(rangeNew
);
786 sel
.RangeMain() = rangeNew
;
787 SetRectangularRange();
792 void Editor::SetEmptySelection(int currentPos_
) {
793 SetEmptySelection(SelectionPosition(currentPos_
));
796 bool Editor::RangeContainsProtected(int start
, int end
) const {
797 if (vs
.ProtectionActive()) {
803 int mask
= pdoc
->stylingBitsMask
;
804 for (int pos
= start
; pos
< end
; pos
++) {
805 if (vs
.styles
[pdoc
->StyleAt(pos
) & mask
].IsProtected())
812 bool Editor::SelectionContainsProtected() {
813 for (size_t r
=0; r
<sel
.Count(); r
++) {
814 if (RangeContainsProtected(sel
.Range(r
).Start().Position(),
815 sel
.Range(r
).End().Position())) {
823 * Asks document to find a good position and then moves out of any invisible positions.
825 int Editor::MovePositionOutsideChar(int pos
, int moveDir
, bool checkLineEnd
) const {
826 return MovePositionOutsideChar(SelectionPosition(pos
), moveDir
, checkLineEnd
).Position();
829 SelectionPosition
Editor::MovePositionOutsideChar(SelectionPosition pos
, int moveDir
, bool checkLineEnd
) const {
830 int posMoved
= pdoc
->MovePositionOutsideChar(pos
.Position(), moveDir
, checkLineEnd
);
831 if (posMoved
!= pos
.Position())
832 pos
.SetPosition(posMoved
);
833 if (vs
.ProtectionActive()) {
834 int mask
= pdoc
->stylingBitsMask
;
836 if ((pos
.Position() > 0) && vs
.styles
[pdoc
->StyleAt(pos
.Position() - 1) & mask
].IsProtected()) {
837 while ((pos
.Position() < pdoc
->Length()) &&
838 (vs
.styles
[pdoc
->StyleAt(pos
.Position()) & mask
].IsProtected()))
841 } else if (moveDir
< 0) {
842 if (vs
.styles
[pdoc
->StyleAt(pos
.Position()) & mask
].IsProtected()) {
843 while ((pos
.Position() > 0) &&
844 (vs
.styles
[pdoc
->StyleAt(pos
.Position() - 1) & mask
].IsProtected()))
852 int Editor::MovePositionTo(SelectionPosition newPos
, Selection::selTypes selt
, bool ensureVisible
) {
853 bool simpleCaret
= (sel
.Count() == 1) && sel
.Empty();
854 SelectionPosition spCaret
= sel
.Last();
856 int delta
= newPos
.Position() - sel
.MainCaret();
857 newPos
= ClampPositionIntoDocument(newPos
);
858 newPos
= MovePositionOutsideChar(newPos
, delta
);
859 if (!multipleSelection
&& sel
.IsRectangular() && (selt
== Selection::selStream
)) {
860 // Can't turn into multiple selection so clear additional selections
861 InvalidateSelection(SelectionRange(newPos
), true);
862 SelectionRange rangeMain
= sel
.RangeMain();
863 sel
.SetSelection(rangeMain
);
865 if (!sel
.IsRectangular() && (selt
== Selection::selRectangle
)) {
866 // Switching to rectangular
867 SelectionRange rangeMain
= sel
.RangeMain();
869 sel
.Rectangular() = rangeMain
;
871 if (selt
!= Selection::noSel
) {
874 if (selt
!= Selection::noSel
|| sel
.MoveExtends()) {
875 SetSelection(newPos
);
877 SetEmptySelection(newPos
);
879 ShowCaretAtCurrentPosition();
881 XYScrollPosition newXY
= XYScrollToMakeVisible(true, true, true);
882 if (simpleCaret
&& (newXY
.xOffset
== xOffset
)) {
883 // simple vertical scroll then invalidate
884 ScrollTo(newXY
.topLine
);
885 InvalidateSelection(SelectionRange(spCaret
), true);
893 int Editor::MovePositionTo(int newPos
, Selection::selTypes selt
, bool ensureVisible
) {
894 return MovePositionTo(SelectionPosition(newPos
), selt
, ensureVisible
);
897 SelectionPosition
Editor::MovePositionSoVisible(SelectionPosition pos
, int moveDir
) {
898 pos
= ClampPositionIntoDocument(pos
);
899 pos
= MovePositionOutsideChar(pos
, moveDir
);
900 int lineDoc
= pdoc
->LineFromPosition(pos
.Position());
901 if (cs
.GetVisible(lineDoc
)) {
904 int lineDisplay
= cs
.DisplayFromDoc(lineDoc
);
906 // lineDisplay is already line before fold as lines in fold use display line of line after fold
907 lineDisplay
= Platform::Clamp(lineDisplay
, 0, cs
.LinesDisplayed());
908 return SelectionPosition(pdoc
->LineStart(cs
.DocFromDisplay(lineDisplay
)));
910 lineDisplay
= Platform::Clamp(lineDisplay
- 1, 0, cs
.LinesDisplayed());
911 return SelectionPosition(pdoc
->LineEnd(cs
.DocFromDisplay(lineDisplay
)));
916 SelectionPosition
Editor::MovePositionSoVisible(int pos
, int moveDir
) {
917 return MovePositionSoVisible(SelectionPosition(pos
), moveDir
);
920 Point
Editor::PointMainCaret() {
921 return LocationFromPosition(sel
.Range(sel
.Main()).caret
);
925 * Choose the x position that the caret will try to stick to
926 * as it moves up and down.
928 void Editor::SetLastXChosen() {
929 Point pt
= PointMainCaret();
930 lastXChosen
= pt
.x
+ xOffset
;
933 void Editor::ScrollTo(int line
, bool moveThumb
) {
934 int topLineNew
= Platform::Clamp(line
, 0, MaxScrollPos());
935 if (topLineNew
!= topLine
) {
936 // Try to optimise small scrolls
937 int linesToMove
= topLine
- topLineNew
;
938 SetTopLine(topLineNew
);
939 // Optimize by styling the view as this will invalidate any needed area
940 // which could abort the initial paint if discovered later.
941 StyleToPositionInView(PositionAfterArea(GetClientRectangle()));
943 // Perform redraw rather than scroll if many lines would be redrawn anyway.
944 if ((abs(linesToMove
) <= 10) && (paintState
== notPainting
)) {
945 ScrollText(linesToMove
);
953 SetVerticalScrollPos();
958 void Editor::ScrollText(int /* linesToMove */) {
959 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
963 void Editor::HorizontalScrollTo(int xPos
) {
964 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
967 if ((wrapState
== eWrapNone
) && (xOffset
!= xPos
)) {
969 SetHorizontalScrollPos();
970 RedrawRect(GetClientRectangle());
974 void Editor::MoveCaretInsideView(bool ensureVisible
) {
975 PRectangle rcClient
= GetTextRectangle();
976 Point pt
= PointMainCaret();
977 if (pt
.y
< rcClient
.top
) {
978 MovePositionTo(SPositionFromLocation(
979 Point(lastXChosen
- xOffset
, rcClient
.top
)),
980 Selection::noSel
, ensureVisible
);
981 } else if ((pt
.y
+ vs
.lineHeight
- 1) > rcClient
.bottom
) {
982 int yOfLastLineFullyDisplayed
= rcClient
.top
+ (LinesOnScreen() - 1) * vs
.lineHeight
;
983 MovePositionTo(SPositionFromLocation(
984 Point(lastXChosen
- xOffset
, rcClient
.top
+ yOfLastLineFullyDisplayed
)),
985 Selection::noSel
, ensureVisible
);
989 int Editor::DisplayFromPosition(int pos
) {
990 int lineDoc
= pdoc
->LineFromPosition(pos
);
991 int lineDisplay
= cs
.DisplayFromDoc(lineDoc
);
992 AutoSurface
surface(this);
993 AutoLineLayout
ll(llc
, RetrieveLineLayout(lineDoc
));
995 LayoutLine(lineDoc
, surface
, vs
, ll
, wrapWidth
);
996 unsigned int posLineStart
= pdoc
->LineStart(lineDoc
);
997 int posInLine
= pos
- posLineStart
;
998 lineDisplay
--; // To make up for first increment ahead.
999 for (int subLine
= 0; subLine
< ll
->lines
; subLine
++) {
1000 if (posInLine
>= ll
->LineStart(subLine
)) {
1009 * Ensure the caret is reasonably visible in context.
1011 Caret policy in SciTE
1013 If slop is set, we can define a slop value.
1014 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1015 This zone is defined as a number of pixels near the vertical margins,
1016 and as a number of lines near the horizontal margins.
1017 By keeping the caret away from the edges, it is seen within its context,
1018 so it is likely that the identifier that the caret is on can be completely seen,
1019 and that the current line is seen with some of the lines following it which are
1020 often dependent on that line.
1022 If strict is set, the policy is enforced... strictly.
1023 The caret is centred on the display if slop is not set,
1024 and cannot go in the UZ if slop is set.
1026 If jumps is set, the display is moved more energetically
1027 so the caret can move in the same direction longer before the policy is applied again.
1028 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1030 If even is not set, instead of having symmetrical UZs,
1031 the left and bottom UZs are extended up to right and top UZs respectively.
1032 This way, we favour the displaying of useful information: the begining of lines,
1033 where most code reside, and the lines after the caret, eg. the body of a function.
1036 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1037 | | | | | visibility or going into the UZ) display is...
1038 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1039 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1040 0 | 0 | 0 | 1 | Yes | moved by one position
1041 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1042 0 | 0 | 1 | 1 | Yes | centred on the caret
1043 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1044 0 | 1 | - | 1 | No, caret is always centred | -
1045 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1046 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1047 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1048 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1049 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1050 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1051 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1054 Editor::XYScrollPosition
Editor::XYScrollToMakeVisible(const bool useMargin
, const bool vert
, const bool horiz
) {
1055 PRectangle rcClient
= GetTextRectangle();
1056 const SelectionPosition posCaret
= posDrag
.IsValid() ? posDrag
: sel
.RangeMain().caret
;
1057 const Point pt
= LocationFromPosition(posCaret
);
1058 const Point
ptBottomCaret(pt
.x
, pt
.y
+ vs
.lineHeight
- 1);
1059 const int lineCaret
= DisplayFromPosition(posCaret
.Position());
1061 XYScrollPosition
newXY(xOffset
, topLine
);
1063 // Vertical positioning
1064 if (vert
&& (pt
.y
< rcClient
.top
|| ptBottomCaret
.y
> rcClient
.bottom
|| (caretYPolicy
& CARET_STRICT
) != 0)) {
1065 const int linesOnScreen
= LinesOnScreen();
1066 const int halfScreen
= Platform::Maximum(linesOnScreen
- 1, 2) / 2;
1067 const bool bSlop
= (caretYPolicy
& CARET_SLOP
) != 0;
1068 const bool bStrict
= (caretYPolicy
& CARET_STRICT
) != 0;
1069 const bool bJump
= (caretYPolicy
& CARET_JUMPS
) != 0;
1070 const bool bEven
= (caretYPolicy
& CARET_EVEN
) != 0;
1072 // It should be possible to scroll the window to show the caret,
1073 // but this fails to remove the caret on GTK+
1074 if (bSlop
) { // A margin is defined
1077 int yMarginT
, yMarginB
;
1079 // In drag mode, avoid moves
1080 // otherwise, a double click will select several lines.
1081 yMarginT
= yMarginB
= 0;
1083 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1084 // a maximum of slightly less than half the heigth of the text area.
1085 yMarginT
= Platform::Clamp(caretYSlop
, 1, halfScreen
);
1087 yMarginB
= yMarginT
;
1089 yMarginB
= linesOnScreen
- yMarginT
- 1;
1095 yMoveT
= Platform::Clamp(caretYSlop
* 3, 1, halfScreen
);
1099 yMoveB
= linesOnScreen
- yMoveT
- 1;
1101 if (lineCaret
< topLine
+ yMarginT
) {
1102 // Caret goes too high
1103 newXY
.topLine
= lineCaret
- yMoveT
;
1104 } else if (lineCaret
> topLine
+ linesOnScreen
- 1 - yMarginB
) {
1105 // Caret goes too low
1106 newXY
.topLine
= lineCaret
- linesOnScreen
+ 1 + yMoveB
;
1108 } else { // Not strict
1109 yMoveT
= bJump
? caretYSlop
* 3 : caretYSlop
;
1110 yMoveT
= Platform::Clamp(yMoveT
, 1, halfScreen
);
1114 yMoveB
= linesOnScreen
- yMoveT
- 1;
1116 if (lineCaret
< topLine
) {
1117 // Caret goes too high
1118 newXY
.topLine
= lineCaret
- yMoveT
;
1119 } else if (lineCaret
> topLine
+ linesOnScreen
- 1) {
1120 // Caret goes too low
1121 newXY
.topLine
= lineCaret
- linesOnScreen
+ 1 + yMoveB
;
1125 if (!bStrict
&& !bJump
) {
1127 if (lineCaret
< topLine
) {
1128 // Caret goes too high
1129 newXY
.topLine
= lineCaret
;
1130 } else if (lineCaret
> topLine
+ linesOnScreen
- 1) {
1131 // Caret goes too low
1133 newXY
.topLine
= lineCaret
- linesOnScreen
+ 1;
1135 newXY
.topLine
= lineCaret
;
1138 } else { // Strict or going out of display
1140 // Always center caret
1141 newXY
.topLine
= lineCaret
- halfScreen
;
1143 // Always put caret on top of display
1144 newXY
.topLine
= lineCaret
;
1148 newXY
.topLine
= Platform::Clamp(newXY
.topLine
, 0, MaxScrollPos());
1151 // Horizontal positioning
1152 if (horiz
&& (wrapState
== eWrapNone
)) {
1153 const int halfScreen
= Platform::Maximum(rcClient
.Width() - 4, 4) / 2;
1154 const bool bSlop
= (caretXPolicy
& CARET_SLOP
) != 0;
1155 const bool bStrict
= (caretXPolicy
& CARET_STRICT
) != 0;
1156 const bool bJump
= (caretXPolicy
& CARET_JUMPS
) != 0;
1157 const bool bEven
= (caretXPolicy
& CARET_EVEN
) != 0;
1159 if (bSlop
) { // A margin is defined
1162 int xMarginL
, xMarginR
;
1164 // In drag mode, avoid moves unless very near of the margin
1165 // otherwise, a simple click will select text.
1166 xMarginL
= xMarginR
= 2;
1168 // xMargin must equal to caretXSlop, with a minimum of 2 and
1169 // a maximum of slightly less than half the width of the text area.
1170 xMarginR
= Platform::Clamp(caretXSlop
, 2, halfScreen
);
1172 xMarginL
= xMarginR
;
1174 xMarginL
= rcClient
.Width() - xMarginR
- 4;
1177 if (bJump
&& bEven
) {
1178 // Jump is used only in even mode
1179 xMoveL
= xMoveR
= Platform::Clamp(caretXSlop
* 3, 1, halfScreen
);
1181 xMoveL
= xMoveR
= 0; // Not used, avoid a warning
1183 if (pt
.x
< rcClient
.left
+ xMarginL
) {
1184 // Caret is on the left of the display
1185 if (bJump
&& bEven
) {
1186 newXY
.xOffset
-= xMoveL
;
1188 // Move just enough to allow to display the caret
1189 newXY
.xOffset
-= (rcClient
.left
+ xMarginL
) - pt
.x
;
1191 } else if (pt
.x
>= rcClient
.right
- xMarginR
) {
1192 // Caret is on the right of the display
1193 if (bJump
&& bEven
) {
1194 newXY
.xOffset
+= xMoveR
;
1196 // Move just enough to allow to display the caret
1197 newXY
.xOffset
+= pt
.x
- (rcClient
.right
- xMarginR
) + 1;
1200 } else { // Not strict
1201 xMoveR
= bJump
? caretXSlop
* 3 : caretXSlop
;
1202 xMoveR
= Platform::Clamp(xMoveR
, 1, halfScreen
);
1206 xMoveL
= rcClient
.Width() - xMoveR
- 4;
1208 if (pt
.x
< rcClient
.left
) {
1209 // Caret is on the left of the display
1210 newXY
.xOffset
-= xMoveL
;
1211 } else if (pt
.x
>= rcClient
.right
) {
1212 // Caret is on the right of the display
1213 newXY
.xOffset
+= xMoveR
;
1218 (bJump
&& (pt
.x
< rcClient
.left
|| pt
.x
>= rcClient
.right
))) {
1219 // Strict or going out of display
1222 newXY
.xOffset
+= pt
.x
- rcClient
.left
- halfScreen
;
1224 // Put caret on right
1225 newXY
.xOffset
+= pt
.x
- rcClient
.right
+ 1;
1228 // Move just enough to allow to display the caret
1229 if (pt
.x
< rcClient
.left
) {
1230 // Caret is on the left of the display
1232 newXY
.xOffset
-= rcClient
.left
- pt
.x
;
1234 newXY
.xOffset
+= pt
.x
- rcClient
.right
+ 1;
1236 } else if (pt
.x
>= rcClient
.right
) {
1237 // Caret is on the right of the display
1238 newXY
.xOffset
+= pt
.x
- rcClient
.right
+ 1;
1242 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1243 if (pt
.x
+ xOffset
< rcClient
.left
+ newXY
.xOffset
) {
1244 newXY
.xOffset
= pt
.x
+ xOffset
- rcClient
.left
;
1245 } else if (pt
.x
+ xOffset
>= rcClient
.right
+ newXY
.xOffset
) {
1246 newXY
.xOffset
= pt
.x
+ xOffset
- rcClient
.right
+ 1;
1247 if (vs
.caretStyle
== CARETSTYLE_BLOCK
) {
1248 // Ensure we can see a good portion of the block caret
1249 newXY
.xOffset
+= vs
.aveCharWidth
;
1252 if (newXY
.xOffset
< 0) {
1260 void Editor::SetXYScroll(XYScrollPosition newXY
) {
1261 if ((newXY
.topLine
!= topLine
) || (newXY
.xOffset
!= xOffset
)) {
1262 if (newXY
.topLine
!= topLine
) {
1263 SetTopLine(newXY
.topLine
);
1264 SetVerticalScrollPos();
1266 if (newXY
.xOffset
!= xOffset
) {
1267 xOffset
= newXY
.xOffset
;
1268 if (newXY
.xOffset
> 0) {
1269 PRectangle rcText
= GetTextRectangle();
1270 if (horizontalScrollBarVisible
&&
1271 rcText
.Width() + xOffset
> scrollWidth
) {
1272 scrollWidth
= xOffset
+ rcText
.Width();
1276 SetHorizontalScrollPos();
1279 UpdateSystemCaret();
1283 void Editor::EnsureCaretVisible(bool useMargin
, bool vert
, bool horiz
) {
1284 SetXYScroll(XYScrollToMakeVisible(useMargin
, vert
, horiz
));
1287 void Editor::ShowCaretAtCurrentPosition() {
1289 caret
.active
= true;
1293 caret
.active
= false;
1299 void Editor::DropCaret() {
1300 caret
.active
= false;
1304 void Editor::InvalidateCaret() {
1305 if (posDrag
.IsValid()) {
1306 InvalidateRange(posDrag
.Position(), posDrag
.Position() + 1);
1308 for (size_t r
=0; r
<sel
.Count(); r
++) {
1309 InvalidateRange(sel
.Range(r
).caret
.Position(), sel
.Range(r
).caret
.Position() + 1);
1312 UpdateSystemCaret();
1315 void Editor::UpdateSystemCaret() {
1318 void Editor::NeedWrapping(int docLineStart
, int docLineEnd
) {
1319 docLineStart
= Platform::Clamp(docLineStart
, 0, pdoc
->LinesTotal());
1320 if (wrapStart
> docLineStart
) {
1321 wrapStart
= docLineStart
;
1322 llc
.Invalidate(LineLayout::llPositions
);
1324 if (wrapEnd
< docLineEnd
) {
1325 wrapEnd
= docLineEnd
;
1327 wrapEnd
= Platform::Clamp(wrapEnd
, 0, pdoc
->LinesTotal());
1328 // Wrap lines during idle.
1329 if ((wrapState
!= eWrapNone
) && (wrapEnd
!= wrapStart
)) {
1334 bool Editor::WrapOneLine(Surface
*surface
, int lineToWrap
) {
1335 AutoLineLayout
ll(llc
, RetrieveLineLayout(lineToWrap
));
1336 int linesWrapped
= 1;
1338 LayoutLine(lineToWrap
, surface
, vs
, ll
, wrapWidth
);
1339 linesWrapped
= ll
->lines
;
1341 return cs
.SetHeight(lineToWrap
, linesWrapped
+
1342 (vs
.annotationVisible
? pdoc
->AnnotationLines(lineToWrap
) : 0));
1345 // Check if wrapping needed and perform any needed wrapping.
1346 // fullwrap: if true, all lines which need wrapping will be done,
1347 // in this single call.
1348 // priorityWrapLineStart: If greater than or equal to zero, all lines starting from
1349 // here to 1 page + 100 lines past will be wrapped (even if there are
1350 // more lines under wrapping process in idle).
1351 // If it is neither fullwrap, nor priorityWrap, then 1 page + 100 lines will be
1352 // wrapped, if there are any wrapping going on in idle. (Generally this
1353 // condition is called only from idler).
1354 // Return true if wrapping occurred.
1355 bool Editor::WrapLines(bool fullWrap
, int priorityWrapLineStart
) {
1356 // If there are any pending wraps, do them during idle if possible.
1357 int linesInOneCall
= LinesOnScreen() + 100;
1358 if (wrapState
!= eWrapNone
) {
1359 if (wrapStart
< wrapEnd
) {
1360 if (!SetIdle(true)) {
1361 // Idle processing not supported so full wrap required.
1365 if (!fullWrap
&& priorityWrapLineStart
>= 0 &&
1366 // .. and if the paint window is outside pending wraps
1367 (((priorityWrapLineStart
+ linesInOneCall
) < wrapStart
) ||
1368 (priorityWrapLineStart
> wrapEnd
))) {
1369 // No priority wrap pending
1373 int goodTopLine
= topLine
;
1374 bool wrapOccurred
= false;
1375 if (wrapStart
<= pdoc
->LinesTotal()) {
1376 if (wrapState
== eWrapNone
) {
1377 if (wrapWidth
!= LineLayout::wrapWidthInfinite
) {
1378 wrapWidth
= LineLayout::wrapWidthInfinite
;
1379 for (int lineDoc
= 0; lineDoc
< pdoc
->LinesTotal(); lineDoc
++) {
1380 cs
.SetHeight(lineDoc
, 1 +
1381 (vs
.annotationVisible
? pdoc
->AnnotationLines(lineDoc
) : 0));
1383 wrapOccurred
= true;
1385 wrapStart
= wrapLineLarge
;
1386 wrapEnd
= wrapLineLarge
;
1388 if (wrapEnd
>= pdoc
->LinesTotal())
1389 wrapEnd
= pdoc
->LinesTotal();
1391 int lineDocTop
= cs
.DocFromDisplay(topLine
);
1392 int subLineTop
= topLine
- cs
.DisplayFromDoc(lineDocTop
);
1393 PRectangle rcTextArea
= GetClientRectangle();
1394 rcTextArea
.left
= vs
.fixedColumnWidth
;
1395 rcTextArea
.right
-= vs
.rightMarginWidth
;
1396 wrapWidth
= rcTextArea
.Width();
1397 // Ensure all of the document is styled.
1398 pdoc
->EnsureStyledTo(pdoc
->Length());
1400 AutoSurface
surface(this);
1402 bool priorityWrap
= false;
1403 int lastLineToWrap
= wrapEnd
;
1404 int lineToWrap
= wrapStart
;
1406 if (priorityWrapLineStart
>= 0) {
1407 // This is a priority wrap.
1408 lineToWrap
= priorityWrapLineStart
;
1409 lastLineToWrap
= priorityWrapLineStart
+ linesInOneCall
;
1410 priorityWrap
= true;
1412 // This is idle wrap.
1413 lastLineToWrap
= wrapStart
+ linesInOneCall
;
1415 if (lastLineToWrap
>= wrapEnd
)
1416 lastLineToWrap
= wrapEnd
;
1417 } // else do a fullWrap.
1419 // Platform::DebugPrintf("Wraplines: full = %d, priorityStart = %d (wrapping: %d to %d)\n", fullWrap, priorityWrapLineStart, lineToWrap, lastLineToWrap);
1420 // Platform::DebugPrintf("Pending wraps: %d to %d\n", wrapStart, wrapEnd);
1421 while (lineToWrap
< lastLineToWrap
) {
1422 if (WrapOneLine(surface
, lineToWrap
)) {
1423 wrapOccurred
= true;
1428 wrapStart
= lineToWrap
;
1429 // If wrapping is done, bring it to resting position
1430 if (wrapStart
>= wrapEnd
) {
1431 wrapStart
= wrapLineLarge
;
1432 wrapEnd
= wrapLineLarge
;
1435 goodTopLine
= cs
.DisplayFromDoc(lineDocTop
);
1436 if (subLineTop
< cs
.GetHeight(lineDocTop
))
1437 goodTopLine
+= subLineTop
;
1439 goodTopLine
+= cs
.GetHeight(lineDocTop
);
1440 //double durWrap = et.Duration(true);
1441 //Platform::DebugPrintf("Wrap:%9.6g \n", durWrap);
1446 SetTopLine(Platform::Clamp(goodTopLine
, 0, MaxScrollPos()));
1447 SetVerticalScrollPos();
1449 return wrapOccurred
;
1452 void Editor::LinesJoin() {
1453 if (!RangeContainsProtected(targetStart
, targetEnd
)) {
1455 bool prevNonWS
= true;
1456 for (int pos
= targetStart
; pos
< targetEnd
; pos
++) {
1457 if (IsEOLChar(pdoc
->CharAt(pos
))) {
1458 targetEnd
-= pdoc
->LenChar(pos
);
1461 // Ensure at least one space separating previous lines
1462 pdoc
->InsertChar(pos
, ' ');
1466 prevNonWS
= pdoc
->CharAt(pos
) != ' ';
1472 const char *Editor::StringFromEOLMode(int eolMode
) {
1473 if (eolMode
== SC_EOL_CRLF
) {
1475 } else if (eolMode
== SC_EOL_CR
) {
1482 void Editor::LinesSplit(int pixelWidth
) {
1483 if (!RangeContainsProtected(targetStart
, targetEnd
)) {
1484 if (pixelWidth
== 0) {
1485 PRectangle rcText
= GetTextRectangle();
1486 pixelWidth
= rcText
.Width();
1488 int lineStart
= pdoc
->LineFromPosition(targetStart
);
1489 int lineEnd
= pdoc
->LineFromPosition(targetEnd
);
1490 const char *eol
= StringFromEOLMode(pdoc
->eolMode
);
1492 for (int line
= lineStart
; line
<= lineEnd
; line
++) {
1493 AutoSurface
surface(this);
1494 AutoLineLayout
ll(llc
, RetrieveLineLayout(line
));
1495 if (surface
&& ll
) {
1496 unsigned int posLineStart
= pdoc
->LineStart(line
);
1497 LayoutLine(line
, surface
, vs
, ll
, pixelWidth
);
1498 for (int subLine
= 1; subLine
< ll
->lines
; subLine
++) {
1499 pdoc
->InsertCString(posLineStart
+ (subLine
- 1) * strlen(eol
) +
1500 ll
->LineStart(subLine
), eol
);
1501 targetEnd
+= static_cast<int>(strlen(eol
));
1504 lineEnd
= pdoc
->LineFromPosition(targetEnd
);
1509 int Editor::SubstituteMarkerIfEmpty(int markerCheck
, int markerDefault
) {
1510 if (vs
.markers
[markerCheck
].markType
== SC_MARK_EMPTY
)
1511 return markerDefault
;
1515 // Avoid 64 bit compiler warnings.
1516 // Scintilla does not support text buffers larger than 2**31
1517 static int istrlen(const char *s
) {
1518 return static_cast<int>(strlen(s
));
1521 bool ValidStyledText(ViewStyle
&vs
, size_t styleOffset
, const StyledText
&st
) {
1522 if (st
.multipleStyles
) {
1523 for (size_t iStyle
=0; iStyle
<st
.length
; iStyle
++) {
1524 if (!vs
.ValidStyle(styleOffset
+ st
.styles
[iStyle
]))
1528 if (!vs
.ValidStyle(styleOffset
+ st
.style
))
1534 static int WidthStyledText(Surface
*surface
, ViewStyle
&vs
, int styleOffset
,
1535 const char *text
, const unsigned char *styles
, size_t len
) {
1538 while (start
< len
) {
1539 size_t style
= styles
[start
];
1540 size_t endSegment
= start
;
1541 while ((endSegment
+1 < len
) && (static_cast<size_t>(styles
[endSegment
+1]) == style
))
1543 width
+= surface
->WidthText(vs
.styles
[style
+styleOffset
].font
, text
+ start
, endSegment
- start
+ 1);
1544 start
= endSegment
+ 1;
1549 static int WidestLineWidth(Surface
*surface
, ViewStyle
&vs
, int styleOffset
, const StyledText
&st
) {
1552 while (start
< st
.length
) {
1553 size_t lenLine
= st
.LineLength(start
);
1555 if (st
.multipleStyles
) {
1556 widthSubLine
= WidthStyledText(surface
, vs
, styleOffset
, st
.text
+ start
, st
.styles
+ start
, lenLine
);
1558 widthSubLine
= surface
->WidthText(vs
.styles
[styleOffset
+ st
.style
].font
, st
.text
+ start
, lenLine
);
1560 if (widthSubLine
> widthMax
)
1561 widthMax
= widthSubLine
;
1562 start
+= lenLine
+ 1;
1567 void DrawStyledText(Surface
*surface
, ViewStyle
&vs
, int styleOffset
, PRectangle rcText
, int ascent
,
1568 const StyledText
&st
, size_t start
, size_t length
) {
1570 if (st
.multipleStyles
) {
1571 int x
= rcText
.left
;
1573 while (i
< length
) {
1575 int style
= st
.styles
[i
+ start
];
1576 while (end
< length
-1 && st
.styles
[start
+end
+1] == style
)
1578 style
+= styleOffset
;
1579 int width
= surface
->WidthText(vs
.styles
[style
].font
, st
.text
+ start
+ i
, end
- i
+ 1);
1580 PRectangle rcSegment
= rcText
;
1582 rcSegment
.right
= x
+ width
+ 1;
1583 surface
->DrawTextNoClip(rcSegment
, vs
.styles
[style
].font
,
1584 ascent
, st
.text
+ start
+ i
, end
- i
+ 1,
1585 vs
.styles
[style
].fore
.allocated
,
1586 vs
.styles
[style
].back
.allocated
);
1591 int style
= st
.style
+ styleOffset
;
1592 surface
->DrawTextNoClip(rcText
, vs
.styles
[style
].font
,
1593 rcText
.top
+ vs
.maxAscent
, st
.text
+ start
, length
,
1594 vs
.styles
[style
].fore
.allocated
,
1595 vs
.styles
[style
].back
.allocated
);
1599 void Editor::PaintSelMargin(Surface
*surfWindow
, PRectangle
&rc
) {
1600 if (vs
.fixedColumnWidth
== 0)
1603 PRectangle rcMargin
= GetClientRectangle();
1604 rcMargin
.right
= vs
.fixedColumnWidth
;
1606 if (!rc
.Intersects(rcMargin
))
1611 surface
= pixmapSelMargin
;
1613 surface
= surfWindow
;
1616 PRectangle rcSelMargin
= rcMargin
;
1617 rcSelMargin
.right
= rcMargin
.left
;
1619 for (int margin
= 0; margin
< vs
.margins
; margin
++) {
1620 if (vs
.ms
[margin
].width
> 0) {
1622 rcSelMargin
.left
= rcSelMargin
.right
;
1623 rcSelMargin
.right
= rcSelMargin
.left
+ vs
.ms
[margin
].width
;
1625 if (vs
.ms
[margin
].style
!= SC_MARGIN_NUMBER
) {
1626 /* alternate scheme:
1627 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
1628 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_DEFAULT].back.allocated);
1630 // Required because of special way brush is created for selection margin
1631 surface->FillRectangle(rcSelMargin, pixmapSelPattern);
1633 if (vs
.ms
[margin
].mask
& SC_MASK_FOLDERS
)
1634 // Required because of special way brush is created for selection margin
1635 surface
->FillRectangle(rcSelMargin
, *pixmapSelPattern
);
1637 ColourAllocated colour
;
1638 switch (vs
.ms
[margin
].style
) {
1639 case SC_MARGIN_BACK
:
1640 colour
= vs
.styles
[STYLE_DEFAULT
].back
.allocated
;
1642 case SC_MARGIN_FORE
:
1643 colour
= vs
.styles
[STYLE_DEFAULT
].fore
.allocated
;
1646 colour
= vs
.styles
[STYLE_LINENUMBER
].back
.allocated
;
1649 surface
->FillRectangle(rcSelMargin
, colour
);
1652 surface
->FillRectangle(rcSelMargin
, vs
.styles
[STYLE_LINENUMBER
].back
.allocated
);
1655 int visibleLine
= topLine
;
1658 // Work out whether the top line is whitespace located after a
1659 // lessening of fold level which implies a 'fold tail' but which should not
1660 // be displayed until the last of a sequence of whitespace.
1661 bool needWhiteClosure
= false;
1662 int level
= pdoc
->GetLevel(cs
.DocFromDisplay(topLine
));
1663 if (level
& SC_FOLDLEVELWHITEFLAG
) {
1664 int lineBack
= cs
.DocFromDisplay(topLine
);
1665 int levelPrev
= level
;
1666 while ((lineBack
> 0) && (levelPrev
& SC_FOLDLEVELWHITEFLAG
)) {
1668 levelPrev
= pdoc
->GetLevel(lineBack
);
1670 if (!(levelPrev
& SC_FOLDLEVELHEADERFLAG
)) {
1671 if ((level
& SC_FOLDLEVELNUMBERMASK
) < (levelPrev
& SC_FOLDLEVELNUMBERMASK
))
1672 needWhiteClosure
= true;
1676 // Old code does not know about new markers needed to distinguish all cases
1677 int folderOpenMid
= SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID
,
1678 SC_MARKNUM_FOLDEROPEN
);
1679 int folderEnd
= SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND
,
1682 while ((visibleLine
< cs
.LinesDisplayed()) && yposScreen
< rcMargin
.bottom
) {
1684 PLATFORM_ASSERT(visibleLine
< cs
.LinesDisplayed());
1686 int lineDoc
= cs
.DocFromDisplay(visibleLine
);
1687 PLATFORM_ASSERT(cs
.GetVisible(lineDoc
));
1688 bool firstSubLine
= visibleLine
== cs
.DisplayFromDoc(lineDoc
);
1690 // Decide which fold indicator should be displayed
1691 level
= pdoc
->GetLevel(lineDoc
);
1692 int levelNext
= pdoc
->GetLevel(lineDoc
+ 1);
1693 int marks
= pdoc
->GetMark(lineDoc
);
1696 int levelNum
= level
& SC_FOLDLEVELNUMBERMASK
;
1697 int levelNextNum
= levelNext
& SC_FOLDLEVELNUMBERMASK
;
1698 if (level
& SC_FOLDLEVELHEADERFLAG
) {
1700 if (cs
.GetExpanded(lineDoc
)) {
1701 if (levelNum
== SC_FOLDLEVELBASE
)
1702 marks
|= 1 << SC_MARKNUM_FOLDEROPEN
;
1704 marks
|= 1 << folderOpenMid
;
1706 if (levelNum
== SC_FOLDLEVELBASE
)
1707 marks
|= 1 << SC_MARKNUM_FOLDER
;
1709 marks
|= 1 << folderEnd
;
1712 marks
|= 1 << SC_MARKNUM_FOLDERSUB
;
1714 needWhiteClosure
= false;
1715 } else if (level
& SC_FOLDLEVELWHITEFLAG
) {
1716 if (needWhiteClosure
) {
1717 if (levelNext
& SC_FOLDLEVELWHITEFLAG
) {
1718 marks
|= 1 << SC_MARKNUM_FOLDERSUB
;
1719 } else if (levelNum
> SC_FOLDLEVELBASE
) {
1720 marks
|= 1 << SC_MARKNUM_FOLDERMIDTAIL
;
1721 needWhiteClosure
= false;
1723 marks
|= 1 << SC_MARKNUM_FOLDERTAIL
;
1724 needWhiteClosure
= false;
1726 } else if (levelNum
> SC_FOLDLEVELBASE
) {
1727 if (levelNextNum
< levelNum
) {
1728 if (levelNextNum
> SC_FOLDLEVELBASE
) {
1729 marks
|= 1 << SC_MARKNUM_FOLDERMIDTAIL
;
1731 marks
|= 1 << SC_MARKNUM_FOLDERTAIL
;
1734 marks
|= 1 << SC_MARKNUM_FOLDERSUB
;
1737 } else if (levelNum
> SC_FOLDLEVELBASE
) {
1738 if (levelNextNum
< levelNum
) {
1739 needWhiteClosure
= false;
1740 if (levelNext
& SC_FOLDLEVELWHITEFLAG
) {
1741 marks
|= 1 << SC_MARKNUM_FOLDERSUB
;
1742 needWhiteClosure
= true;
1743 } else if (levelNextNum
> SC_FOLDLEVELBASE
) {
1744 marks
|= 1 << SC_MARKNUM_FOLDERMIDTAIL
;
1746 marks
|= 1 << SC_MARKNUM_FOLDERTAIL
;
1749 marks
|= 1 << SC_MARKNUM_FOLDERSUB
;
1753 marks
&= vs
.ms
[margin
].mask
;
1754 PRectangle rcMarker
= rcSelMargin
;
1755 rcMarker
.top
= yposScreen
;
1756 rcMarker
.bottom
= yposScreen
+ vs
.lineHeight
;
1757 if (vs
.ms
[margin
].style
== SC_MARGIN_NUMBER
) {
1761 sprintf(number
, "%d", lineDoc
+ 1);
1762 if (foldFlags
& SC_FOLDFLAG_LEVELNUMBERS
) {
1763 int lev
= pdoc
->GetLevel(lineDoc
);
1764 sprintf(number
, "%c%c %03X %03X",
1765 (lev
& SC_FOLDLEVELHEADERFLAG
) ? 'H' : '_',
1766 (lev
& SC_FOLDLEVELWHITEFLAG
) ? 'W' : '_',
1767 lev
& SC_FOLDLEVELNUMBERMASK
,
1771 PRectangle rcNumber
= rcMarker
;
1773 int width
= surface
->WidthText(vs
.styles
[STYLE_LINENUMBER
].font
, number
, istrlen(number
));
1774 int xpos
= rcNumber
.right
- width
- 3;
1775 rcNumber
.left
= xpos
;
1776 surface
->DrawTextNoClip(rcNumber
, vs
.styles
[STYLE_LINENUMBER
].font
,
1777 rcNumber
.top
+ vs
.maxAscent
, number
, istrlen(number
),
1778 vs
.styles
[STYLE_LINENUMBER
].fore
.allocated
,
1779 vs
.styles
[STYLE_LINENUMBER
].back
.allocated
);
1780 } else if (vs
.ms
[margin
].style
== SC_MARGIN_TEXT
|| vs
.ms
[margin
].style
== SC_MARGIN_RTEXT
) {
1782 const StyledText stMargin
= pdoc
->MarginStyledText(lineDoc
);
1783 if (stMargin
.text
&& ValidStyledText(vs
, vs
.marginStyleOffset
, stMargin
)) {
1784 surface
->FillRectangle(rcMarker
,
1785 vs
.styles
[stMargin
.StyleAt(0)+vs
.marginStyleOffset
].back
.allocated
);
1786 if (vs
.ms
[margin
].style
== SC_MARGIN_RTEXT
) {
1787 int width
= WidestLineWidth(surface
, vs
, vs
.marginStyleOffset
, stMargin
);
1788 rcMarker
.left
= rcMarker
.right
- width
- 3;
1790 DrawStyledText(surface
, vs
, vs
.marginStyleOffset
, rcMarker
, rcMarker
.top
+ vs
.maxAscent
,
1791 stMargin
, 0, stMargin
.length
);
1797 for (int markBit
= 0; (markBit
< 32) && marks
; markBit
++) {
1799 vs
.markers
[markBit
].Draw(surface
, rcMarker
, vs
.styles
[STYLE_LINENUMBER
].font
);
1806 yposScreen
+= vs
.lineHeight
;
1811 PRectangle rcBlankMargin
= rcMargin
;
1812 rcBlankMargin
.left
= rcSelMargin
.right
;
1813 surface
->FillRectangle(rcBlankMargin
, vs
.styles
[STYLE_DEFAULT
].back
.allocated
);
1816 surfWindow
->Copy(rcMargin
, Point(), *pixmapSelMargin
);
1820 void DrawTabArrow(Surface
*surface
, PRectangle rcTab
, int ymid
) {
1821 int ydiff
= (rcTab
.bottom
- rcTab
.top
) / 2;
1822 int xhead
= rcTab
.right
- 1 - ydiff
;
1823 if (xhead
<= rcTab
.left
) {
1824 ydiff
-= rcTab
.left
- xhead
- 1;
1825 xhead
= rcTab
.left
- 1;
1827 if ((rcTab
.left
+ 2) < (rcTab
.right
- 1))
1828 surface
->MoveTo(rcTab
.left
+ 2, ymid
);
1830 surface
->MoveTo(rcTab
.right
- 1, ymid
);
1831 surface
->LineTo(rcTab
.right
- 1, ymid
);
1832 surface
->LineTo(xhead
, ymid
- ydiff
);
1833 surface
->MoveTo(rcTab
.right
- 1, ymid
);
1834 surface
->LineTo(xhead
, ymid
+ ydiff
);
1837 LineLayout
*Editor::RetrieveLineLayout(int lineNumber
) {
1838 int posLineStart
= pdoc
->LineStart(lineNumber
);
1839 int posLineEnd
= pdoc
->LineStart(lineNumber
+ 1);
1840 PLATFORM_ASSERT(posLineEnd
>= posLineStart
);
1841 int lineCaret
= pdoc
->LineFromPosition(sel
.MainCaret());
1842 return llc
.Retrieve(lineNumber
, lineCaret
,
1843 posLineEnd
- posLineStart
, pdoc
->GetStyleClock(),
1844 LinesOnScreen() + 1, pdoc
->LinesTotal());
1847 static bool GoodTrailByte(int v
) {
1848 return (v
>= 0x80) && (v
< 0xc0);
1851 bool BadUTF(const char *s
, int len
, int &trailBytes
) {
1852 // For the rules: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1857 const unsigned char *us
= reinterpret_cast<const unsigned char *>(s
);
1859 // Single bytes easy
1861 } else if (*us
> 0xF4) {
1862 // Characters longer than 4 bytes not possible in current UTF-8
1864 } else if (*us
>= 0xF0) {
1868 if (GoodTrailByte(us
[1]) && GoodTrailByte(us
[2]) && GoodTrailByte(us
[3])) {
1870 // Check if encoding a value beyond the last Unicode character 10FFFF
1873 } else if (us
[1] == 0x8f) {
1876 } else if (us
[2] == 0xbf) {
1882 } else if ((*us
== 0xf0) && ((us
[1] & 0xf0) == 0x80)) {
1891 } else if (*us
>= 0xE0) {
1895 if (GoodTrailByte(us
[1]) && GoodTrailByte(us
[2])) {
1896 if ((*us
== 0xe0) && ((us
[1] & 0xe0) == 0x80)) {
1900 if ((*us
== 0xed) && ((us
[1] & 0xe0) == 0xa0)) {
1904 if ((*us
== 0xef) && (us
[1] == 0xbf) && (us
[2] == 0xbe)) {
1908 if ((*us
== 0xef) && (us
[1] == 0xbf) && (us
[2] == 0xbf)) {
1917 } else if (*us
>= 0xC2) {
1921 if (GoodTrailByte(us
[1])) {
1927 } else if (*us
>= 0xC0) {
1928 // Overlong encoding
1937 * Fill in the LineLayout data for the given line.
1938 * Copy the given @a line and its styles from the document into local arrays.
1939 * Also determine the x position at which each character starts.
1941 void Editor::LayoutLine(int line
, Surface
*surface
, ViewStyle
&vstyle
, LineLayout
*ll
, int width
) {
1945 PLATFORM_ASSERT(line
< pdoc
->LinesTotal());
1946 PLATFORM_ASSERT(ll
->chars
!= NULL
);
1947 int posLineStart
= pdoc
->LineStart(line
);
1948 int posLineEnd
= pdoc
->LineStart(line
+ 1);
1949 // If the line is very long, limit the treatment to a length that should fit in the viewport
1950 if (posLineEnd
> (posLineStart
+ ll
->maxLineLength
)) {
1951 posLineEnd
= posLineStart
+ ll
->maxLineLength
;
1953 if (ll
->validity
== LineLayout::llCheckTextAndStyle
) {
1954 int lineLength
= posLineEnd
- posLineStart
;
1955 if (!vstyle
.viewEOL
) {
1956 int cid
= posLineEnd
- 1;
1957 while ((cid
> posLineStart
) && IsEOLChar(pdoc
->CharAt(cid
))) {
1962 if (lineLength
== ll
->numCharsInLine
) {
1963 // See if chars, styles, indicators, are all the same
1964 bool allSame
= true;
1965 const int styleMask
= pdoc
->stylingBitsMask
;
1966 // Check base line layout
1968 int numCharsInLine
= 0;
1969 while (numCharsInLine
< lineLength
) {
1970 int charInDoc
= numCharsInLine
+ posLineStart
;
1971 char chDoc
= pdoc
->CharAt(charInDoc
);
1972 styleByte
= pdoc
->StyleAt(charInDoc
);
1973 allSame
= allSame
&&
1974 (ll
->styles
[numCharsInLine
] == static_cast<unsigned char>(styleByte
& styleMask
));
1975 allSame
= allSame
&&
1976 (ll
->indicators
[numCharsInLine
] == static_cast<char>(styleByte
& ~styleMask
));
1977 if (vstyle
.styles
[ll
->styles
[numCharsInLine
]].caseForce
== Style::caseMixed
)
1978 allSame
= allSame
&&
1979 (ll
->chars
[numCharsInLine
] == chDoc
);
1980 else if (vstyle
.styles
[ll
->styles
[numCharsInLine
]].caseForce
== Style::caseLower
)
1981 allSame
= allSame
&&
1982 (ll
->chars
[numCharsInLine
] == static_cast<char>(tolower(chDoc
)));
1983 else // Style::caseUpper
1984 allSame
= allSame
&&
1985 (ll
->chars
[numCharsInLine
] == static_cast<char>(toupper(chDoc
)));
1988 allSame
= allSame
&& (ll
->styles
[numCharsInLine
] == styleByte
); // For eolFilled
1990 ll
->validity
= LineLayout::llPositions
;
1992 ll
->validity
= LineLayout::llInvalid
;
1995 ll
->validity
= LineLayout::llInvalid
;
1998 if (ll
->validity
== LineLayout::llInvalid
) {
1999 ll
->widthLine
= LineLayout::wrapWidthInfinite
;
2001 int numCharsInLine
= 0;
2002 int numCharsBeforeEOL
= 0;
2003 if (vstyle
.edgeState
== EDGE_BACKGROUND
) {
2004 ll
->edgeColumn
= pdoc
->FindColumn(line
, theEdge
);
2005 if (ll
->edgeColumn
>= posLineStart
) {
2006 ll
->edgeColumn
-= posLineStart
;
2009 ll
->edgeColumn
= -1;
2013 int styleMask
= pdoc
->stylingBitsMask
;
2014 ll
->styleBitsSet
= 0;
2015 // Fill base line layout
2016 for (int charInDoc
= posLineStart
; charInDoc
< posLineEnd
; charInDoc
++) {
2017 char chDoc
= pdoc
->CharAt(charInDoc
);
2018 styleByte
= pdoc
->StyleAt(charInDoc
);
2019 ll
->styleBitsSet
|= styleByte
;
2020 if (vstyle
.viewEOL
|| (!IsEOLChar(chDoc
))) {
2021 ll
->chars
[numCharsInLine
] = chDoc
;
2022 ll
->styles
[numCharsInLine
] = static_cast<char>(styleByte
& styleMask
);
2023 ll
->indicators
[numCharsInLine
] = static_cast<char>(styleByte
& ~styleMask
);
2024 if (vstyle
.styles
[ll
->styles
[numCharsInLine
]].caseForce
== Style::caseUpper
)
2025 ll
->chars
[numCharsInLine
] = static_cast<char>(toupper(chDoc
));
2026 else if (vstyle
.styles
[ll
->styles
[numCharsInLine
]].caseForce
== Style::caseLower
)
2027 ll
->chars
[numCharsInLine
] = static_cast<char>(tolower(chDoc
));
2029 if (!IsEOLChar(chDoc
))
2030 numCharsBeforeEOL
++;
2033 ll
->xHighlightGuide
= 0;
2034 // Extra element at the end of the line to hold end x position and act as
2035 ll
->chars
[numCharsInLine
] = 0; // Also triggers processing in the loops as this is a control character
2036 ll
->styles
[numCharsInLine
] = styleByte
; // For eolFilled
2037 ll
->indicators
[numCharsInLine
] = 0;
2039 // Layout the line, determining the position of each character,
2040 // with an extra element at the end for the end of the line.
2041 int startseg
= 0; // Start of the current segment, in char. number
2042 int startsegx
= 0; // Start of the current segment, in pixels
2043 ll
->positions
[0] = 0;
2044 unsigned int tabWidth
= vstyle
.spaceWidth
* pdoc
->tabInChars
;
2045 bool lastSegItalics
= false;
2046 Font
&ctrlCharsFont
= vstyle
.styles
[STYLE_CONTROLCHAR
].font
;
2048 int ctrlCharWidth
[32] = {0};
2049 bool isControlNext
= IsControlCharacter(ll
->chars
[0]);
2051 bool isBadUTFNext
= IsUnicodeMode() && BadUTF(ll
->chars
, numCharsInLine
, trailBytes
);
2052 for (int charInLine
= 0; charInLine
< numCharsInLine
; charInLine
++) {
2053 bool isControl
= isControlNext
;
2054 isControlNext
= IsControlCharacter(ll
->chars
[charInLine
+ 1]);
2055 bool isBadUTF
= isBadUTFNext
;
2056 isBadUTFNext
= IsUnicodeMode() && BadUTF(ll
->chars
+ charInLine
+ 1, numCharsInLine
- charInLine
- 1, trailBytes
);
2057 if ((ll
->styles
[charInLine
] != ll
->styles
[charInLine
+ 1]) ||
2058 isControl
|| isControlNext
|| isBadUTF
|| isBadUTFNext
) {
2059 ll
->positions
[startseg
] = 0;
2060 if (vstyle
.styles
[ll
->styles
[charInLine
]].visible
) {
2062 if (ll
->chars
[charInLine
] == '\t') {
2063 ll
->positions
[charInLine
+ 1] = ((((startsegx
+ 2) /
2064 tabWidth
) + 1) * tabWidth
) - startsegx
;
2065 } else if (controlCharSymbol
< 32) {
2066 if (ctrlCharWidth
[ll
->chars
[charInLine
]] == 0) {
2067 const char *ctrlChar
= ControlCharacterString(ll
->chars
[charInLine
]);
2068 // +3 For a blank on front and rounded edge each side:
2069 ctrlCharWidth
[ll
->chars
[charInLine
]] =
2070 surface
->WidthText(ctrlCharsFont
, ctrlChar
, istrlen(ctrlChar
)) + 3;
2072 ll
->positions
[charInLine
+ 1] = ctrlCharWidth
[ll
->chars
[charInLine
]];
2074 char cc
[2] = { static_cast<char>(controlCharSymbol
), '\0' };
2075 surface
->MeasureWidths(ctrlCharsFont
, cc
, 1,
2076 ll
->positions
+ startseg
+ 1);
2078 lastSegItalics
= false;
2079 } else if (isBadUTF
) {
2081 sprintf(hexits
, "x%2X", ll
->chars
[charInLine
] & 0xff);
2082 ll
->positions
[charInLine
+ 1] =
2083 surface
->WidthText(ctrlCharsFont
, hexits
, istrlen(hexits
)) + 3;
2084 } else { // Regular character
2085 int lenSeg
= charInLine
- startseg
+ 1;
2086 if ((lenSeg
== 1) && (' ' == ll
->chars
[startseg
])) {
2087 lastSegItalics
= false;
2088 // Over half the segments are single characters and of these about half are space characters.
2089 ll
->positions
[charInLine
+ 1] = vstyle
.styles
[ll
->styles
[charInLine
]].spaceWidth
;
2091 lastSegItalics
= vstyle
.styles
[ll
->styles
[charInLine
]].italic
;
2092 posCache
.MeasureWidths(surface
, vstyle
, ll
->styles
[charInLine
], ll
->chars
+ startseg
,
2093 lenSeg
, ll
->positions
+ startseg
+ 1);
2096 } else { // invisible
2097 for (int posToZero
= startseg
; posToZero
<= (charInLine
+ 1); posToZero
++) {
2098 ll
->positions
[posToZero
] = 0;
2101 for (int posToIncrease
= startseg
; posToIncrease
<= (charInLine
+ 1); posToIncrease
++) {
2102 ll
->positions
[posToIncrease
] += startsegx
;
2104 startsegx
= ll
->positions
[charInLine
+ 1];
2105 startseg
= charInLine
+ 1;
2108 // Small hack to make lines that end with italics not cut off the edge of the last character
2109 if ((startseg
> 0) && lastSegItalics
) {
2110 ll
->positions
[startseg
] += 2;
2112 ll
->numCharsInLine
= numCharsInLine
;
2113 ll
->numCharsBeforeEOL
= numCharsBeforeEOL
;
2114 ll
->validity
= LineLayout::llPositions
;
2116 // Hard to cope when too narrow, so just assume there is space
2120 if ((ll
->validity
== LineLayout::llPositions
) || (ll
->widthLine
!= width
)) {
2121 ll
->widthLine
= width
;
2122 if (width
== LineLayout::wrapWidthInfinite
) {
2124 } else if (width
> ll
->positions
[ll
->numCharsInLine
]) {
2125 // Simple common case where line does not need wrapping.
2128 if (wrapVisualFlags
& SC_WRAPVISUALFLAG_END
) {
2129 width
-= vstyle
.aveCharWidth
; // take into account the space for end wrap mark
2131 ll
->wrapIndent
= wrapAddIndent
;
2132 if (wrapIndentMode
!= SC_WRAPINDENT_FIXED
)
2133 for (int i
= 0; i
< ll
->numCharsInLine
; i
++) {
2134 if (!IsSpaceOrTab(ll
->chars
[i
])) {
2135 ll
->wrapIndent
+= ll
->positions
[i
]; // Add line indent
2139 // Check for text width minimum
2140 if (ll
->wrapIndent
> width
- static_cast<int>(vstyle
.aveCharWidth
) * 15)
2141 ll
->wrapIndent
= wrapAddIndent
;
2142 // Check for wrapIndent minimum
2143 if ((wrapVisualFlags
& SC_WRAPVISUALFLAG_START
) && (ll
->wrapIndent
< static_cast<int>(vstyle
.aveCharWidth
)))
2144 ll
->wrapIndent
= vstyle
.aveCharWidth
; // Indent to show start visual
2146 // Calculate line start positions based upon width.
2147 int lastGoodBreak
= 0;
2148 int lastLineStart
= 0;
2149 int startOffset
= 0;
2151 while (p
< ll
->numCharsInLine
) {
2152 if ((ll
->positions
[p
+ 1] - startOffset
) >= width
) {
2153 if (lastGoodBreak
== lastLineStart
) {
2154 // Try moving to start of last character
2156 lastGoodBreak
= pdoc
->MovePositionOutsideChar(p
+ posLineStart
, -1)
2159 if (lastGoodBreak
== lastLineStart
) {
2160 // Ensure at least one character on line.
2161 lastGoodBreak
= pdoc
->MovePositionOutsideChar(lastGoodBreak
+ posLineStart
+ 1, 1)
2165 lastLineStart
= lastGoodBreak
;
2167 ll
->SetLineStart(ll
->lines
, lastGoodBreak
);
2168 startOffset
= ll
->positions
[lastGoodBreak
];
2169 // take into account the space for start wrap mark and indent
2170 startOffset
-= ll
->wrapIndent
;
2171 p
= lastGoodBreak
+ 1;
2175 if (wrapState
== eWrapChar
) {
2176 lastGoodBreak
= pdoc
->MovePositionOutsideChar(p
+ posLineStart
, -1)
2178 p
= pdoc
->MovePositionOutsideChar(p
+ 1 + posLineStart
, 1) - posLineStart
;
2180 } else if (ll
->styles
[p
] != ll
->styles
[p
- 1]) {
2182 } else if (IsSpaceOrTab(ll
->chars
[p
- 1]) && !IsSpaceOrTab(ll
->chars
[p
])) {
2190 ll
->validity
= LineLayout::llLines
;
2194 ColourAllocated
Editor::SelectionBackground(ViewStyle
&vsDraw
, bool main
) {
2196 (primarySelection
? vsDraw
.selbackground
.allocated
: vsDraw
.selbackground2
.allocated
) :
2197 vsDraw
.selAdditionalBackground
.allocated
;
2200 ColourAllocated
Editor::TextBackground(ViewStyle
&vsDraw
, bool overrideBackground
,
2201 ColourAllocated background
, int inSelection
, bool inHotspot
, int styleMain
, int i
, LineLayout
*ll
) {
2202 if (inSelection
== 1) {
2203 if (vsDraw
.selbackset
&& (vsDraw
.selAlpha
== SC_ALPHA_NOALPHA
)) {
2204 return SelectionBackground(vsDraw
, true);
2206 } else if (inSelection
== 2) {
2207 if (vsDraw
.selbackset
&& (vsDraw
.selAdditionalAlpha
== SC_ALPHA_NOALPHA
)) {
2208 return SelectionBackground(vsDraw
, false);
2211 if ((vsDraw
.edgeState
== EDGE_BACKGROUND
) &&
2212 (i
>= ll
->edgeColumn
) &&
2213 !IsEOLChar(ll
->chars
[i
]))
2214 return vsDraw
.edgecolour
.allocated
;
2215 if (inHotspot
&& vsDraw
.hotspotBackgroundSet
)
2216 return vsDraw
.hotspotBackground
.allocated
;
2217 if (overrideBackground
&& (styleMain
!= STYLE_BRACELIGHT
) && (styleMain
!= STYLE_BRACEBAD
))
2220 return vsDraw
.styles
[styleMain
].back
.allocated
;
2223 void Editor::DrawIndentGuide(Surface
*surface
, int lineVisible
, int lineHeight
, int start
, PRectangle rcSegment
, bool highlight
) {
2224 Point
from(0, ((lineVisible
& 1) && (lineHeight
& 1)) ? 1 : 0);
2225 PRectangle
rcCopyArea(start
+ 1, rcSegment
.top
, start
+ 2, rcSegment
.bottom
);
2226 surface
->Copy(rcCopyArea
, from
,
2227 highlight
? *pixmapIndentGuideHighlight
: *pixmapIndentGuide
);
2230 void Editor::DrawWrapMarker(Surface
*surface
, PRectangle rcPlace
,
2231 bool isEndMarker
, ColourAllocated wrapColour
) {
2232 surface
->PenColour(wrapColour
);
2234 enum { xa
= 1 }; // gap before start
2235 int w
= rcPlace
.right
- rcPlace
.left
- xa
- 1;
2237 bool xStraight
= isEndMarker
; // x-mirrored symbol for start marker
2238 bool yStraight
= true;
2239 //bool yStraight= isEndMarker; // comment in for start marker y-mirrowed
2241 int x0
= xStraight
? rcPlace
.left
: rcPlace
.right
- 1;
2242 int y0
= yStraight
? rcPlace
.top
: rcPlace
.bottom
- 1;
2244 int dy
= (rcPlace
.bottom
- rcPlace
.top
) / 5;
2245 int y
= (rcPlace
.bottom
- rcPlace
.top
) / 2 + dy
;
2253 void MoveTo(int xRelative
, int yRelative
) {
2254 surface
->MoveTo(xBase
+ xDir
* xRelative
, yBase
+ yDir
* yRelative
);
2256 void LineTo(int xRelative
, int yRelative
) {
2257 surface
->LineTo(xBase
+ xDir
* xRelative
, yBase
+ yDir
* yRelative
);
2260 Relative rel
= {surface
, x0
, xStraight
? 1 : -1, y0
, yStraight
? 1 : -1};
2264 rel
.LineTo(xa
+ 2*w
/ 3, y
- dy
);
2266 rel
.LineTo(xa
+ 2*w
/ 3, y
+ dy
);
2270 rel
.LineTo(xa
+ w
, y
);
2271 rel
.LineTo(xa
+ w
, y
- 2 * dy
);
2272 rel
.LineTo(xa
- 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
2276 static void SimpleAlphaRectangle(Surface
*surface
, PRectangle rc
, ColourAllocated fill
, int alpha
) {
2277 if (alpha
!= SC_ALPHA_NOALPHA
) {
2278 surface
->AlphaRectangle(rc
, 0, fill
, alpha
, fill
, alpha
, 0);
2282 void DrawTextBlob(Surface
*surface
, ViewStyle
&vsDraw
, PRectangle rcSegment
,
2283 const char *s
, ColourAllocated textBack
, ColourAllocated textFore
, bool twoPhaseDraw
) {
2284 if (!twoPhaseDraw
) {
2285 surface
->FillRectangle(rcSegment
, textBack
);
2287 Font
&ctrlCharsFont
= vsDraw
.styles
[STYLE_CONTROLCHAR
].font
;
2288 int normalCharHeight
= surface
->Ascent(ctrlCharsFont
) -
2289 surface
->InternalLeading(ctrlCharsFont
);
2290 PRectangle rcCChar
= rcSegment
;
2291 rcCChar
.left
= rcCChar
.left
+ 1;
2292 rcCChar
.top
= rcSegment
.top
+ vsDraw
.maxAscent
- normalCharHeight
;
2293 rcCChar
.bottom
= rcSegment
.top
+ vsDraw
.maxAscent
+ 1;
2294 PRectangle rcCentral
= rcCChar
;
2297 surface
->FillRectangle(rcCentral
, textFore
);
2298 PRectangle rcChar
= rcCChar
;
2301 surface
->DrawTextClipped(rcChar
, ctrlCharsFont
,
2302 rcSegment
.top
+ vsDraw
.maxAscent
, s
, istrlen(s
),
2303 textBack
, textFore
);
2306 void Editor::DrawEOL(Surface
*surface
, ViewStyle
&vsDraw
, PRectangle rcLine
, LineLayout
*ll
,
2307 int line
, int lineEnd
, int xStart
, int subLine
, int subLineStart
,
2308 bool overrideBackground
, ColourAllocated background
,
2309 bool drawWrapMarkEnd
, ColourAllocated wrapColour
) {
2311 const int posLineStart
= pdoc
->LineStart(line
);
2312 const int styleMask
= pdoc
->stylingBitsMask
;
2313 PRectangle rcSegment
= rcLine
;
2315 const bool lastSubLine
= subLine
== (ll
->lines
- 1);
2316 int virtualSpace
= 0;
2318 const int spaceWidth
= static_cast<int>(vsDraw
.styles
[ll
->EndLineStyle()].spaceWidth
);
2319 virtualSpace
= sel
.VirtualSpaceFor(pdoc
->LineEnd(line
)) * spaceWidth
;
2322 // Fill in a PRectangle representing the end of line characters
2324 int xEol
= ll
->positions
[lineEnd
] - subLineStart
;
2326 // Fill the virtual space and show selections within it
2328 rcSegment
.left
= xEol
+ xStart
;
2329 rcSegment
.right
= xEol
+ xStart
+ virtualSpace
;
2330 surface
->FillRectangle(rcSegment
, overrideBackground
? background
: vsDraw
.styles
[ll
->styles
[ll
->numCharsInLine
] & styleMask
].back
.allocated
);
2331 if (!hideSelection
&& ((vsDraw
.selAlpha
== SC_ALPHA_NOALPHA
) || (vsDraw
.selAdditionalAlpha
== SC_ALPHA_NOALPHA
))) {
2332 SelectionSegment
virtualSpaceRange(SelectionPosition(pdoc
->LineEnd(line
)), SelectionPosition(pdoc
->LineEnd(line
), sel
.VirtualSpaceFor(pdoc
->LineEnd(line
))));
2333 for (size_t r
=0; r
<sel
.Count(); r
++) {
2334 int alpha
= (r
== sel
.Main()) ? vsDraw
.selAlpha
: vsDraw
.selAdditionalAlpha
;
2335 if (alpha
== SC_ALPHA_NOALPHA
) {
2336 SelectionSegment portion
= sel
.Range(r
).Intersect(virtualSpaceRange
);
2337 if (!portion
.Empty()) {
2338 const int spaceWidth
= static_cast<int>(vsDraw
.styles
[ll
->EndLineStyle()].spaceWidth
);
2339 rcSegment
.left
= xStart
+ ll
->positions
[portion
.start
.Position() - posLineStart
] - subLineStart
+ portion
.start
.VirtualSpace() * spaceWidth
;
2340 rcSegment
.right
= xStart
+ ll
->positions
[portion
.end
.Position() - posLineStart
] - subLineStart
+ portion
.end
.VirtualSpace() * spaceWidth
;
2341 rcSegment
.left
= Platform::Maximum(rcSegment
.left
, rcLine
.left
);
2342 rcSegment
.right
= Platform::Minimum(rcSegment
.right
, rcLine
.right
);
2343 surface
->FillRectangle(rcSegment
, SelectionBackground(vsDraw
, r
== sel
.Main()));
2350 int posAfterLineEnd
= pdoc
->LineStart(line
+ 1);
2351 int eolInSelection
= (subLine
== (ll
->lines
- 1)) ? sel
.InSelectionForEOL(posAfterLineEnd
) : 0;
2352 int alpha
= (eolInSelection
== 1) ? vsDraw
.selAlpha
: vsDraw
.selAdditionalAlpha
;
2354 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
2357 for (int eolPos
=ll
->numCharsBeforeEOL
; eolPos
<ll
->numCharsInLine
; eolPos
++) {
2358 rcSegment
.left
= xStart
+ ll
->positions
[eolPos
] - subLineStart
+ virtualSpace
;
2359 rcSegment
.right
= xStart
+ ll
->positions
[eolPos
+1] - subLineStart
+ virtualSpace
;
2360 blobsWidth
+= rcSegment
.Width();
2361 const char *ctrlChar
= ControlCharacterString(ll
->chars
[eolPos
]);
2362 int inSelection
= 0;
2363 bool inHotspot
= false;
2364 int styleMain
= ll
->styles
[eolPos
];
2365 ColourAllocated textBack
= TextBackground(vsDraw
, overrideBackground
, background
, inSelection
, inHotspot
, styleMain
, eolPos
, ll
);
2366 ColourAllocated textFore
= vsDraw
.styles
[styleMain
].fore
.allocated
;
2367 if (!hideSelection
&& eolInSelection
&& vsDraw
.selbackset
&& (line
< pdoc
->LinesTotal() - 1)) {
2368 if (alpha
== SC_ALPHA_NOALPHA
) {
2369 surface
->FillRectangle(rcSegment
, SelectionBackground(vsDraw
, eolInSelection
== 1));
2371 surface
->FillRectangle(rcSegment
, textBack
);
2372 SimpleAlphaRectangle(surface
, rcSegment
, SelectionBackground(vsDraw
, eolInSelection
== 1), alpha
);
2375 surface
->FillRectangle(rcSegment
, textBack
);
2377 DrawTextBlob(surface
, vsDraw
, rcSegment
, ctrlChar
, textBack
, textFore
, twoPhaseDraw
);
2381 // Draw the eol-is-selected rectangle
2382 rcSegment
.left
= xEol
+ xStart
+ virtualSpace
+ blobsWidth
;
2383 rcSegment
.right
= xEol
+ xStart
+ virtualSpace
+ blobsWidth
+ vsDraw
.aveCharWidth
;
2385 if (!hideSelection
&& eolInSelection
&& vsDraw
.selbackset
&& (line
< pdoc
->LinesTotal() - 1) && (alpha
== SC_ALPHA_NOALPHA
)) {
2386 surface
->FillRectangle(rcSegment
, SelectionBackground(vsDraw
, eolInSelection
== 1));
2388 if (overrideBackground
) {
2389 surface
->FillRectangle(rcSegment
, background
);
2390 } else if (line
< pdoc
->LinesTotal() - 1) {
2391 surface
->FillRectangle(rcSegment
, vsDraw
.styles
[ll
->styles
[ll
->numCharsInLine
] & styleMask
].back
.allocated
);
2392 } else if (vsDraw
.styles
[ll
->styles
[ll
->numCharsInLine
] & styleMask
].eolFilled
) {
2393 surface
->FillRectangle(rcSegment
, vsDraw
.styles
[ll
->styles
[ll
->numCharsInLine
] & styleMask
].back
.allocated
);
2395 surface
->FillRectangle(rcSegment
, vsDraw
.styles
[STYLE_DEFAULT
].back
.allocated
);
2397 if (!hideSelection
&& eolInSelection
&& vsDraw
.selbackset
&& (line
< pdoc
->LinesTotal() - 1) && (alpha
!= SC_ALPHA_NOALPHA
)) {
2398 SimpleAlphaRectangle(surface
, rcSegment
, SelectionBackground(vsDraw
, eolInSelection
== 1), alpha
);
2402 // Fill the remainder of the line
2403 rcSegment
.left
= xEol
+ xStart
+ virtualSpace
+ blobsWidth
+ vsDraw
.aveCharWidth
;
2404 rcSegment
.right
= rcLine
.right
;
2406 if (!hideSelection
&& vsDraw
.selEOLFilled
&& eolInSelection
&& vsDraw
.selbackset
&& (line
< pdoc
->LinesTotal() - 1) && (alpha
== SC_ALPHA_NOALPHA
)) {
2407 surface
->FillRectangle(rcSegment
, SelectionBackground(vsDraw
, eolInSelection
== 1));
2409 if (overrideBackground
) {
2410 surface
->FillRectangle(rcSegment
, background
);
2411 } else if (vsDraw
.styles
[ll
->styles
[ll
->numCharsInLine
] & styleMask
].eolFilled
) {
2412 surface
->FillRectangle(rcSegment
, vsDraw
.styles
[ll
->styles
[ll
->numCharsInLine
] & styleMask
].back
.allocated
);
2414 surface
->FillRectangle(rcSegment
, vsDraw
.styles
[STYLE_DEFAULT
].back
.allocated
);
2416 if (!hideSelection
&& vsDraw
.selEOLFilled
&& eolInSelection
&& vsDraw
.selbackset
&& (line
< pdoc
->LinesTotal() - 1) && (alpha
!= SC_ALPHA_NOALPHA
)) {
2417 SimpleAlphaRectangle(surface
, rcSegment
, SelectionBackground(vsDraw
, eolInSelection
== 1), alpha
);
2421 if (drawWrapMarkEnd
) {
2422 PRectangle rcPlace
= rcSegment
;
2424 if (wrapVisualFlagsLocation
& SC_WRAPVISUALFLAGLOC_END_BY_TEXT
) {
2425 rcPlace
.left
= xEol
+ xStart
+ virtualSpace
;
2426 rcPlace
.right
= rcPlace
.left
+ vsDraw
.aveCharWidth
;
2428 // draw left of the right text margin, to avoid clipping by the current clip rect
2429 rcPlace
.right
= rcLine
.right
- vs
.rightMarginWidth
;
2430 rcPlace
.left
= rcPlace
.right
- vsDraw
.aveCharWidth
;
2432 DrawWrapMarker(surface
, rcPlace
, true, wrapColour
);
2436 void Editor::DrawIndicators(Surface
*surface
, ViewStyle
&vsDraw
, int line
, int xStart
,
2437 PRectangle rcLine
, LineLayout
*ll
, int subLine
, int lineEnd
, bool under
) {
2439 const int posLineStart
= pdoc
->LineStart(line
);
2440 const int lineStart
= ll
->LineStart(subLine
);
2441 const int subLineStart
= ll
->positions
[lineStart
];
2442 const int posLineEnd
= posLineStart
+ lineEnd
;
2446 // foreach indicator...
2447 for (int indicnum
= 0, mask
= 1 << pdoc
->stylingBits
; mask
< 0x100; indicnum
++) {
2448 if (!(mask
& ll
->styleBitsSet
)) {
2453 // foreach style pos in line...
2454 for (int indicPos
= lineStart
; indicPos
<= lineEnd
; indicPos
++) {
2455 // look for starts...
2457 // NOT in indicator run, looking for START
2458 if (indicPos
< lineEnd
&& (ll
->indicators
[indicPos
] & mask
))
2459 startPos
= indicPos
;
2462 if (startPos
>= 0) {
2463 // IN indicator run, looking for END
2464 if (indicPos
>= lineEnd
|| !(ll
->indicators
[indicPos
] & mask
)) {
2465 // AT end of indicator run, DRAW it!
2467 ll
->positions
[startPos
] + xStart
- subLineStart
,
2468 rcLine
.top
+ vsDraw
.maxAscent
,
2469 ll
->positions
[indicPos
] + xStart
- subLineStart
,
2470 rcLine
.top
+ vsDraw
.maxAscent
+ 3);
2471 vsDraw
.indicators
[indicnum
].Draw(surface
, rcIndic
, rcLine
);
2472 // RESET control var
2481 for (Decoration
*deco
= pdoc
->decorations
.root
; deco
; deco
= deco
->next
) {
2482 if (under
== vsDraw
.indicators
[deco
->indicator
].under
) {
2483 int startPos
= posLineStart
+ lineStart
;
2484 if (!deco
->rs
.ValueAt(startPos
)) {
2485 startPos
= deco
->rs
.EndRun(startPos
);
2487 while ((startPos
< posLineEnd
) && (deco
->rs
.ValueAt(startPos
))) {
2488 int endPos
= deco
->rs
.EndRun(startPos
);
2489 if (endPos
> posLineEnd
)
2490 endPos
= posLineEnd
;
2492 ll
->positions
[startPos
- posLineStart
] + xStart
- subLineStart
,
2493 rcLine
.top
+ vsDraw
.maxAscent
,
2494 ll
->positions
[endPos
- posLineStart
] + xStart
- subLineStart
,
2495 rcLine
.top
+ vsDraw
.maxAscent
+ 3);
2496 vsDraw
.indicators
[deco
->indicator
].Draw(surface
, rcIndic
, rcLine
);
2497 startPos
= deco
->rs
.EndRun(endPos
);
2503 void Editor::DrawAnnotation(Surface
*surface
, ViewStyle
&vsDraw
, int line
, int xStart
,
2504 PRectangle rcLine
, LineLayout
*ll
, int subLine
) {
2505 int indent
= pdoc
->GetLineIndentation(line
) * vsDraw
.spaceWidth
;
2506 PRectangle rcSegment
= rcLine
;
2507 int annotationLine
= subLine
- ll
->lines
;
2508 const StyledText stAnnotation
= pdoc
->AnnotationStyledText(line
);
2509 if (stAnnotation
.text
&& ValidStyledText(vsDraw
, vsDraw
.annotationStyleOffset
, stAnnotation
)) {
2510 surface
->FillRectangle(rcSegment
, vsDraw
.styles
[0].back
.allocated
);
2511 if (vs
.annotationVisible
== ANNOTATION_BOXED
) {
2512 // Only care about calculating width if need to draw box
2513 int widthAnnotation
= WidestLineWidth(surface
, vsDraw
, vsDraw
.annotationStyleOffset
, stAnnotation
);
2514 widthAnnotation
+= vsDraw
.spaceWidth
* 2; // Margins
2515 rcSegment
.left
= xStart
+ indent
;
2516 rcSegment
.right
= rcSegment
.left
+ widthAnnotation
;
2517 surface
->PenColour(vsDraw
.styles
[vsDraw
.annotationStyleOffset
].fore
.allocated
);
2519 rcSegment
.left
= xStart
;
2521 const int annotationLines
= pdoc
->AnnotationLines(line
);
2523 size_t lengthAnnotation
= stAnnotation
.LineLength(start
);
2524 int lineInAnnotation
= 0;
2525 while ((lineInAnnotation
< annotationLine
) && (start
< stAnnotation
.length
)) {
2526 start
+= lengthAnnotation
+ 1;
2527 lengthAnnotation
= stAnnotation
.LineLength(start
);
2530 PRectangle rcText
= rcSegment
;
2531 if (vs
.annotationVisible
== ANNOTATION_BOXED
) {
2532 surface
->FillRectangle(rcText
,
2533 vsDraw
.styles
[stAnnotation
.StyleAt(start
) + vsDraw
.annotationStyleOffset
].back
.allocated
);
2534 rcText
.left
+= vsDraw
.spaceWidth
;
2536 DrawStyledText(surface
, vsDraw
, vsDraw
.annotationStyleOffset
, rcText
, rcText
.top
+ vsDraw
.maxAscent
,
2537 stAnnotation
, start
, lengthAnnotation
);
2538 if (vs
.annotationVisible
== ANNOTATION_BOXED
) {
2539 surface
->MoveTo(rcSegment
.left
, rcSegment
.top
);
2540 surface
->LineTo(rcSegment
.left
, rcSegment
.bottom
);
2541 surface
->MoveTo(rcSegment
.right
, rcSegment
.top
);
2542 surface
->LineTo(rcSegment
.right
, rcSegment
.bottom
);
2543 if (subLine
== ll
->lines
) {
2544 surface
->MoveTo(rcSegment
.left
, rcSegment
.top
);
2545 surface
->LineTo(rcSegment
.right
, rcSegment
.top
);
2547 if (subLine
== ll
->lines
+annotationLines
-1) {
2548 surface
->MoveTo(rcSegment
.left
, rcSegment
.bottom
- 1);
2549 surface
->LineTo(rcSegment
.right
, rcSegment
.bottom
- 1);
2555 void Editor::DrawLine(Surface
*surface
, ViewStyle
&vsDraw
, int line
, int lineVisible
, int xStart
,
2556 PRectangle rcLine
, LineLayout
*ll
, int subLine
) {
2558 PRectangle rcSegment
= rcLine
;
2560 // Using one font for all control characters so it can be controlled independently to ensure
2561 // the box goes around the characters tightly. Seems to be no way to work out what height
2562 // is taken by an individual character - internal leading gives varying results.
2563 Font
&ctrlCharsFont
= vsDraw
.styles
[STYLE_CONTROLCHAR
].font
;
2565 // See if something overrides the line background color: Either if caret is on the line
2566 // and background color is set for that, or if a marker is defined that forces its background
2567 // color onto the line, or if a marker is defined but has no selection margin in which to
2568 // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
2569 // with the earlier taking precedence. When multiple markers cause background override,
2570 // the color for the highest numbered one is used.
2571 bool overrideBackground
= false;
2572 ColourAllocated background
;
2573 if (caret
.active
&& vsDraw
.showCaretLineBackground
&& (vsDraw
.caretLineAlpha
== SC_ALPHA_NOALPHA
) && ll
->containsCaret
) {
2574 overrideBackground
= true;
2575 background
= vsDraw
.caretLineBackground
.allocated
;
2577 if (!overrideBackground
) {
2578 int marks
= pdoc
->GetMark(line
);
2579 for (int markBit
= 0; (markBit
< 32) && marks
; markBit
++) {
2580 if ((marks
& 1) && (vsDraw
.markers
[markBit
].markType
== SC_MARK_BACKGROUND
) &&
2581 (vsDraw
.markers
[markBit
].alpha
== SC_ALPHA_NOALPHA
)) {
2582 background
= vsDraw
.markers
[markBit
].back
.allocated
;
2583 overrideBackground
= true;
2588 if (!overrideBackground
) {
2589 if (vsDraw
.maskInLine
) {
2590 int marksMasked
= pdoc
->GetMark(line
) & vsDraw
.maskInLine
;
2592 for (int markBit
= 0; (markBit
< 32) && marksMasked
; markBit
++) {
2593 if ((marksMasked
& 1) && (vsDraw
.markers
[markBit
].markType
!= SC_MARK_EMPTY
) &&
2594 (vsDraw
.markers
[markBit
].alpha
== SC_ALPHA_NOALPHA
)) {
2595 overrideBackground
= true;
2596 background
= vsDraw
.markers
[markBit
].back
.allocated
;
2604 bool drawWhitespaceBackground
= (vsDraw
.viewWhitespace
!= wsInvisible
) &&
2605 (!overrideBackground
) && (vsDraw
.whitespaceBackgroundSet
);
2607 bool inIndentation
= subLine
== 0; // Do not handle indentation except on first subline.
2608 int indentWidth
= pdoc
->IndentSize() * vsDraw
.spaceWidth
;
2610 int posLineStart
= pdoc
->LineStart(line
);
2612 int startseg
= ll
->LineStart(subLine
);
2613 int subLineStart
= ll
->positions
[startseg
];
2614 if (subLine
>= ll
->lines
) {
2615 DrawAnnotation(surface
, vsDraw
, line
, xStart
, rcLine
, ll
, subLine
);
2616 return; // No further drawing
2620 if (subLine
< ll
->lines
) {
2621 lineStart
= ll
->LineStart(subLine
);
2622 lineEnd
= ll
->LineStart(subLine
+ 1);
2623 if (subLine
== ll
->lines
- 1) {
2624 lineEnd
= ll
->numCharsBeforeEOL
;
2628 ColourAllocated wrapColour
= vsDraw
.styles
[STYLE_DEFAULT
].fore
.allocated
;
2629 if (vsDraw
.whitespaceForegroundSet
)
2630 wrapColour
= vsDraw
.whitespaceForeground
.allocated
;
2632 bool drawWrapMarkEnd
= false;
2634 if (wrapVisualFlags
& SC_WRAPVISUALFLAG_END
) {
2635 if (subLine
+ 1 < ll
->lines
) {
2636 drawWrapMarkEnd
= ll
->LineStart(subLine
+ 1) != 0;
2640 if (ll
->wrapIndent
!= 0) {
2642 bool continuedWrapLine
= false;
2643 if (subLine
< ll
->lines
) {
2644 continuedWrapLine
= ll
->LineStart(subLine
) != 0;
2647 if (continuedWrapLine
) {
2648 // draw continuation rect
2649 PRectangle rcPlace
= rcSegment
;
2651 rcPlace
.left
= ll
->positions
[startseg
] + xStart
- subLineStart
;
2652 rcPlace
.right
= rcPlace
.left
+ ll
->wrapIndent
;
2654 // default bgnd here..
2655 surface
->FillRectangle(rcSegment
, overrideBackground
? background
:
2656 vsDraw
.styles
[STYLE_DEFAULT
].back
.allocated
);
2658 // main line style would be below but this would be inconsistent with end markers
2659 // also would possibly not be the style at wrap point
2660 //int styleMain = ll->styles[lineStart];
2661 //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back.allocated);
2663 if (wrapVisualFlags
& SC_WRAPVISUALFLAG_START
) {
2665 if (wrapVisualFlagsLocation
& SC_WRAPVISUALFLAGLOC_START_BY_TEXT
)
2666 rcPlace
.left
= rcPlace
.right
- vsDraw
.aveCharWidth
;
2668 rcPlace
.right
= rcPlace
.left
+ vsDraw
.aveCharWidth
;
2670 DrawWrapMarker(surface
, rcPlace
, false, wrapColour
);
2673 xStart
+= ll
->wrapIndent
;
2677 bool selBackDrawn
= vsDraw
.selbackset
&&
2678 ((vsDraw
.selAlpha
== SC_ALPHA_NOALPHA
) || (vsDraw
.selAdditionalAlpha
== SC_ALPHA_NOALPHA
));
2680 // Does not take margin into account but not significant
2681 int xStartVisible
= subLineStart
- xStart
;
2685 BreakFinder
bfBack(ll
, lineStart
, lineEnd
, posLineStart
, IsUnicodeMode(), xStartVisible
, selBackDrawn
);
2686 int next
= bfBack
.First();
2688 // Background drawing loop
2689 while (twoPhaseDraw
&& (next
< lineEnd
)) {
2692 next
= bfBack
.Next();
2694 int iDoc
= i
+ posLineStart
;
2696 rcSegment
.left
= ll
->positions
[startseg
] + xStart
- subLineStart
;
2697 rcSegment
.right
= ll
->positions
[i
+ 1] + xStart
- subLineStart
;
2698 // Only try to draw if really visible - enhances performance by not calling environment to
2699 // draw strings that are completely past the right side of the window.
2700 if ((rcSegment
.left
<= rcLine
.right
) && (rcSegment
.right
>= rcLine
.left
)) {
2701 // Clip to line rectangle, since may have a huge position which will not work with some platforms
2702 rcSegment
.left
= Platform::Maximum(rcSegment
.left
, rcLine
.left
);
2703 rcSegment
.right
= Platform::Minimum(rcSegment
.right
, rcLine
.right
);
2705 int styleMain
= ll
->styles
[i
];
2706 const int inSelection
= hideSelection
? 0 : sel
.CharacterInSelection(iDoc
);
2707 bool inHotspot
= (ll
->hsStart
!= -1) && (iDoc
>= ll
->hsStart
) && (iDoc
< ll
->hsEnd
);
2708 ColourAllocated textBack
= TextBackground(vsDraw
, overrideBackground
, background
, inSelection
, inHotspot
, styleMain
, i
, ll
);
2709 if (ll
->chars
[i
] == '\t') {
2711 if (drawWhitespaceBackground
&&
2712 (!inIndentation
|| vsDraw
.viewWhitespace
== wsVisibleAlways
))
2713 textBack
= vsDraw
.whitespaceBackground
.allocated
;
2714 surface
->FillRectangle(rcSegment
, textBack
);
2715 } else if (IsControlCharacter(ll
->chars
[i
])) {
2716 // Control character display
2717 inIndentation
= false;
2718 surface
->FillRectangle(rcSegment
, textBack
);
2720 // Normal text display
2721 surface
->FillRectangle(rcSegment
, textBack
);
2722 if (vsDraw
.viewWhitespace
!= wsInvisible
||
2723 (inIndentation
&& vsDraw
.viewIndentationGuides
== ivReal
)) {
2724 for (int cpos
= 0; cpos
<= i
- startseg
; cpos
++) {
2725 if (ll
->chars
[cpos
+ startseg
] == ' ') {
2726 if (drawWhitespaceBackground
&&
2727 (!inIndentation
|| vsDraw
.viewWhitespace
== wsVisibleAlways
)) {
2728 PRectangle
rcSpace(ll
->positions
[cpos
+ startseg
] + xStart
- subLineStart
,
2730 ll
->positions
[cpos
+ startseg
+ 1] + xStart
- subLineStart
,
2732 surface
->FillRectangle(rcSpace
, vsDraw
.whitespaceBackground
.allocated
);
2735 inIndentation
= false;
2740 } else if (rcSegment
.left
> rcLine
.right
) {
2746 DrawEOL(surface
, vsDraw
, rcLine
, ll
, line
, lineEnd
,
2747 xStart
, subLine
, subLineStart
, overrideBackground
, background
,
2748 drawWrapMarkEnd
, wrapColour
);
2751 DrawIndicators(surface
, vsDraw
, line
, xStart
, rcLine
, ll
, subLine
, lineEnd
, true);
2753 if (vsDraw
.edgeState
== EDGE_LINE
) {
2754 int edgeX
= theEdge
* vsDraw
.spaceWidth
;
2755 rcSegment
.left
= edgeX
+ xStart
;
2756 rcSegment
.right
= rcSegment
.left
+ 1;
2757 surface
->FillRectangle(rcSegment
, vsDraw
.edgecolour
.allocated
);
2760 // Draw underline mark as part of background if not transparent
2761 int marks
= pdoc
->GetMark(line
);
2763 for (markBit
= 0; (markBit
< 32) && marks
; markBit
++) {
2764 if ((marks
& 1) && (vsDraw
.markers
[markBit
].markType
== SC_MARK_UNDERLINE
) &&
2765 (vsDraw
.markers
[markBit
].alpha
== SC_ALPHA_NOALPHA
)) {
2766 PRectangle rcUnderline
= rcLine
;
2767 rcUnderline
.top
= rcUnderline
.bottom
- 2;
2768 surface
->FillRectangle(rcUnderline
, vsDraw
.markers
[markBit
].back
.allocated
);
2773 inIndentation
= subLine
== 0; // Do not handle indentation except on first subline.
2774 // Foreground drawing loop
2775 BreakFinder
bfFore(ll
, lineStart
, lineEnd
, posLineStart
, IsUnicodeMode(), xStartVisible
,
2776 ((!twoPhaseDraw
&& selBackDrawn
) || vsDraw
.selforeset
));
2777 next
= bfFore
.First();
2779 while (next
< lineEnd
) {
2782 next
= bfFore
.Next();
2785 int iDoc
= i
+ posLineStart
;
2787 rcSegment
.left
= ll
->positions
[startseg
] + xStart
- subLineStart
;
2788 rcSegment
.right
= ll
->positions
[i
+ 1] + xStart
- subLineStart
;
2789 // Only try to draw if really visible - enhances performance by not calling environment to
2790 // draw strings that are completely past the right side of the window.
2791 if ((rcSegment
.left
<= rcLine
.right
) && (rcSegment
.right
>= rcLine
.left
)) {
2792 int styleMain
= ll
->styles
[i
];
2793 ColourAllocated textFore
= vsDraw
.styles
[styleMain
].fore
.allocated
;
2794 Font
&textFont
= vsDraw
.styles
[styleMain
].font
;
2795 //hotspot foreground
2796 if (ll
->hsStart
!= -1 && iDoc
>= ll
->hsStart
&& iDoc
< hsEnd
) {
2797 if (vsDraw
.hotspotForegroundSet
)
2798 textFore
= vsDraw
.hotspotForeground
.allocated
;
2800 const int inSelection
= hideSelection
? 0 : sel
.CharacterInSelection(iDoc
);
2801 if (inSelection
&& (vsDraw
.selforeset
)) {
2802 textFore
= (inSelection
== 1) ? vsDraw
.selforeground
.allocated
: vsDraw
.selAdditionalForeground
.allocated
;
2804 bool inHotspot
= (ll
->hsStart
!= -1) && (iDoc
>= ll
->hsStart
) && (iDoc
< ll
->hsEnd
);
2805 ColourAllocated textBack
= TextBackground(vsDraw
, overrideBackground
, background
, inSelection
, inHotspot
, styleMain
, i
, ll
);
2806 if (ll
->chars
[i
] == '\t') {
2808 if (!twoPhaseDraw
) {
2809 if (drawWhitespaceBackground
&&
2810 (!inIndentation
|| vsDraw
.viewWhitespace
== wsVisibleAlways
))
2811 textBack
= vsDraw
.whitespaceBackground
.allocated
;
2812 surface
->FillRectangle(rcSegment
, textBack
);
2814 if ((vsDraw
.viewWhitespace
!= wsInvisible
) ||
2815 (inIndentation
&& vsDraw
.viewIndentationGuides
!= ivNone
)) {
2816 if (vsDraw
.whitespaceForegroundSet
)
2817 textFore
= vsDraw
.whitespaceForeground
.allocated
;
2818 surface
->PenColour(textFore
);
2820 if (inIndentation
&& vsDraw
.viewIndentationGuides
== ivReal
) {
2821 for (int xIG
= ll
->positions
[i
] / indentWidth
* indentWidth
; xIG
< ll
->positions
[i
+ 1]; xIG
+= indentWidth
) {
2822 if (xIG
>= ll
->positions
[i
] && xIG
> 0) {
2823 DrawIndentGuide(surface
, lineVisible
, vsDraw
.lineHeight
, xIG
+ xStart
, rcSegment
,
2824 (ll
->xHighlightGuide
== xIG
));
2828 if (vsDraw
.viewWhitespace
!= wsInvisible
) {
2829 if (!inIndentation
|| vsDraw
.viewWhitespace
== wsVisibleAlways
) {
2830 PRectangle
rcTab(rcSegment
.left
+ 1, rcSegment
.top
+ 4,
2831 rcSegment
.right
- 1, rcSegment
.bottom
- vsDraw
.maxDescent
);
2832 DrawTabArrow(surface
, rcTab
, rcSegment
.top
+ vsDraw
.lineHeight
/ 2);
2835 } else if (IsControlCharacter(ll
->chars
[i
])) {
2836 // Control character display
2837 inIndentation
= false;
2838 if (controlCharSymbol
< 32) {
2839 // Draw the character
2840 const char *ctrlChar
= ControlCharacterString(ll
->chars
[i
]);
2841 DrawTextBlob(surface
, vsDraw
, rcSegment
, ctrlChar
, textBack
, textFore
, twoPhaseDraw
);
2843 char cc
[2] = { static_cast<char>(controlCharSymbol
), '\0' };
2844 surface
->DrawTextNoClip(rcSegment
, ctrlCharsFont
,
2845 rcSegment
.top
+ vsDraw
.maxAscent
,
2846 cc
, 1, textBack
, textFore
);
2848 } else if ((i
== startseg
) && (static_cast<unsigned char>(ll
->chars
[i
]) >= 0x80) && IsUnicodeMode()) {
2849 // A single byte >= 0x80 in UTF-8 is a bad byte and is displayed as its hex value
2851 sprintf(hexits
, "x%2X", ll
->chars
[i
] & 0xff);
2852 DrawTextBlob(surface
, vsDraw
, rcSegment
, hexits
, textBack
, textFore
, twoPhaseDraw
);
2854 // Normal text display
2855 if (vsDraw
.styles
[styleMain
].visible
) {
2857 surface
->DrawTextTransparent(rcSegment
, textFont
,
2858 rcSegment
.top
+ vsDraw
.maxAscent
, ll
->chars
+ startseg
,
2859 i
- startseg
+ 1, textFore
);
2861 surface
->DrawTextNoClip(rcSegment
, textFont
,
2862 rcSegment
.top
+ vsDraw
.maxAscent
, ll
->chars
+ startseg
,
2863 i
- startseg
+ 1, textFore
, textBack
);
2866 if (vsDraw
.viewWhitespace
!= wsInvisible
||
2867 (inIndentation
&& vsDraw
.viewIndentationGuides
!= ivNone
)) {
2868 for (int cpos
= 0; cpos
<= i
- startseg
; cpos
++) {
2869 if (ll
->chars
[cpos
+ startseg
] == ' ') {
2870 if (vsDraw
.viewWhitespace
!= wsInvisible
) {
2871 if (vsDraw
.whitespaceForegroundSet
)
2872 textFore
= vsDraw
.whitespaceForeground
.allocated
;
2873 if (!inIndentation
|| vsDraw
.viewWhitespace
== wsVisibleAlways
) {
2874 int xmid
= (ll
->positions
[cpos
+ startseg
] + ll
->positions
[cpos
+ startseg
+ 1]) / 2;
2875 if (!twoPhaseDraw
&& drawWhitespaceBackground
&&
2876 (!inIndentation
|| vsDraw
.viewWhitespace
== wsVisibleAlways
)) {
2877 textBack
= vsDraw
.whitespaceBackground
.allocated
;
2878 PRectangle
rcSpace(ll
->positions
[cpos
+ startseg
] + xStart
- subLineStart
,
2880 ll
->positions
[cpos
+ startseg
+ 1] + xStart
- subLineStart
,
2882 surface
->FillRectangle(rcSpace
, textBack
);
2884 PRectangle
rcDot(xmid
+ xStart
- subLineStart
, rcSegment
.top
+ vsDraw
.lineHeight
/ 2, 0, 0);
2885 rcDot
.right
= rcDot
.left
+ vs
.whitespaceSize
;
2886 rcDot
.bottom
= rcDot
.top
+ vs
.whitespaceSize
;
2887 surface
->FillRectangle(rcDot
, textFore
);
2890 if (inIndentation
&& vsDraw
.viewIndentationGuides
== ivReal
) {
2891 int startSpace
= ll
->positions
[cpos
+ startseg
];
2892 if (startSpace
> 0 && (startSpace
% indentWidth
== 0)) {
2893 DrawIndentGuide(surface
, lineVisible
, vsDraw
.lineHeight
, startSpace
+ xStart
, rcSegment
,
2894 (ll
->xHighlightGuide
== ll
->positions
[cpos
+ startseg
]));
2898 inIndentation
= false;
2903 if (ll
->hsStart
!= -1 && vsDraw
.hotspotUnderline
&& iDoc
>= ll
->hsStart
&& iDoc
< ll
->hsEnd
) {
2904 PRectangle rcUL
= rcSegment
;
2905 rcUL
.top
= rcUL
.top
+ vsDraw
.maxAscent
+ 1;
2906 rcUL
.bottom
= rcUL
.top
+ 1;
2907 if (vsDraw
.hotspotForegroundSet
)
2908 surface
->FillRectangle(rcUL
, vsDraw
.hotspotForeground
.allocated
);
2910 surface
->FillRectangle(rcUL
, textFore
);
2911 } else if (vsDraw
.styles
[styleMain
].underline
) {
2912 PRectangle rcUL
= rcSegment
;
2913 rcUL
.top
= rcUL
.top
+ vsDraw
.maxAscent
+ 1;
2914 rcUL
.bottom
= rcUL
.top
+ 1;
2915 surface
->FillRectangle(rcUL
, textFore
);
2917 } else if (rcSegment
.left
> rcLine
.right
) {
2921 if ((vsDraw
.viewIndentationGuides
== ivLookForward
|| vsDraw
.viewIndentationGuides
== ivLookBoth
)
2922 && (subLine
== 0)) {
2923 int indentSpace
= pdoc
->GetLineIndentation(line
);
2924 int xStartText
= ll
->positions
[pdoc
->GetLineIndentPosition(line
) - posLineStart
];
2926 // Find the most recent line with some text
2928 int lineLastWithText
= line
;
2929 while (lineLastWithText
> Platform::Maximum(line
-20, 0) && pdoc
->IsWhiteLine(lineLastWithText
)) {
2932 if (lineLastWithText
< line
) {
2933 xStartText
= 100000; // Don't limit to visible indentation on empty line
2934 // This line is empty, so use indentation of last line with text
2935 int indentLastWithText
= pdoc
->GetLineIndentation(lineLastWithText
);
2936 int isFoldHeader
= pdoc
->GetLevel(lineLastWithText
) & SC_FOLDLEVELHEADERFLAG
;
2938 // Level is one more level than parent
2939 indentLastWithText
+= pdoc
->IndentSize();
2941 if (vsDraw
.viewIndentationGuides
== ivLookForward
) {
2942 // In viLookForward mode, previous line only used if it is a fold header
2944 indentSpace
= Platform::Maximum(indentSpace
, indentLastWithText
);
2946 } else { // viLookBoth
2947 indentSpace
= Platform::Maximum(indentSpace
, indentLastWithText
);
2951 int lineNextWithText
= line
;
2952 while (lineNextWithText
< Platform::Minimum(line
+20, pdoc
->LinesTotal()) && pdoc
->IsWhiteLine(lineNextWithText
)) {
2955 if (lineNextWithText
> line
) {
2956 // This line is empty, so use indentation of last line with text
2957 indentSpace
= Platform::Maximum(indentSpace
,
2958 pdoc
->GetLineIndentation(lineNextWithText
));
2961 for (int indentPos
= pdoc
->IndentSize(); indentPos
< indentSpace
; indentPos
+= pdoc
->IndentSize()) {
2962 int xIndent
= indentPos
* vsDraw
.spaceWidth
;
2963 if (xIndent
< xStartText
) {
2964 DrawIndentGuide(surface
, lineVisible
, vsDraw
.lineHeight
, xIndent
+ xStart
, rcSegment
,
2965 (ll
->xHighlightGuide
== xIndent
));
2970 DrawIndicators(surface
, vsDraw
, line
, xStart
, rcLine
, ll
, subLine
, lineEnd
, false);
2972 // End of the drawing of the current line
2973 if (!twoPhaseDraw
) {
2974 DrawEOL(surface
, vsDraw
, rcLine
, ll
, line
, lineEnd
,
2975 xStart
, subLine
, subLineStart
, overrideBackground
, background
,
2976 drawWrapMarkEnd
, wrapColour
);
2978 if (!hideSelection
&& ((vsDraw
.selAlpha
!= SC_ALPHA_NOALPHA
) || (vsDraw
.selAdditionalAlpha
!= SC_ALPHA_NOALPHA
))) {
2979 // For each selection draw
2980 int virtualSpaces
= 0;
2981 if (subLine
== (ll
->lines
- 1)) {
2982 virtualSpaces
= sel
.VirtualSpaceFor(pdoc
->LineEnd(line
));
2984 SelectionPosition
posStart(posLineStart
);
2985 SelectionPosition
posEnd(posLineStart
+ lineEnd
, virtualSpaces
);
2986 SelectionSegment
virtualSpaceRange(posStart
, posEnd
);
2987 for (size_t r
=0; r
<sel
.Count(); r
++) {
2988 int alpha
= (r
== sel
.Main()) ? vsDraw
.selAlpha
: vsDraw
.selAdditionalAlpha
;
2989 if (alpha
!= SC_ALPHA_NOALPHA
) {
2990 SelectionSegment portion
= sel
.Range(r
).Intersect(virtualSpaceRange
);
2991 if (!portion
.Empty()) {
2992 const int spaceWidth
= static_cast<int>(vsDraw
.styles
[ll
->EndLineStyle()].spaceWidth
);
2993 rcSegment
.left
= xStart
+ ll
->positions
[portion
.start
.Position() - posLineStart
] - subLineStart
+ portion
.start
.VirtualSpace() * spaceWidth
;
2994 rcSegment
.right
= xStart
+ ll
->positions
[portion
.end
.Position() - posLineStart
] - subLineStart
+ portion
.end
.VirtualSpace() * spaceWidth
;
2995 rcSegment
.left
= Platform::Maximum(rcSegment
.left
, rcLine
.left
);
2996 rcSegment
.right
= Platform::Minimum(rcSegment
.right
, rcLine
.right
);
2997 SimpleAlphaRectangle(surface
, rcSegment
, SelectionBackground(vsDraw
, r
== sel
.Main()), alpha
);
3003 // Draw any translucent whole line states
3004 rcSegment
.left
= xStart
;
3005 rcSegment
.right
= rcLine
.right
- 1;
3006 if (caret
.active
&& vsDraw
.showCaretLineBackground
&& ll
->containsCaret
) {
3007 SimpleAlphaRectangle(surface
, rcSegment
, vsDraw
.caretLineBackground
.allocated
, vsDraw
.caretLineAlpha
);
3009 marks
= pdoc
->GetMark(line
);
3010 for (markBit
= 0; (markBit
< 32) && marks
; markBit
++) {
3011 if ((marks
& 1) && (vsDraw
.markers
[markBit
].markType
== SC_MARK_BACKGROUND
)) {
3012 SimpleAlphaRectangle(surface
, rcSegment
, vsDraw
.markers
[markBit
].back
.allocated
, vsDraw
.markers
[markBit
].alpha
);
3013 } else if ((marks
& 1) && (vsDraw
.markers
[markBit
].markType
== SC_MARK_UNDERLINE
)) {
3014 PRectangle rcUnderline
= rcSegment
;
3015 rcUnderline
.top
= rcUnderline
.bottom
- 2;
3016 SimpleAlphaRectangle(surface
, rcUnderline
, vsDraw
.markers
[markBit
].back
.allocated
, vsDraw
.markers
[markBit
].alpha
);
3020 if (vsDraw
.maskInLine
) {
3021 int marksMasked
= pdoc
->GetMark(line
) & vsDraw
.maskInLine
;
3023 for (markBit
= 0; (markBit
< 32) && marksMasked
; markBit
++) {
3024 if ((marksMasked
& 1) && (vsDraw
.markers
[markBit
].markType
!= SC_MARK_EMPTY
)) {
3025 SimpleAlphaRectangle(surface
, rcSegment
, vsDraw
.markers
[markBit
].back
.allocated
, vsDraw
.markers
[markBit
].alpha
);
3033 void Editor::DrawBlockCaret(Surface
*surface
, ViewStyle
&vsDraw
, LineLayout
*ll
, int subLine
,
3034 int xStart
, int offset
, int posCaret
, PRectangle rcCaret
, ColourAllocated caretColour
) {
3036 int lineStart
= ll
->LineStart(subLine
);
3037 int posBefore
= posCaret
;
3038 int posAfter
= MovePositionOutsideChar(posCaret
+ 1, 1);
3039 int numCharsToDraw
= posAfter
- posCaret
;
3041 // Work out where the starting and ending offsets are. We need to
3042 // see if the previous character shares horizontal space, such as a
3043 // glyph / combining character. If so we'll need to draw that too.
3044 int offsetFirstChar
= offset
;
3045 int offsetLastChar
= offset
+ (posAfter
- posCaret
);
3046 while ((offsetLastChar
- numCharsToDraw
) >= lineStart
) {
3047 if ((ll
->positions
[offsetLastChar
] - ll
->positions
[offsetLastChar
- numCharsToDraw
]) > 0) {
3048 // The char does not share horizontal space
3051 // Char shares horizontal space, update the numChars to draw
3052 // Update posBefore to point to the prev char
3053 posBefore
= MovePositionOutsideChar(posBefore
- 1, -1);
3054 numCharsToDraw
= posAfter
- posBefore
;
3055 offsetFirstChar
= offset
- (posCaret
- posBefore
);
3058 // See if the next character shares horizontal space, if so we'll
3059 // need to draw that too.
3060 numCharsToDraw
= offsetLastChar
- offsetFirstChar
;
3061 while ((offsetLastChar
< ll
->LineStart(subLine
+ 1)) && (offsetLastChar
<= ll
->numCharsInLine
)) {
3062 // Update posAfter to point to the 2nd next char, this is where
3063 // the next character ends, and 2nd next begins. We'll need
3064 // to compare these two
3065 posBefore
= posAfter
;
3066 posAfter
= MovePositionOutsideChar(posAfter
+ 1, 1);
3067 offsetLastChar
= offset
+ (posAfter
- posCaret
);
3068 if ((ll
->positions
[offsetLastChar
] - ll
->positions
[offsetLastChar
- (posAfter
- posBefore
)]) > 0) {
3069 // The char does not share horizontal space
3072 // Char shares horizontal space, update the numChars to draw
3073 numCharsToDraw
= offsetLastChar
- offsetFirstChar
;
3076 // We now know what to draw, update the caret drawing rectangle
3077 rcCaret
.left
= ll
->positions
[offsetFirstChar
] - ll
->positions
[lineStart
] + xStart
;
3078 rcCaret
.right
= ll
->positions
[offsetFirstChar
+numCharsToDraw
] - ll
->positions
[lineStart
] + xStart
;
3080 // Adjust caret position to take into account any word wrapping symbols.
3081 if ((ll
->wrapIndent
!= 0) && (lineStart
!= 0)) {
3082 int wordWrapCharWidth
= ll
->wrapIndent
;
3083 rcCaret
.left
+= wordWrapCharWidth
;
3084 rcCaret
.right
+= wordWrapCharWidth
;
3087 // This character is where the caret block is, we override the colours
3088 // (inversed) for drawing the caret here.
3089 int styleMain
= ll
->styles
[offsetFirstChar
];
3090 surface
->DrawTextClipped(rcCaret
, vsDraw
.styles
[styleMain
].font
,
3091 rcCaret
.top
+ vsDraw
.maxAscent
, ll
->chars
+ offsetFirstChar
,
3092 numCharsToDraw
, vsDraw
.styles
[styleMain
].back
.allocated
,
3096 void Editor::RefreshPixMaps(Surface
*surfaceWindow
) {
3097 if (!pixmapSelPattern
->Initialised()) {
3098 const int patternSize
= 8;
3099 pixmapSelPattern
->InitPixMap(patternSize
, patternSize
, surfaceWindow
, wMain
.GetID());
3100 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
3101 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
3102 // way between the chrome colour and the chrome highlight colour making a nice transition
3103 // between the window chrome and the content area. And it works in low colour depths.
3104 PRectangle
rcPattern(0, 0, patternSize
, patternSize
);
3106 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
3107 ColourAllocated colourFMFill
= vs
.selbar
.allocated
;
3108 ColourAllocated colourFMStripes
= vs
.selbarlight
.allocated
;
3110 if (!(vs
.selbarlight
.desired
== ColourDesired(0xff, 0xff, 0xff))) {
3111 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
3112 // (Typically, the highlight colour is white.)
3113 colourFMFill
= vs
.selbarlight
.allocated
;
3116 if (vs
.foldmarginColourSet
) {
3117 // override default fold margin colour
3118 colourFMFill
= vs
.foldmarginColour
.allocated
;
3120 if (vs
.foldmarginHighlightColourSet
) {
3121 // override default fold margin highlight colour
3122 colourFMStripes
= vs
.foldmarginHighlightColour
.allocated
;
3125 pixmapSelPattern
->FillRectangle(rcPattern
, colourFMFill
);
3126 pixmapSelPattern
->PenColour(colourFMStripes
);
3127 for (int stripe
= 0; stripe
< patternSize
; stripe
++) {
3128 // Alternating 1 pixel stripes is same as checkerboard.
3129 pixmapSelPattern
->MoveTo(0, stripe
* 2);
3130 pixmapSelPattern
->LineTo(patternSize
, stripe
* 2 - patternSize
);
3134 if (!pixmapIndentGuide
->Initialised()) {
3135 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
3136 pixmapIndentGuide
->InitPixMap(1, vs
.lineHeight
+ 1, surfaceWindow
, wMain
.GetID());
3137 pixmapIndentGuideHighlight
->InitPixMap(1, vs
.lineHeight
+ 1, surfaceWindow
, wMain
.GetID());
3138 PRectangle
rcIG(0, 0, 1, vs
.lineHeight
);
3139 pixmapIndentGuide
->FillRectangle(rcIG
, vs
.styles
[STYLE_INDENTGUIDE
].back
.allocated
);
3140 pixmapIndentGuide
->PenColour(vs
.styles
[STYLE_INDENTGUIDE
].fore
.allocated
);
3141 pixmapIndentGuideHighlight
->FillRectangle(rcIG
, vs
.styles
[STYLE_BRACELIGHT
].back
.allocated
);
3142 pixmapIndentGuideHighlight
->PenColour(vs
.styles
[STYLE_BRACELIGHT
].fore
.allocated
);
3143 for (int stripe
= 1; stripe
< vs
.lineHeight
+ 1; stripe
+= 2) {
3144 pixmapIndentGuide
->MoveTo(0, stripe
);
3145 pixmapIndentGuide
->LineTo(2, stripe
);
3146 pixmapIndentGuideHighlight
->MoveTo(0, stripe
);
3147 pixmapIndentGuideHighlight
->LineTo(2, stripe
);
3152 if (!pixmapLine
->Initialised()) {
3153 PRectangle rcClient
= GetClientRectangle();
3154 pixmapLine
->InitPixMap(rcClient
.Width(), vs
.lineHeight
,
3155 surfaceWindow
, wMain
.GetID());
3156 pixmapSelMargin
->InitPixMap(vs
.fixedColumnWidth
,
3157 rcClient
.Height(), surfaceWindow
, wMain
.GetID());
3162 void Editor::DrawCarets(Surface
*surface
, ViewStyle
&vsDraw
, int lineDoc
, int xStart
,
3163 PRectangle rcLine
, LineLayout
*ll
, int subLine
) {
3164 // When drag is active it is the only caret drawn
3165 bool drawDrag
= posDrag
.IsValid();
3166 if (hideSelection
&& !drawDrag
)
3168 const int posLineStart
= pdoc
->LineStart(lineDoc
);
3169 // For each selection draw
3170 for (size_t r
=0; (r
<sel
.Count()) || drawDrag
; r
++) {
3171 const bool mainCaret
= r
== sel
.Main();
3172 const SelectionPosition posCaret
= (drawDrag
? posDrag
: sel
.Range(r
).caret
);
3173 const int offset
= posCaret
.Position() - posLineStart
;
3174 const int spaceWidth
= static_cast<int>(vsDraw
.styles
[ll
->EndLineStyle()].spaceWidth
);
3175 const int virtualOffset
= posCaret
.VirtualSpace() * spaceWidth
;
3176 if (ll
->InLine(offset
, subLine
) && offset
<= ll
->numCharsBeforeEOL
) {
3177 int xposCaret
= ll
->positions
[offset
] + virtualOffset
- ll
->positions
[ll
->LineStart(subLine
)];
3178 if (ll
->wrapIndent
!= 0) {
3179 int lineStart
= ll
->LineStart(subLine
);
3180 if (lineStart
!= 0) // Wrapped
3181 xposCaret
+= ll
->wrapIndent
;
3183 bool caretBlinkState
= (caret
.active
&& caret
.on
) || (!additionalCaretsBlink
&& !mainCaret
);
3184 bool caretVisibleState
= additionalCaretsVisible
|| mainCaret
;
3185 if ((xposCaret
>= 0) && (vsDraw
.caretWidth
> 0) && (vsDraw
.caretStyle
!= CARETSTYLE_INVISIBLE
) &&
3186 ((posDrag
.IsValid()) || (caretBlinkState
&& caretVisibleState
))) {
3187 bool caretAtEOF
= false;
3188 bool caretAtEOL
= false;
3189 bool drawBlockCaret
= false;
3190 int widthOverstrikeCaret
;
3191 int caretWidthOffset
= 0;
3192 PRectangle rcCaret
= rcLine
;
3194 if (posCaret
.Position() == pdoc
->Length()) { // At end of document
3196 widthOverstrikeCaret
= vsDraw
.aveCharWidth
;
3197 } else if ((posCaret
.Position() - posLineStart
) >= ll
->numCharsInLine
) { // At end of line
3199 widthOverstrikeCaret
= vsDraw
.aveCharWidth
;
3201 widthOverstrikeCaret
= ll
->positions
[offset
+ 1] - ll
->positions
[offset
];
3203 if (widthOverstrikeCaret
< 3) // Make sure its visible
3204 widthOverstrikeCaret
= 3;
3207 caretWidthOffset
= 1; // Move back so overlaps both character cells.
3208 xposCaret
+= xStart
;
3209 if (posDrag
.IsValid()) {
3210 /* Dragging text, use a line caret */
3211 rcCaret
.left
= xposCaret
- caretWidthOffset
;
3212 rcCaret
.right
= rcCaret
.left
+ vsDraw
.caretWidth
;
3213 } else if (inOverstrike
) {
3214 /* Overstrike (insert mode), use a modified bar caret */
3215 rcCaret
.top
= rcCaret
.bottom
- 2;
3216 rcCaret
.left
= xposCaret
+ 1;
3217 rcCaret
.right
= rcCaret
.left
+ widthOverstrikeCaret
- 1;
3218 } else if (vsDraw
.caretStyle
== CARETSTYLE_BLOCK
) {
3220 rcCaret
.left
= xposCaret
;
3221 if (!caretAtEOL
&& !caretAtEOF
&& (ll
->chars
[offset
] != '\t') && !(IsControlCharacter(ll
->chars
[offset
]))) {
3222 drawBlockCaret
= true;
3223 rcCaret
.right
= xposCaret
+ widthOverstrikeCaret
;
3225 rcCaret
.right
= xposCaret
+ vsDraw
.aveCharWidth
;
3229 rcCaret
.left
= xposCaret
- caretWidthOffset
;
3230 rcCaret
.right
= rcCaret
.left
+ vsDraw
.caretWidth
;
3232 ColourAllocated caretColour
= mainCaret
? vsDraw
.caretcolour
.allocated
: vsDraw
.additionalCaretColour
.allocated
;
3233 if (drawBlockCaret
) {
3234 DrawBlockCaret(surface
, vsDraw
, ll
, subLine
, xStart
, offset
, posCaret
.Position(), rcCaret
, caretColour
);
3236 surface
->FillRectangle(rcCaret
, caretColour
);
3245 void Editor::Paint(Surface
*surfaceWindow
, PRectangle rcArea
) {
3246 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
3247 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
3249 StyleToPositionInView(PositionAfterArea(rcArea
));
3251 pixmapLine
->Release();
3253 RefreshPixMaps(surfaceWindow
);
3255 PRectangle rcClient
= GetClientRectangle();
3256 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
3257 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3259 surfaceWindow
->SetPalette(&palette
, true);
3260 pixmapLine
->SetPalette(&palette
, !hasFocus
);
3262 int screenLinePaintFirst
= rcArea
.top
/ vs
.lineHeight
;
3264 int xStart
= vs
.fixedColumnWidth
- xOffset
;
3267 ypos
+= screenLinePaintFirst
* vs
.lineHeight
;
3268 int yposScreen
= screenLinePaintFirst
* vs
.lineHeight
;
3270 bool paintAbandonedByStyling
= paintState
== paintAbandoned
;
3272 // Deselect palette by selecting a temporary palette
3274 surfaceWindow
->SetPalette(&palTemp
, true);
3277 needUpdateUI
= false;
3280 RefreshPixMaps(surfaceWindow
);
3281 surfaceWindow
->SetPalette(&palette
, true);
3282 pixmapLine
->SetPalette(&palette
, !hasFocus
);
3285 // Call priority lines wrap on a window of lines which are likely
3286 // to rendered with the following paint (that is wrap the visible
3288 int startLineToWrap
= cs
.DocFromDisplay(topLine
) - 5;
3289 if (startLineToWrap
< 0)
3290 startLineToWrap
= 0;
3291 if (WrapLines(false, startLineToWrap
)) {
3292 // The wrapping process has changed the height of some lines so
3293 // abandon this paint for a complete repaint.
3294 if (AbandonPaint()) {
3297 RefreshPixMaps(surfaceWindow
); // In case pixmaps invalidated by scrollbar change
3299 PLATFORM_ASSERT(pixmapSelPattern
->Initialised());
3301 if (paintState
!= paintAbandoned
) {
3302 PaintSelMargin(surfaceWindow
, rcArea
);
3304 PRectangle rcRightMargin
= rcClient
;
3305 rcRightMargin
.left
= rcRightMargin
.right
- vs
.rightMarginWidth
;
3306 if (rcArea
.Intersects(rcRightMargin
)) {
3307 surfaceWindow
->FillRectangle(rcRightMargin
, vs
.styles
[STYLE_DEFAULT
].back
.allocated
);
3311 if (paintState
== paintAbandoned
) {
3312 // Either styling or NotifyUpdateUI noticed that painting is needed
3313 // outside the current painting rectangle
3314 //Platform::DebugPrintf("Abandoning paint\n");
3315 if (wrapState
!= eWrapNone
) {
3316 if (paintAbandonedByStyling
) {
3317 // Styling has spilled over a line end, such as occurs by starting a multiline
3318 // comment. The width of subsequent text may have changed, so rewrap.
3319 NeedWrapping(cs
.DocFromDisplay(topLine
));
3324 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
3327 if (rcArea
.right
> vs
.fixedColumnWidth
) {
3329 Surface
*surface
= surfaceWindow
;
3331 surface
= pixmapLine
;
3332 PLATFORM_ASSERT(pixmapLine
->Initialised());
3334 surface
->SetUnicodeMode(IsUnicodeMode());
3335 surface
->SetDBCSMode(CodePage());
3337 int visibleLine
= topLine
+ screenLinePaintFirst
;
3339 SelectionPosition posCaret
= sel
.RangeMain().caret
;
3340 if (posDrag
.IsValid())
3342 int lineCaret
= pdoc
->LineFromPosition(posCaret
.Position());
3344 // Remove selection margin from drawing area so text will not be drawn
3345 // on it in unbuffered mode.
3346 PRectangle rcTextArea
= rcClient
;
3347 rcTextArea
.left
= vs
.fixedColumnWidth
;
3348 rcTextArea
.right
-= vs
.rightMarginWidth
;
3349 surfaceWindow
->SetClip(rcTextArea
);
3351 // Loop on visible lines
3352 //double durLayout = 0.0;
3353 //double durPaint = 0.0;
3354 //double durCopy = 0.0;
3355 //ElapsedTime etWhole;
3356 int lineDocPrevious
= -1; // Used to avoid laying out one document line multiple times
3357 AutoLineLayout
ll(llc
, 0);
3358 while (visibleLine
< cs
.LinesDisplayed() && yposScreen
< rcArea
.bottom
) {
3360 int lineDoc
= cs
.DocFromDisplay(visibleLine
);
3361 // Only visible lines should be handled by the code within the loop
3362 PLATFORM_ASSERT(cs
.GetVisible(lineDoc
));
3363 int lineStartSet
= cs
.DisplayFromDoc(lineDoc
);
3364 int subLine
= visibleLine
- lineStartSet
;
3366 // Copy this line and its styles from the document into local arrays
3367 // and determine the x position at which each character starts.
3369 if (lineDoc
!= lineDocPrevious
) {
3371 ll
.Set(RetrieveLineLayout(lineDoc
));
3372 LayoutLine(lineDoc
, surface
, vs
, ll
, wrapWidth
);
3373 lineDocPrevious
= lineDoc
;
3375 //durLayout += et.Duration(true);
3378 ll
->containsCaret
= lineDoc
== lineCaret
;
3379 if (hideSelection
) {
3380 ll
->containsCaret
= false;
3383 GetHotSpotRange(ll
->hsStart
, ll
->hsEnd
);
3385 PRectangle rcLine
= rcClient
;
3387 rcLine
.bottom
= ypos
+ vs
.lineHeight
;
3389 Range
rangeLine(pdoc
->LineStart(lineDoc
), pdoc
->LineStart(lineDoc
+ 1));
3390 // Highlight the current braces if any
3391 ll
->SetBracesHighlight(rangeLine
, braces
, static_cast<char>(bracesMatchStyle
),
3392 highlightGuideColumn
* vs
.spaceWidth
);
3395 DrawLine(surface
, vs
, lineDoc
, visibleLine
, xStart
, rcLine
, ll
, subLine
);
3396 //durPaint += et.Duration(true);
3398 // Restore the previous styles for the brace highlights in case layout is in cache.
3399 ll
->RestoreBracesHighlight(rangeLine
, braces
);
3401 bool expanded
= cs
.GetExpanded(lineDoc
);
3402 // Paint the line above the fold
3403 if ((expanded
&& (foldFlags
& SC_FOLDFLAG_LINEBEFORE_EXPANDED
))
3405 (!expanded
&& (foldFlags
& SC_FOLDFLAG_LINEBEFORE_CONTRACTED
))) {
3406 if (pdoc
->GetLevel(lineDoc
) & SC_FOLDLEVELHEADERFLAG
) {
3407 PRectangle rcFoldLine
= rcLine
;
3408 rcFoldLine
.bottom
= rcFoldLine
.top
+ 1;
3409 surface
->FillRectangle(rcFoldLine
, vs
.styles
[STYLE_DEFAULT
].fore
.allocated
);
3412 // Paint the line below the fold
3413 if ((expanded
&& (foldFlags
& SC_FOLDFLAG_LINEAFTER_EXPANDED
))
3415 (!expanded
&& (foldFlags
& SC_FOLDFLAG_LINEAFTER_CONTRACTED
))) {
3416 if (pdoc
->GetLevel(lineDoc
) & SC_FOLDLEVELHEADERFLAG
) {
3417 PRectangle rcFoldLine
= rcLine
;
3418 rcFoldLine
.top
= rcFoldLine
.bottom
- 1;
3419 surface
->FillRectangle(rcFoldLine
, vs
.styles
[STYLE_DEFAULT
].fore
.allocated
);
3423 DrawCarets(surface
, vs
, lineDoc
, xStart
, rcLine
, ll
, subLine
);
3426 Point
from(vs
.fixedColumnWidth
, 0);
3427 PRectangle
rcCopyArea(vs
.fixedColumnWidth
, yposScreen
,
3428 rcClient
.right
, yposScreen
+ vs
.lineHeight
);
3429 surfaceWindow
->Copy(rcCopyArea
, from
, *pixmapLine
);
3432 lineWidthMaxSeen
= Platform::Maximum(
3433 lineWidthMaxSeen
, ll
->positions
[ll
->numCharsInLine
]);
3434 //durCopy += et.Duration(true);
3437 if (!bufferedDraw
) {
3438 ypos
+= vs
.lineHeight
;
3441 yposScreen
+= vs
.lineHeight
;
3447 //if (durPaint < 0.00000001)
3448 // durPaint = 0.00000001;
3450 // Right column limit indicator
3451 PRectangle rcBeyondEOF
= rcClient
;
3452 rcBeyondEOF
.left
= vs
.fixedColumnWidth
;
3453 rcBeyondEOF
.right
= rcBeyondEOF
.right
;
3454 rcBeyondEOF
.top
= (cs
.LinesDisplayed() - topLine
) * vs
.lineHeight
;
3455 if (rcBeyondEOF
.top
< rcBeyondEOF
.bottom
) {
3456 surfaceWindow
->FillRectangle(rcBeyondEOF
, vs
.styles
[STYLE_DEFAULT
].back
.allocated
);
3457 if (vs
.edgeState
== EDGE_LINE
) {
3458 int edgeX
= theEdge
* vs
.spaceWidth
;
3459 rcBeyondEOF
.left
= edgeX
+ xStart
;
3460 rcBeyondEOF
.right
= rcBeyondEOF
.left
+ 1;
3461 surfaceWindow
->FillRectangle(rcBeyondEOF
, vs
.edgecolour
.allocated
);
3464 //Platform::DebugPrintf(
3465 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
3466 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
3471 // Space (3 space characters) between line numbers and text when printing.
3472 #define lineNumberPrintSpace " "
3474 ColourDesired
InvertedLight(ColourDesired orig
) {
3475 unsigned int r
= orig
.GetRed();
3476 unsigned int g
= orig
.GetGreen();
3477 unsigned int b
= orig
.GetBlue();
3478 unsigned int l
= (r
+ g
+ b
) / 3; // There is a better calculation for this that matches human eye
3479 unsigned int il
= 0xff - l
;
3481 return ColourDesired(0xff, 0xff, 0xff);
3485 return ColourDesired(Platform::Minimum(r
, 0xff), Platform::Minimum(g
, 0xff), Platform::Minimum(b
, 0xff));
3488 // This is mostly copied from the Paint method but with some things omitted
3489 // such as the margin markers, line numbers, selection and caret
3490 // Should be merged back into a combined Draw method.
3491 long Editor::FormatRange(bool draw
, Sci_RangeToFormat
*pfr
) {
3495 AutoSurface
surface(pfr
->hdc
, this);
3498 AutoSurface
surfaceMeasure(pfr
->hdcTarget
, this);
3499 if (!surfaceMeasure
) {
3503 // Can't use measurements cached for screen
3506 ViewStyle
vsPrint(vs
);
3508 // Modify the view style for printing as do not normally want any of the transient features to be printed
3509 // Printing supports only the line number margin.
3510 int lineNumberIndex
= -1;
3511 for (int margin
= 0; margin
< ViewStyle::margins
; margin
++) {
3512 if ((vsPrint
.ms
[margin
].style
== SC_MARGIN_NUMBER
) && (vsPrint
.ms
[margin
].width
> 0)) {
3513 lineNumberIndex
= margin
;
3515 vsPrint
.ms
[margin
].width
= 0;
3518 vsPrint
.showMarkedLines
= false;
3519 vsPrint
.fixedColumnWidth
= 0;
3520 vsPrint
.zoomLevel
= printMagnification
;
3521 vsPrint
.viewIndentationGuides
= ivNone
;
3522 // Don't show the selection when printing
3523 vsPrint
.selbackset
= false;
3524 vsPrint
.selforeset
= false;
3525 vsPrint
.selAlpha
= SC_ALPHA_NOALPHA
;
3526 vsPrint
.selAdditionalAlpha
= SC_ALPHA_NOALPHA
;
3527 vsPrint
.whitespaceBackgroundSet
= false;
3528 vsPrint
.whitespaceForegroundSet
= false;
3529 vsPrint
.showCaretLineBackground
= false;
3531 // Set colours for printing according to users settings
3532 for (size_t sty
= 0; sty
< vsPrint
.stylesSize
; sty
++) {
3533 if (printColourMode
== SC_PRINT_INVERTLIGHT
) {
3534 vsPrint
.styles
[sty
].fore
.desired
= InvertedLight(vsPrint
.styles
[sty
].fore
.desired
);
3535 vsPrint
.styles
[sty
].back
.desired
= InvertedLight(vsPrint
.styles
[sty
].back
.desired
);
3536 } else if (printColourMode
== SC_PRINT_BLACKONWHITE
) {
3537 vsPrint
.styles
[sty
].fore
.desired
= ColourDesired(0, 0, 0);
3538 vsPrint
.styles
[sty
].back
.desired
= ColourDesired(0xff, 0xff, 0xff);
3539 } else if (printColourMode
== SC_PRINT_COLOURONWHITE
) {
3540 vsPrint
.styles
[sty
].back
.desired
= ColourDesired(0xff, 0xff, 0xff);
3541 } else if (printColourMode
== SC_PRINT_COLOURONWHITEDEFAULTBG
) {
3542 if (sty
<= STYLE_DEFAULT
) {
3543 vsPrint
.styles
[sty
].back
.desired
= ColourDesired(0xff, 0xff, 0xff);
3547 // White background for the line numbers
3548 vsPrint
.styles
[STYLE_LINENUMBER
].back
.desired
= ColourDesired(0xff, 0xff, 0xff);
3550 vsPrint
.Refresh(*surfaceMeasure
);
3551 // Determining width must hapen after fonts have been realised in Refresh
3552 int lineNumberWidth
= 0;
3553 if (lineNumberIndex
>= 0) {
3554 lineNumberWidth
= surfaceMeasure
->WidthText(vsPrint
.styles
[STYLE_LINENUMBER
].font
,
3555 "99999" lineNumberPrintSpace
, 5 + istrlen(lineNumberPrintSpace
));
3556 vsPrint
.ms
[lineNumberIndex
].width
= lineNumberWidth
;
3557 vsPrint
.Refresh(*surfaceMeasure
); // Recalculate fixedColumnWidth
3559 // Ensure colours are set up
3560 vsPrint
.RefreshColourPalette(palette
, true);
3561 vsPrint
.RefreshColourPalette(palette
, false);
3563 int linePrintStart
= pdoc
->LineFromPosition(pfr
->chrg
.cpMin
);
3564 int linePrintLast
= linePrintStart
+ (pfr
->rc
.bottom
- pfr
->rc
.top
) / vsPrint
.lineHeight
- 1;
3565 if (linePrintLast
< linePrintStart
)
3566 linePrintLast
= linePrintStart
;
3567 int linePrintMax
= pdoc
->LineFromPosition(pfr
->chrg
.cpMax
);
3568 if (linePrintLast
> linePrintMax
)
3569 linePrintLast
= linePrintMax
;
3570 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
3571 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
3572 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
3573 int endPosPrint
= pdoc
->Length();
3574 if (linePrintLast
< pdoc
->LinesTotal())
3575 endPosPrint
= pdoc
->LineStart(linePrintLast
+ 1);
3577 // Ensure we are styled to where we are formatting.
3578 pdoc
->EnsureStyledTo(endPosPrint
);
3580 int xStart
= vsPrint
.fixedColumnWidth
+ pfr
->rc
.left
;
3581 int ypos
= pfr
->rc
.top
;
3583 int lineDoc
= linePrintStart
;
3585 int nPrintPos
= pfr
->chrg
.cpMin
;
3586 int visibleLine
= 0;
3587 int widthPrint
= pfr
->rc
.right
- pfr
->rc
.left
- vsPrint
.fixedColumnWidth
;
3588 if (printWrapState
== eWrapNone
)
3589 widthPrint
= LineLayout::wrapWidthInfinite
;
3591 while (lineDoc
<= linePrintLast
&& ypos
< pfr
->rc
.bottom
) {
3593 // When printing, the hdc and hdcTarget may be the same, so
3594 // changing the state of surfaceMeasure may change the underlying
3595 // state of surface. Therefore, any cached state is discarded before
3596 // using each surface.
3597 surfaceMeasure
->FlushCachedState();
3599 // Copy this line and its styles from the document into local arrays
3600 // and determine the x position at which each character starts.
3601 LineLayout
ll(8000);
3602 LayoutLine(lineDoc
, surfaceMeasure
, vsPrint
, &ll
, widthPrint
);
3604 ll
.containsCaret
= false;
3607 rcLine
.left
= pfr
->rc
.left
;
3609 rcLine
.right
= pfr
->rc
.right
- 1;
3610 rcLine
.bottom
= ypos
+ vsPrint
.lineHeight
;
3612 // When document line is wrapped over multiple display lines, find where
3613 // to start printing from to ensure a particular position is on the first
3614 // line of the page.
3615 if (visibleLine
== 0) {
3616 int startWithinLine
= nPrintPos
- pdoc
->LineStart(lineDoc
);
3617 for (int iwl
= 0; iwl
< ll
.lines
- 1; iwl
++) {
3618 if (ll
.LineStart(iwl
) <= startWithinLine
&& ll
.LineStart(iwl
+ 1) >= startWithinLine
) {
3623 if (ll
.lines
> 1 && startWithinLine
>= ll
.LineStart(ll
.lines
- 1)) {
3624 visibleLine
= -(ll
.lines
- 1);
3628 if (draw
&& lineNumberWidth
&&
3629 (ypos
+ vsPrint
.lineHeight
<= pfr
->rc
.bottom
) &&
3630 (visibleLine
>= 0)) {
3632 sprintf(number
, "%d" lineNumberPrintSpace
, lineDoc
+ 1);
3633 PRectangle rcNumber
= rcLine
;
3634 rcNumber
.right
= rcNumber
.left
+ lineNumberWidth
;
3636 rcNumber
.left
= rcNumber
.right
- surfaceMeasure
->WidthText(
3637 vsPrint
.styles
[STYLE_LINENUMBER
].font
, number
, istrlen(number
));
3638 surface
->FlushCachedState();
3639 surface
->DrawTextNoClip(rcNumber
, vsPrint
.styles
[STYLE_LINENUMBER
].font
,
3640 ypos
+ vsPrint
.maxAscent
, number
, istrlen(number
),
3641 vsPrint
.styles
[STYLE_LINENUMBER
].fore
.allocated
,
3642 vsPrint
.styles
[STYLE_LINENUMBER
].back
.allocated
);
3646 surface
->FlushCachedState();
3648 for (int iwl
= 0; iwl
< ll
.lines
; iwl
++) {
3649 if (ypos
+ vsPrint
.lineHeight
<= pfr
->rc
.bottom
) {
3650 if (visibleLine
>= 0) {
3653 rcLine
.bottom
= ypos
+ vsPrint
.lineHeight
;
3654 DrawLine(surface
, vsPrint
, lineDoc
, visibleLine
, xStart
, rcLine
, &ll
, iwl
);
3656 ypos
+= vsPrint
.lineHeight
;
3659 if (iwl
== ll
.lines
- 1)
3660 nPrintPos
= pdoc
->LineStart(lineDoc
+ 1);
3662 nPrintPos
+= ll
.LineStart(iwl
+ 1) - ll
.LineStart(iwl
);
3669 // Clear cache so measurements are not used for screen
3675 int Editor::TextWidth(int style
, const char *text
) {
3677 AutoSurface
surface(this);
3679 return surface
->WidthText(vs
.styles
[style
].font
, text
, istrlen(text
));
3685 // Empty method is overridden on GTK+ to show / hide scrollbars
3686 void Editor::ReconfigureScrollBars() {}
3688 void Editor::SetScrollBars() {
3691 int nMax
= MaxScrollPos();
3692 int nPage
= LinesOnScreen();
3693 bool modified
= ModifyScrollBars(nMax
+ nPage
- 1, nPage
);
3698 // TODO: ensure always showing as many lines as possible
3699 // May not be, if, for example, window made larger
3700 if (topLine
> MaxScrollPos()) {
3701 SetTopLine(Platform::Clamp(topLine
, 0, MaxScrollPos()));
3702 SetVerticalScrollPos();
3706 if (!AbandonPaint())
3709 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
3712 void Editor::ChangeSize() {
3715 if (wrapState
!= eWrapNone
) {
3716 PRectangle rcTextArea
= GetClientRectangle();
3717 rcTextArea
.left
= vs
.fixedColumnWidth
;
3718 rcTextArea
.right
-= vs
.rightMarginWidth
;
3719 if (wrapWidth
!= rcTextArea
.Width()) {
3726 int Editor::InsertSpace(int position
, unsigned int spaces
) {
3728 std::string
spaceText(spaces
, ' ');
3729 pdoc
->InsertString(position
, spaceText
.c_str(), spaces
);
3735 void Editor::AddChar(char ch
) {
3742 void Editor::FilterSelections() {
3743 if (!additionalSelectionTyping
&& (sel
.Count() > 1)) {
3744 SelectionRange rangeOnly
= sel
.RangeMain();
3745 InvalidateSelection(rangeOnly
, true);
3746 sel
.SetSelection(rangeOnly
);
3750 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
3751 void Editor::AddCharUTF(char *s
, unsigned int len
, bool treatAsDBCS
) {
3754 UndoGroup
ug(pdoc
, (sel
.Count() > 1) || !sel
.Empty() || inOverstrike
);
3755 for (size_t r
=0; r
<sel
.Count(); r
++) {
3756 if (!RangeContainsProtected(sel
.Range(r
).Start().Position(),
3757 sel
.Range(r
).End().Position())) {
3758 int positionInsert
= sel
.Range(r
).Start().Position();
3759 if (!sel
.Range(r
).Empty()) {
3760 if (sel
.Range(r
).Length()) {
3761 pdoc
->DeleteChars(positionInsert
, sel
.Range(r
).Length());
3762 sel
.Range(r
).ClearVirtualSpace();
3764 // Range is all virtual so collapse to start of virtual space
3765 sel
.Range(r
).MinimizeVirtualSpace();
3767 } else if (inOverstrike
) {
3768 if (positionInsert
< pdoc
->Length()) {
3769 if (!IsEOLChar(pdoc
->CharAt(positionInsert
))) {
3770 pdoc
->DelChar(positionInsert
);
3771 sel
.Range(r
).ClearVirtualSpace();
3775 positionInsert
= InsertSpace(positionInsert
, sel
.Range(r
).caret
.VirtualSpace());
3776 if (pdoc
->InsertString(positionInsert
, s
, len
)) {
3777 sel
.Range(r
).caret
.SetPosition(positionInsert
+ len
);
3778 sel
.Range(r
).anchor
.SetPosition(positionInsert
+ len
);
3780 sel
.Range(r
).ClearVirtualSpace();
3781 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
3782 if (wrapState
!= eWrapNone
) {
3783 AutoSurface
surface(this);
3785 if (WrapOneLine(surface
, pdoc
->LineFromPosition(positionInsert
))) {
3787 SetVerticalScrollPos();
3795 if (wrapState
!= eWrapNone
) {
3798 ThinRectangularRange();
3799 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
3800 EnsureCaretVisible();
3801 // Avoid blinking during rapid typing:
3802 ShowCaretAtCurrentPosition();
3808 NotifyChar((static_cast<unsigned char>(s
[0]) << 8) |
3809 static_cast<unsigned char>(s
[1]));
3811 int byte
= static_cast<unsigned char>(s
[0]);
3812 if ((byte
< 0xC0) || (1 == len
)) {
3813 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
3814 // characters when not in UTF-8 mode.
3815 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
3816 // characters representing themselves.
3818 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
3819 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
3820 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
3822 int byte2
= static_cast<unsigned char>(s
[1]);
3823 if ((byte2
& 0xC0) == 0x80) {
3824 // Two-byte-character lead-byte followed by a trail-byte.
3825 byte
= (((byte
& 0x1F) << 6) | (byte2
& 0x3F));
3827 // A two-byte-character lead-byte not followed by trail-byte
3828 // represents itself.
3829 } else if (byte
< 0xF0) {
3830 int byte2
= static_cast<unsigned char>(s
[1]);
3831 int byte3
= static_cast<unsigned char>(s
[2]);
3832 if (((byte2
& 0xC0) == 0x80) && ((byte3
& 0xC0) == 0x80)) {
3833 // Three-byte-character lead byte followed by two trail bytes.
3834 byte
= (((byte
& 0x0F) << 12) | ((byte2
& 0x3F) << 6) |
3837 // A three-byte-character lead-byte not followed by two trail-bytes
3838 // represents itself.
3844 if (recordingMacro
) {
3845 NotifyMacroRecord(SCI_REPLACESEL
, 0, reinterpret_cast<sptr_t
>(s
));
3849 void Editor::InsertPaste(SelectionPosition selStart
, const char *text
, int len
) {
3850 if (multiPasteMode
== SC_MULTIPASTE_ONCE
) {
3851 selStart
= SelectionPosition(InsertSpace(selStart
.Position(), selStart
.VirtualSpace()));
3852 if (pdoc
->InsertString(selStart
.Position(), text
, len
)) {
3853 SetEmptySelection(selStart
.Position() + len
);
3856 // SC_MULTIPASTE_EACH
3857 for (size_t r
=0; r
<sel
.Count(); r
++) {
3858 if (!RangeContainsProtected(sel
.Range(r
).Start().Position(),
3859 sel
.Range(r
).End().Position())) {
3860 int positionInsert
= sel
.Range(r
).Start().Position();
3861 if (!sel
.Range(r
).Empty()) {
3862 if (sel
.Range(r
).Length()) {
3863 pdoc
->DeleteChars(positionInsert
, sel
.Range(r
).Length());
3864 sel
.Range(r
).ClearVirtualSpace();
3866 // Range is all virtual so collapse to start of virtual space
3867 sel
.Range(r
).MinimizeVirtualSpace();
3870 positionInsert
= InsertSpace(positionInsert
, sel
.Range(r
).caret
.VirtualSpace());
3871 if (pdoc
->InsertString(positionInsert
, text
, len
)) {
3872 sel
.Range(r
).caret
.SetPosition(positionInsert
+ len
);
3873 sel
.Range(r
).anchor
.SetPosition(positionInsert
+ len
);
3875 sel
.Range(r
).ClearVirtualSpace();
3881 void Editor::ClearSelection() {
3882 if (!sel
.IsRectangular())
3885 for (size_t r
=0; r
<sel
.Count(); r
++) {
3886 if (!sel
.Range(r
).Empty()) {
3887 if (!RangeContainsProtected(sel
.Range(r
).Start().Position(),
3888 sel
.Range(r
).End().Position())) {
3889 pdoc
->DeleteChars(sel
.Range(r
).Start().Position(),
3890 sel
.Range(r
).Length());
3891 sel
.Range(r
) = sel
.Range(r
).Start();
3895 ThinRectangularRange();
3896 sel
.RemoveDuplicates();
3900 void Editor::ClearAll() {
3903 if (0 != pdoc
->Length()) {
3904 pdoc
->DeleteChars(0, pdoc
->Length());
3906 if (!pdoc
->IsReadOnly()) {
3908 pdoc
->AnnotationClearAll();
3909 pdoc
->MarginClearAll();
3914 SetVerticalScrollPos();
3915 InvalidateStyleRedraw();
3918 void Editor::ClearDocumentStyle() {
3919 Decoration
*deco
= pdoc
->decorations
.root
;
3921 // Save next in case deco deleted
3922 Decoration
*decoNext
= deco
->next
;
3923 if (deco
->indicator
< INDIC_CONTAINER
) {
3924 pdoc
->decorations
.SetCurrentIndicator(deco
->indicator
);
3925 pdoc
->DecorationFillRange(0, 0, pdoc
->Length());
3929 pdoc
->StartStyling(0, '\377');
3930 pdoc
->SetStyleFor(pdoc
->Length(), 0);
3932 pdoc
->ClearLevels();
3935 void Editor::CopyAllowLine() {
3936 SelectionText selectedText
;
3937 CopySelectionRange(&selectedText
, true);
3938 CopyToClipboard(selectedText
);
3941 void Editor::Cut() {
3942 pdoc
->CheckReadOnly();
3943 if (!pdoc
->IsReadOnly() && !SelectionContainsProtected()) {
3949 void Editor::PasteRectangular(SelectionPosition pos
, const char *ptr
, int len
) {
3950 if (pdoc
->IsReadOnly() || SelectionContainsProtected()) {
3954 sel
.RangeMain() = SelectionRange(pos
);
3955 int line
= pdoc
->LineFromPosition(sel
.MainCaret());
3957 sel
.RangeMain().caret
= SelectionPosition(
3958 InsertSpace(sel
.RangeMain().caret
.Position(), sel
.RangeMain().caret
.VirtualSpace()));
3959 int xInsert
= XFromPosition(sel
.RangeMain().caret
);
3960 bool prevCr
= false;
3961 while ((len
> 0) && IsEOLChar(ptr
[len
-1]))
3963 for (int i
= 0; i
< len
; i
++) {
3964 if (IsEOLChar(ptr
[i
])) {
3965 if ((ptr
[i
] == '\r') || (!prevCr
))
3967 if (line
>= pdoc
->LinesTotal()) {
3968 if (pdoc
->eolMode
!= SC_EOL_LF
)
3969 pdoc
->InsertChar(pdoc
->Length(), '\r');
3970 if (pdoc
->eolMode
!= SC_EOL_CR
)
3971 pdoc
->InsertChar(pdoc
->Length(), '\n');
3973 // Pad the end of lines with spaces if required
3974 sel
.RangeMain().caret
.SetPosition(PositionFromLineX(line
, xInsert
));
3975 if ((XFromPosition(sel
.MainCaret()) < xInsert
) && (i
+ 1 < len
)) {
3976 while (XFromPosition(sel
.MainCaret()) < xInsert
) {
3977 pdoc
->InsertChar(sel
.MainCaret(), ' ');
3978 sel
.RangeMain().caret
.Add(1);
3981 prevCr
= ptr
[i
] == '\r';
3983 pdoc
->InsertString(sel
.MainCaret(), ptr
+ i
, 1);
3984 sel
.RangeMain().caret
.Add(1);
3988 SetEmptySelection(pos
);
3991 bool Editor::CanPaste() {
3992 return !pdoc
->IsReadOnly() && !SelectionContainsProtected();
3995 void Editor::Clear() {
3996 // If multiple selections, don't delete EOLS
3998 UndoGroup
ug(pdoc
, sel
.Count() > 1);
3999 for (size_t r
=0; r
<sel
.Count(); r
++) {
4000 if (!RangeContainsProtected(sel
.Range(r
).caret
.Position(), sel
.Range(r
).caret
.Position() + 1)) {
4001 if (sel
.Range(r
).Start().VirtualSpace()) {
4002 if (sel
.Range(r
).anchor
< sel
.Range(r
).caret
)
4003 sel
.Range(r
) = SelectionPosition(InsertSpace(sel
.Range(r
).anchor
.Position(), sel
.Range(r
).anchor
.VirtualSpace()));
4005 sel
.Range(r
) = SelectionPosition(InsertSpace(sel
.Range(r
).caret
.Position(), sel
.Range(r
).caret
.VirtualSpace()));
4007 if ((sel
.Count() == 1) || !IsEOLChar(pdoc
->CharAt(sel
.Range(r
).caret
.Position()))) {
4008 pdoc
->DelChar(sel
.Range(r
).caret
.Position());
4009 sel
.Range(r
).ClearVirtualSpace();
4010 } // else multiple selection so don't eat line ends
4012 sel
.Range(r
).ClearVirtualSpace();
4018 sel
.RemoveDuplicates();
4021 void Editor::SelectAll() {
4023 SetSelection(0, pdoc
->Length());
4027 void Editor::Undo() {
4028 if (pdoc
->CanUndo()) {
4030 int newPos
= pdoc
->Undo();
4032 SetEmptySelection(newPos
);
4033 EnsureCaretVisible();
4037 void Editor::Redo() {
4038 if (pdoc
->CanRedo()) {
4039 int newPos
= pdoc
->Redo();
4041 SetEmptySelection(newPos
);
4042 EnsureCaretVisible();
4046 void Editor::DelChar() {
4047 if (!RangeContainsProtected(sel
.MainCaret(), sel
.MainCaret() + 1)) {
4048 pdoc
->DelChar(sel
.MainCaret());
4050 // Avoid blinking during rapid typing:
4051 ShowCaretAtCurrentPosition();
4054 void Editor::DelCharBack(bool allowLineStartDeletion
) {
4055 if (!sel
.IsRectangular())
4057 if (sel
.IsRectangular())
4058 allowLineStartDeletion
= false;
4059 UndoGroup
ug(pdoc
, (sel
.Count() > 1) || !sel
.Empty());
4061 for (size_t r
=0; r
<sel
.Count(); r
++) {
4062 if (!RangeContainsProtected(sel
.Range(r
).caret
.Position(), sel
.Range(r
).caret
.Position() + 1)) {
4063 if (sel
.Range(r
).caret
.VirtualSpace()) {
4064 sel
.Range(r
).caret
.SetVirtualSpace(sel
.Range(r
).caret
.VirtualSpace() - 1);
4065 sel
.Range(r
).anchor
.SetVirtualSpace(sel
.Range(r
).caret
.VirtualSpace());
4067 int lineCurrentPos
= pdoc
->LineFromPosition(sel
.Range(r
).caret
.Position());
4068 if (allowLineStartDeletion
|| (pdoc
->LineStart(lineCurrentPos
) != sel
.Range(r
).caret
.Position())) {
4069 if (pdoc
->GetColumn(sel
.Range(r
).caret
.Position()) <= pdoc
->GetLineIndentation(lineCurrentPos
) &&
4070 pdoc
->GetColumn(sel
.Range(r
).caret
.Position()) > 0 && pdoc
->backspaceUnindents
) {
4071 UndoGroup
ugInner(pdoc
, !ug
.Needed());
4072 int indentation
= pdoc
->GetLineIndentation(lineCurrentPos
);
4073 int indentationStep
= pdoc
->IndentSize();
4074 if (indentation
% indentationStep
== 0) {
4075 pdoc
->SetLineIndentation(lineCurrentPos
, indentation
- indentationStep
);
4077 pdoc
->SetLineIndentation(lineCurrentPos
, indentation
- (indentation
% indentationStep
));
4079 // SetEmptySelection
4080 sel
.Range(r
) = SelectionRange(pdoc
->GetLineIndentPosition(lineCurrentPos
),
4081 pdoc
->GetLineIndentPosition(lineCurrentPos
));
4083 pdoc
->DelCharBack(sel
.Range(r
).caret
.Position());
4088 sel
.Range(r
).ClearVirtualSpace();
4094 sel
.RemoveDuplicates();
4095 // Avoid blinking during rapid typing:
4096 ShowCaretAtCurrentPosition();
4099 void Editor::NotifyFocus(bool) {}
4101 void Editor::NotifyStyleToNeeded(int endStyleNeeded
) {
4102 SCNotification scn
= {0};
4103 scn
.nmhdr
.code
= SCN_STYLENEEDED
;
4104 scn
.position
= endStyleNeeded
;
4108 void Editor::NotifyStyleNeeded(Document
*, void *, int endStyleNeeded
) {
4109 NotifyStyleToNeeded(endStyleNeeded
);
4112 void Editor::NotifyChar(int ch
) {
4113 SCNotification scn
= {0};
4114 scn
.nmhdr
.code
= SCN_CHARADDED
;
4119 void Editor::NotifySavePoint(bool isSavePoint
) {
4120 SCNotification scn
= {0};
4122 scn
.nmhdr
.code
= SCN_SAVEPOINTREACHED
;
4124 scn
.nmhdr
.code
= SCN_SAVEPOINTLEFT
;
4129 void Editor::NotifyModifyAttempt() {
4130 SCNotification scn
= {0};
4131 scn
.nmhdr
.code
= SCN_MODIFYATTEMPTRO
;
4135 void Editor::NotifyDoubleClick(Point pt
, bool shift
, bool ctrl
, bool alt
) {
4136 SCNotification scn
= {0};
4137 scn
.nmhdr
.code
= SCN_DOUBLECLICK
;
4138 scn
.line
= LineFromLocation(pt
);
4139 scn
.position
= PositionFromLocation(pt
, true);
4140 scn
.modifiers
= (shift
? SCI_SHIFT
: 0) | (ctrl
? SCI_CTRL
: 0) |
4141 (alt
? SCI_ALT
: 0);
4145 void Editor::NotifyHotSpotDoubleClicked(int position
, bool shift
, bool ctrl
, bool alt
) {
4146 SCNotification scn
= {0};
4147 scn
.nmhdr
.code
= SCN_HOTSPOTDOUBLECLICK
;
4148 scn
.position
= position
;
4149 scn
.modifiers
= (shift
? SCI_SHIFT
: 0) | (ctrl
? SCI_CTRL
: 0) |
4150 (alt
? SCI_ALT
: 0);
4154 void Editor::NotifyHotSpotClicked(int position
, bool shift
, bool ctrl
, bool alt
) {
4155 SCNotification scn
= {0};
4156 scn
.nmhdr
.code
= SCN_HOTSPOTCLICK
;
4157 scn
.position
= position
;
4158 scn
.modifiers
= (shift
? SCI_SHIFT
: 0) | (ctrl
? SCI_CTRL
: 0) |
4159 (alt
? SCI_ALT
: 0);
4163 void Editor::NotifyUpdateUI() {
4164 SCNotification scn
= {0};
4165 scn
.nmhdr
.code
= SCN_UPDATEUI
;
4169 void Editor::NotifyPainted() {
4170 SCNotification scn
= {0};
4171 scn
.nmhdr
.code
= SCN_PAINTED
;
4175 void Editor::NotifyIndicatorClick(bool click
, int position
, bool shift
, bool ctrl
, bool alt
) {
4176 int mask
= pdoc
->decorations
.AllOnFor(position
);
4177 if ((click
&& mask
) || pdoc
->decorations
.clickNotified
) {
4178 SCNotification scn
= {0};
4179 pdoc
->decorations
.clickNotified
= click
;
4180 scn
.nmhdr
.code
= click
? SCN_INDICATORCLICK
: SCN_INDICATORRELEASE
;
4181 scn
.modifiers
= (shift
? SCI_SHIFT
: 0) | (ctrl
? SCI_CTRL
: 0) | (alt
? SCI_ALT
: 0);
4182 scn
.position
= position
;
4187 bool Editor::NotifyMarginClick(Point pt
, bool shift
, bool ctrl
, bool alt
) {
4188 int marginClicked
= -1;
4190 for (int margin
= 0; margin
< ViewStyle::margins
; margin
++) {
4191 if ((pt
.x
> x
) && (pt
.x
< x
+ vs
.ms
[margin
].width
))
4192 marginClicked
= margin
;
4193 x
+= vs
.ms
[margin
].width
;
4195 if ((marginClicked
>= 0) && vs
.ms
[marginClicked
].sensitive
) {
4196 SCNotification scn
= {0};
4197 scn
.nmhdr
.code
= SCN_MARGINCLICK
;
4198 scn
.modifiers
= (shift
? SCI_SHIFT
: 0) | (ctrl
? SCI_CTRL
: 0) |
4199 (alt
? SCI_ALT
: 0);
4200 scn
.position
= pdoc
->LineStart(LineFromLocation(pt
));
4201 scn
.margin
= marginClicked
;
4209 void Editor::NotifyNeedShown(int pos
, int len
) {
4210 SCNotification scn
= {0};
4211 scn
.nmhdr
.code
= SCN_NEEDSHOWN
;
4217 void Editor::NotifyDwelling(Point pt
, bool state
) {
4218 SCNotification scn
= {0};
4219 scn
.nmhdr
.code
= state
? SCN_DWELLSTART
: SCN_DWELLEND
;
4220 scn
.position
= PositionFromLocation(pt
, true);
4226 void Editor::NotifyZoom() {
4227 SCNotification scn
= {0};
4228 scn
.nmhdr
.code
= SCN_ZOOM
;
4232 // Notifications from document
4233 void Editor::NotifyModifyAttempt(Document
*, void *) {
4234 //Platform::DebugPrintf("** Modify Attempt\n");
4235 NotifyModifyAttempt();
4238 void Editor::NotifySavePoint(Document
*, void *, bool atSavePoint
) {
4239 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
4240 NotifySavePoint(atSavePoint
);
4243 void Editor::CheckModificationForWrap(DocModification mh
) {
4244 if (mh
.modificationType
& (SC_MOD_INSERTTEXT
| SC_MOD_DELETETEXT
)) {
4245 llc
.Invalidate(LineLayout::llCheckTextAndStyle
);
4246 if (wrapState
!= eWrapNone
) {
4247 int lineDoc
= pdoc
->LineFromPosition(mh
.position
);
4248 int lines
= Platform::Maximum(0, mh
.linesAdded
);
4249 NeedWrapping(lineDoc
, lineDoc
+ lines
+ 1);
4251 // Fix up annotation heights
4252 int lineDoc
= pdoc
->LineFromPosition(mh
.position
);
4253 int lines
= Platform::Maximum(0, mh
.linesAdded
);
4254 SetAnnotationHeights(lineDoc
, lineDoc
+ lines
+ 2);
4258 // Move a position so it is still after the same character as before the insertion.
4259 static inline int MovePositionForInsertion(int position
, int startInsertion
, int length
) {
4260 if (position
> startInsertion
) {
4261 return position
+ length
;
4266 // Move a position so it is still after the same character as before the deletion if that
4267 // character is still present else after the previous surviving character.
4268 static inline int MovePositionForDeletion(int position
, int startDeletion
, int length
) {
4269 if (position
> startDeletion
) {
4270 int endDeletion
= startDeletion
+ length
;
4271 if (position
> endDeletion
) {
4272 return position
- length
;
4274 return startDeletion
;
4281 void Editor::NotifyModified(Document
*, DocModification mh
, void *) {
4282 needUpdateUI
= true;
4283 if (paintState
== painting
) {
4284 CheckForChangeOutsidePaint(Range(mh
.position
, mh
.position
+ mh
.length
));
4286 if (mh
.modificationType
& SC_MOD_CHANGELINESTATE
) {
4287 if (paintState
== painting
) {
4288 CheckForChangeOutsidePaint(
4289 Range(pdoc
->LineStart(mh
.line
), pdoc
->LineStart(mh
.line
+ 1)));
4291 // Could check that change is before last visible line.
4295 if (mh
.modificationType
& (SC_MOD_CHANGESTYLE
| SC_MOD_CHANGEINDICATOR
)) {
4296 if (mh
.modificationType
& SC_MOD_CHANGESTYLE
) {
4297 pdoc
->IncrementStyleClock();
4299 if (paintState
== notPainting
) {
4300 if (mh
.position
< pdoc
->LineStart(topLine
)) {
4301 // Styling performed before this view
4304 InvalidateRange(mh
.position
, mh
.position
+ mh
.length
);
4307 if (mh
.modificationType
& SC_MOD_CHANGESTYLE
) {
4308 llc
.Invalidate(LineLayout::llCheckTextAndStyle
);
4311 // Move selection and brace highlights
4312 if (mh
.modificationType
& SC_MOD_INSERTTEXT
) {
4313 sel
.MovePositions(true, mh
.position
, mh
.length
);
4314 braces
[0] = MovePositionForInsertion(braces
[0], mh
.position
, mh
.length
);
4315 braces
[1] = MovePositionForInsertion(braces
[1], mh
.position
, mh
.length
);
4316 } else if (mh
.modificationType
& SC_MOD_DELETETEXT
) {
4317 sel
.MovePositions(false, mh
.position
, mh
.length
);
4318 braces
[0] = MovePositionForDeletion(braces
[0], mh
.position
, mh
.length
);
4319 braces
[1] = MovePositionForDeletion(braces
[1], mh
.position
, mh
.length
);
4321 if (cs
.LinesDisplayed() < cs
.LinesInDoc()) {
4322 // Some lines are hidden so may need shown.
4323 // TODO: check if the modified area is hidden.
4324 if (mh
.modificationType
& SC_MOD_BEFOREINSERT
) {
4325 NotifyNeedShown(mh
.position
, 0);
4326 } else if (mh
.modificationType
& SC_MOD_BEFOREDELETE
) {
4327 NotifyNeedShown(mh
.position
, mh
.length
);
4330 if (mh
.linesAdded
!= 0) {
4331 // Update contraction state for inserted and removed lines
4332 // lineOfPos should be calculated in context of state before modification, shouldn't it
4333 int lineOfPos
= pdoc
->LineFromPosition(mh
.position
);
4334 if (mh
.linesAdded
> 0) {
4335 cs
.InsertLines(lineOfPos
, mh
.linesAdded
);
4337 cs
.DeleteLines(lineOfPos
, -mh
.linesAdded
);
4340 if (mh
.modificationType
& SC_MOD_CHANGEANNOTATION
) {
4341 int lineDoc
= pdoc
->LineFromPosition(mh
.position
);
4342 if (vs
.annotationVisible
) {
4343 cs
.SetHeight(lineDoc
, cs
.GetHeight(lineDoc
) + mh
.annotationLinesAdded
);
4346 CheckModificationForWrap(mh
);
4347 if (mh
.linesAdded
!= 0) {
4348 // Avoid scrolling of display if change before current display
4349 if (mh
.position
< posTopLine
&& !CanDeferToLastStep(mh
)) {
4350 int newTop
= Platform::Clamp(topLine
+ mh
.linesAdded
, 0, MaxScrollPos());
4351 if (newTop
!= topLine
) {
4353 SetVerticalScrollPos();
4357 //Platform::DebugPrintf("** %x Doc Changed\n", this);
4358 // TODO: could invalidate from mh.startModification to end of screen
4359 //InvalidateRange(mh.position, mh.position + mh.length);
4360 if (paintState
== notPainting
&& !CanDeferToLastStep(mh
)) {
4361 QueueStyling(pdoc
->Length());
4365 //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
4366 // mh.position, mh.position + mh.length);
4367 if (paintState
== notPainting
&& mh
.length
&& !CanEliminate(mh
)) {
4368 QueueStyling(mh
.position
+ mh
.length
);
4369 InvalidateRange(mh
.position
, mh
.position
+ mh
.length
);
4374 if (mh
.linesAdded
!= 0 && !CanDeferToLastStep(mh
)) {
4378 if ((mh
.modificationType
& SC_MOD_CHANGEMARKER
) || (mh
.modificationType
& SC_MOD_CHANGEMARGIN
)) {
4379 if ((paintState
== notPainting
) || !PaintContainsMargin()) {
4380 if (mh
.modificationType
& SC_MOD_CHANGEFOLD
) {
4381 // Fold changes can affect the drawing of following lines so redraw whole margin
4382 RedrawSelMargin(mh
.line
-1, true);
4384 RedrawSelMargin(mh
.line
);
4389 // NOW pay the piper WRT "deferred" visual updates
4390 if (IsLastStep(mh
)) {
4395 // If client wants to see this modification
4396 if (mh
.modificationType
& modEventMask
) {
4397 if ((mh
.modificationType
& (SC_MOD_CHANGESTYLE
| SC_MOD_CHANGEINDICATOR
)) == 0) {
4398 // Real modification made to text of document.
4399 NotifyChange(); // Send EN_CHANGE
4402 SCNotification scn
= {0};
4403 scn
.nmhdr
.code
= SCN_MODIFIED
;
4404 scn
.position
= mh
.position
;
4405 scn
.modificationType
= mh
.modificationType
;
4407 scn
.length
= mh
.length
;
4408 scn
.linesAdded
= mh
.linesAdded
;
4410 scn
.foldLevelNow
= mh
.foldLevelNow
;
4411 scn
.foldLevelPrev
= mh
.foldLevelPrev
;
4412 scn
.token
= mh
.token
;
4413 scn
.annotationLinesAdded
= mh
.annotationLinesAdded
;
4418 void Editor::NotifyDeleted(Document
*, void *) {
4422 void Editor::NotifyMacroRecord(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
4424 // Enumerates all macroable messages
4430 case SCI_REPLACESEL
:
4432 case SCI_INSERTTEXT
:
4433 case SCI_APPENDTEXT
:
4438 case SCI_SEARCHANCHOR
:
4439 case SCI_SEARCHNEXT
:
4440 case SCI_SEARCHPREV
:
4442 case SCI_LINEDOWNEXTEND
:
4444 case SCI_PARADOWNEXTEND
:
4446 case SCI_LINEUPEXTEND
:
4448 case SCI_PARAUPEXTEND
:
4450 case SCI_CHARLEFTEXTEND
:
4452 case SCI_CHARRIGHTEXTEND
:
4454 case SCI_WORDLEFTEXTEND
:
4456 case SCI_WORDRIGHTEXTEND
:
4457 case SCI_WORDPARTLEFT
:
4458 case SCI_WORDPARTLEFTEXTEND
:
4459 case SCI_WORDPARTRIGHT
:
4460 case SCI_WORDPARTRIGHTEXTEND
:
4461 case SCI_WORDLEFTEND
:
4462 case SCI_WORDLEFTENDEXTEND
:
4463 case SCI_WORDRIGHTEND
:
4464 case SCI_WORDRIGHTENDEXTEND
:
4466 case SCI_HOMEEXTEND
:
4468 case SCI_LINEENDEXTEND
:
4470 case SCI_HOMEWRAPEXTEND
:
4471 case SCI_LINEENDWRAP
:
4472 case SCI_LINEENDWRAPEXTEND
:
4473 case SCI_DOCUMENTSTART
:
4474 case SCI_DOCUMENTSTARTEXTEND
:
4475 case SCI_DOCUMENTEND
:
4476 case SCI_DOCUMENTENDEXTEND
:
4477 case SCI_STUTTEREDPAGEUP
:
4478 case SCI_STUTTEREDPAGEUPEXTEND
:
4479 case SCI_STUTTEREDPAGEDOWN
:
4480 case SCI_STUTTEREDPAGEDOWNEXTEND
:
4482 case SCI_PAGEUPEXTEND
:
4484 case SCI_PAGEDOWNEXTEND
:
4485 case SCI_EDITTOGGLEOVERTYPE
:
4487 case SCI_DELETEBACK
:
4492 case SCI_VCHOMEEXTEND
:
4493 case SCI_VCHOMEWRAP
:
4494 case SCI_VCHOMEWRAPEXTEND
:
4495 case SCI_DELWORDLEFT
:
4496 case SCI_DELWORDRIGHT
:
4497 case SCI_DELWORDRIGHTEND
:
4498 case SCI_DELLINELEFT
:
4499 case SCI_DELLINERIGHT
:
4502 case SCI_LINEDELETE
:
4503 case SCI_LINETRANSPOSE
:
4504 case SCI_LINEDUPLICATE
:
4507 case SCI_LINESCROLLDOWN
:
4508 case SCI_LINESCROLLUP
:
4509 case SCI_DELETEBACKNOTLINE
:
4510 case SCI_HOMEDISPLAY
:
4511 case SCI_HOMEDISPLAYEXTEND
:
4512 case SCI_LINEENDDISPLAY
:
4513 case SCI_LINEENDDISPLAYEXTEND
:
4514 case SCI_SETSELECTIONMODE
:
4515 case SCI_LINEDOWNRECTEXTEND
:
4516 case SCI_LINEUPRECTEXTEND
:
4517 case SCI_CHARLEFTRECTEXTEND
:
4518 case SCI_CHARRIGHTRECTEXTEND
:
4519 case SCI_HOMERECTEXTEND
:
4520 case SCI_VCHOMERECTEXTEND
:
4521 case SCI_LINEENDRECTEXTEND
:
4522 case SCI_PAGEUPRECTEXTEND
:
4523 case SCI_PAGEDOWNRECTEXTEND
:
4524 case SCI_SELECTIONDUPLICATE
:
4525 case SCI_COPYALLOWLINE
:
4528 // Filter out all others like display changes. Also, newlines are redundant
4529 // with char insert messages.
4532 // printf("Filtered out %ld of macro recording\n", iMessage);
4536 // Send notification
4537 SCNotification scn
= {0};
4538 scn
.nmhdr
.code
= SCN_MACRORECORD
;
4539 scn
.message
= iMessage
;
4540 scn
.wParam
= wParam
;
4541 scn
.lParam
= lParam
;
4546 * Force scroll and keep position relative to top of window.
4548 * If stuttered = true and not already at first/last row, move to first/last row of window.
4549 * If stuttered = true and already at first/last row, scroll as normal.
4551 void Editor::PageMove(int direction
, Selection::selTypes selt
, bool stuttered
) {
4552 int topLineNew
, newPos
;
4554 // I consider only the caretYSlop, and ignore the caretYPolicy-- is that a problem?
4555 int currentLine
= pdoc
->LineFromPosition(sel
.MainCaret());
4556 int topStutterLine
= topLine
+ caretYSlop
;
4557 int bottomStutterLine
=
4558 pdoc
->LineFromPosition(PositionFromLocation(
4559 Point(lastXChosen
- xOffset
, direction
* vs
.lineHeight
* LinesToScroll())))
4562 if (stuttered
&& (direction
< 0 && currentLine
> topStutterLine
)) {
4563 topLineNew
= topLine
;
4564 newPos
= PositionFromLocation(Point(lastXChosen
- xOffset
, vs
.lineHeight
* caretYSlop
));
4566 } else if (stuttered
&& (direction
> 0 && currentLine
< bottomStutterLine
)) {
4567 topLineNew
= topLine
;
4568 newPos
= PositionFromLocation(Point(lastXChosen
- xOffset
, vs
.lineHeight
* (LinesToScroll() - caretYSlop
)));
4571 Point pt
= LocationFromPosition(sel
.MainCaret());
4573 topLineNew
= Platform::Clamp(
4574 topLine
+ direction
* LinesToScroll(), 0, MaxScrollPos());
4575 newPos
= PositionFromLocation(
4576 Point(lastXChosen
- xOffset
, pt
.y
+ direction
* (vs
.lineHeight
* LinesToScroll())));
4579 if (topLineNew
!= topLine
) {
4580 SetTopLine(topLineNew
);
4581 MovePositionTo(SelectionPosition(newPos
), selt
);
4583 SetVerticalScrollPos();
4585 MovePositionTo(SelectionPosition(newPos
), selt
);
4589 void Editor::ChangeCaseOfSelection(int caseMapping
) {
4591 for (size_t r
=0; r
<sel
.Count(); r
++) {
4592 SelectionRange current
= sel
.Range(r
);
4593 SelectionRange currentNoVS
= current
;
4594 currentNoVS
.ClearVirtualSpace();
4595 char *text
= CopyRange(currentNoVS
.Start().Position(), currentNoVS
.End().Position());
4596 size_t rangeBytes
= currentNoVS
.Length();
4597 if (rangeBytes
> 0) {
4598 std::string
sText(text
, rangeBytes
);
4600 std::string sMapped
= CaseMapString(sText
, caseMapping
);
4602 if (sMapped
!= sText
) {
4603 size_t firstDifference
= 0;
4604 while (sMapped
[firstDifference
] == sText
[firstDifference
])
4606 size_t lastDifference
= sMapped
.size() - 1;
4607 while (sMapped
[lastDifference
] == sText
[lastDifference
])
4609 size_t endSame
= sMapped
.size() - 1 - lastDifference
;
4610 pdoc
->DeleteChars(currentNoVS
.Start().Position() + firstDifference
,
4611 rangeBytes
- firstDifference
- endSame
);
4612 pdoc
->InsertString(currentNoVS
.Start().Position() + firstDifference
,
4613 sMapped
.c_str() + firstDifference
, lastDifference
- firstDifference
+ 1);
4614 // Automatic movement changes selection so reset to exactly the same as it was.
4615 sel
.Range(r
) = current
;
4622 void Editor::LineTranspose() {
4623 int line
= pdoc
->LineFromPosition(sel
.MainCaret());
4626 int startPrev
= pdoc
->LineStart(line
- 1);
4627 int endPrev
= pdoc
->LineEnd(line
- 1);
4628 int start
= pdoc
->LineStart(line
);
4629 int end
= pdoc
->LineEnd(line
);
4630 char *line1
= CopyRange(startPrev
, endPrev
);
4631 int len1
= endPrev
- startPrev
;
4632 char *line2
= CopyRange(start
, end
);
4633 int len2
= end
- start
;
4634 pdoc
->DeleteChars(start
, len2
);
4635 pdoc
->DeleteChars(startPrev
, len1
);
4636 pdoc
->InsertString(startPrev
, line2
, len2
);
4637 pdoc
->InsertString(start
- len1
+ len2
, line1
, len1
);
4638 MovePositionTo(SelectionPosition(start
- len1
+ len2
));
4644 void Editor::Duplicate(bool forLine
) {
4648 UndoGroup
ug(pdoc
, sel
.Count() > 1);
4649 SelectionPosition last
;
4650 const char *eol
= "";
4653 eol
= StringFromEOLMode(pdoc
->eolMode
);
4654 eolLen
= istrlen(eol
);
4656 for (size_t r
=0; r
<sel
.Count(); r
++) {
4657 SelectionPosition start
= sel
.Range(r
).Start();
4658 SelectionPosition end
= sel
.Range(r
).End();
4660 int line
= pdoc
->LineFromPosition(sel
.Range(r
).caret
.Position());
4661 start
= SelectionPosition(pdoc
->LineStart(line
));
4662 end
= SelectionPosition(pdoc
->LineEnd(line
));
4664 char *text
= CopyRange(start
.Position(), end
.Position());
4666 pdoc
->InsertString(end
.Position(), eol
, eolLen
);
4667 pdoc
->InsertString(end
.Position() + eolLen
, text
, SelectionRange(end
, start
).Length());
4670 if (sel
.Count() && sel
.IsRectangular()) {
4671 SelectionPosition last
= sel
.Last();
4673 int line
= pdoc
->LineFromPosition(last
.Position());
4674 last
= SelectionPosition(last
.Position() + pdoc
->LineStart(line
+1) - pdoc
->LineStart(line
));
4676 if (sel
.Rectangular().anchor
> sel
.Rectangular().caret
)
4677 sel
.Rectangular().anchor
= last
;
4679 sel
.Rectangular().caret
= last
;
4680 SetRectangularRange();
4684 void Editor::CancelModes() {
4685 sel
.SetMoveExtends(false);
4688 void Editor::NewLine() {
4690 const char *eol
= "\n";
4691 if (pdoc
->eolMode
== SC_EOL_CRLF
) {
4693 } else if (pdoc
->eolMode
== SC_EOL_CR
) {
4695 } // else SC_EOL_LF -> "\n" already set
4696 if (pdoc
->InsertCString(sel
.MainCaret(), eol
)) {
4697 SetEmptySelection(sel
.MainCaret() + istrlen(eol
));
4700 if (recordingMacro
) {
4704 NotifyMacroRecord(SCI_REPLACESEL
, 0, reinterpret_cast<sptr_t
>(txt
));
4711 EnsureCaretVisible();
4712 // Avoid blinking during rapid typing:
4713 ShowCaretAtCurrentPosition();
4716 void Editor::CursorUpOrDown(int direction
, Selection::selTypes selt
) {
4717 SelectionPosition caretToUse
= sel
.Range(sel
.Main()).caret
;
4718 if (sel
.IsRectangular()) {
4719 if (selt
== Selection::noSel
) {
4720 caretToUse
= (direction
> 0) ? sel
.Limits().end
: sel
.Limits().start
;
4722 caretToUse
= sel
.Rectangular().caret
;
4725 Point pt
= LocationFromPosition(caretToUse
);
4726 int lineDoc
= pdoc
->LineFromPosition(caretToUse
.Position());
4727 Point ptStartLine
= LocationFromPosition(pdoc
->LineStart(lineDoc
));
4728 int subLine
= (pt
.y
- ptStartLine
.y
) / vs
.lineHeight
;
4729 int commentLines
= vs
.annotationVisible
? pdoc
->AnnotationLines(lineDoc
) : 0;
4730 SelectionPosition posNew
= SPositionFromLocation(
4731 Point(lastXChosen
- xOffset
, pt
.y
+ direction
* vs
.lineHeight
), false, false, UserVirtualSpace());
4732 if ((direction
> 0) && (subLine
>= (cs
.GetHeight(lineDoc
) - 1 - commentLines
))) {
4733 posNew
= SPositionFromLocation(
4734 Point(lastXChosen
- xOffset
, pt
.y
+ (commentLines
+ 1) * vs
.lineHeight
), false, false, UserVirtualSpace());
4736 if (direction
< 0) {
4737 // Line wrapping may lead to a location on the same line, so
4738 // seek back if that is the case.
4739 // There is an equivalent case when moving down which skips
4740 // over a line but as that does not trap the user it is fine.
4741 Point ptNew
= LocationFromPosition(posNew
.Position());
4742 while ((posNew
.Position() > 0) && (pt
.y
== ptNew
.y
)) {
4744 posNew
.SetVirtualSpace(0);
4745 ptNew
= LocationFromPosition(posNew
.Position());
4748 MovePositionTo(posNew
, selt
);
4751 void Editor::ParaUpOrDown(int direction
, Selection::selTypes selt
) {
4752 int lineDoc
, savedPos
= sel
.MainCaret();
4754 MovePositionTo(SelectionPosition(direction
> 0 ? pdoc
->ParaDown(sel
.MainCaret()) : pdoc
->ParaUp(sel
.MainCaret())), selt
);
4755 lineDoc
= pdoc
->LineFromPosition(sel
.MainCaret());
4756 if (direction
> 0) {
4757 if (sel
.MainCaret() >= pdoc
->Length() && !cs
.GetVisible(lineDoc
)) {
4758 if (selt
== Selection::noSel
) {
4759 MovePositionTo(SelectionPosition(pdoc
->LineEndPosition(savedPos
)));
4764 } while (!cs
.GetVisible(lineDoc
));
4767 int Editor::StartEndDisplayLine(int pos
, bool start
) {
4769 int line
= pdoc
->LineFromPosition(pos
);
4770 AutoSurface
surface(this);
4771 AutoLineLayout
ll(llc
, RetrieveLineLayout(line
));
4772 int posRet
= INVALID_POSITION
;
4773 if (surface
&& ll
) {
4774 unsigned int posLineStart
= pdoc
->LineStart(line
);
4775 LayoutLine(line
, surface
, vs
, ll
, wrapWidth
);
4776 int posInLine
= pos
- posLineStart
;
4777 if (posInLine
<= ll
->maxLineLength
) {
4778 for (int subLine
= 0; subLine
< ll
->lines
; subLine
++) {
4779 if ((posInLine
>= ll
->LineStart(subLine
)) && (posInLine
<= ll
->LineStart(subLine
+ 1))) {
4781 posRet
= ll
->LineStart(subLine
) + posLineStart
;
4783 if (subLine
== ll
->lines
- 1)
4784 posRet
= ll
->LineStart(subLine
+ 1) + posLineStart
;
4786 posRet
= ll
->LineStart(subLine
+ 1) + posLineStart
- 1;
4792 if (posRet
== INVALID_POSITION
) {
4799 int Editor::KeyCommand(unsigned int iMessage
) {
4804 case SCI_LINEDOWNEXTEND
:
4805 CursorUpOrDown(1, Selection::selStream
);
4807 case SCI_LINEDOWNRECTEXTEND
:
4808 CursorUpOrDown(1, Selection::selRectangle
);
4813 case SCI_PARADOWNEXTEND
:
4814 ParaUpOrDown(1, Selection::selStream
);
4816 case SCI_LINESCROLLDOWN
:
4817 ScrollTo(topLine
+ 1);
4818 MoveCaretInsideView(false);
4823 case SCI_LINEUPEXTEND
:
4824 CursorUpOrDown(-1, Selection::selStream
);
4826 case SCI_LINEUPRECTEXTEND
:
4827 CursorUpOrDown(-1, Selection::selRectangle
);
4832 case SCI_PARAUPEXTEND
:
4833 ParaUpOrDown(-1, Selection::selStream
);
4835 case SCI_LINESCROLLUP
:
4836 ScrollTo(topLine
- 1);
4837 MoveCaretInsideView(false);
4840 if (SelectionEmpty() || sel
.MoveExtends()) {
4841 if ((sel
.Count() == 1) && pdoc
->IsLineEndPosition(sel
.MainCaret()) && sel
.RangeMain().caret
.VirtualSpace()) {
4842 SelectionPosition spCaret
= sel
.RangeMain().caret
;
4843 spCaret
.SetVirtualSpace(spCaret
.VirtualSpace() - 1);
4844 MovePositionTo(spCaret
);
4846 MovePositionTo(MovePositionSoVisible(
4847 SelectionPosition((sel
.LimitsForRectangularElseMain().start
).Position() - 1), -1));
4850 MovePositionTo(sel
.LimitsForRectangularElseMain().start
);
4854 case SCI_CHARLEFTEXTEND
:
4855 if (pdoc
->IsLineEndPosition(sel
.MainCaret()) && sel
.RangeMain().caret
.VirtualSpace()) {
4856 SelectionPosition spCaret
= sel
.RangeMain().caret
;
4857 spCaret
.SetVirtualSpace(spCaret
.VirtualSpace() - 1);
4858 MovePositionTo(spCaret
, Selection::selStream
);
4860 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel
.MainCaret() - 1), -1), Selection::selStream
);
4864 case SCI_CHARLEFTRECTEXTEND
:
4865 if (pdoc
->IsLineEndPosition(sel
.MainCaret()) && sel
.RangeMain().caret
.VirtualSpace()) {
4866 SelectionPosition spCaret
= sel
.RangeMain().caret
;
4867 spCaret
.SetVirtualSpace(spCaret
.VirtualSpace() - 1);
4868 MovePositionTo(spCaret
, Selection::selRectangle
);
4870 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel
.MainCaret() - 1), -1), Selection::selRectangle
);
4875 if (SelectionEmpty() || sel
.MoveExtends()) {
4876 if ((virtualSpaceOptions
& SCVS_USERACCESSIBLE
) && pdoc
->IsLineEndPosition(sel
.MainCaret())) {
4877 SelectionPosition spCaret
= sel
.RangeMain().caret
;
4878 spCaret
.SetVirtualSpace(spCaret
.VirtualSpace() + 1);
4879 MovePositionTo(spCaret
);
4881 MovePositionTo(MovePositionSoVisible(
4882 SelectionPosition((sel
.LimitsForRectangularElseMain().end
).Position() + 1), 1));
4885 MovePositionTo(sel
.LimitsForRectangularElseMain().end
);
4889 case SCI_CHARRIGHTEXTEND
:
4890 if ((virtualSpaceOptions
& SCVS_USERACCESSIBLE
) && pdoc
->IsLineEndPosition(sel
.MainCaret())) {
4891 SelectionPosition spCaret
= sel
.RangeMain().caret
;
4892 spCaret
.SetVirtualSpace(spCaret
.VirtualSpace() + 1);
4893 MovePositionTo(spCaret
, Selection::selStream
);
4895 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel
.MainCaret() + 1), 1), Selection::selStream
);
4899 case SCI_CHARRIGHTRECTEXTEND
:
4900 if ((virtualSpaceOptions
& SCVS_RECTANGULARSELECTION
) && pdoc
->IsLineEndPosition(sel
.MainCaret())) {
4901 SelectionPosition spCaret
= sel
.RangeMain().caret
;
4902 spCaret
.SetVirtualSpace(spCaret
.VirtualSpace() + 1);
4903 MovePositionTo(spCaret
, Selection::selRectangle
);
4905 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel
.MainCaret() + 1), 1), Selection::selRectangle
);
4910 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordStart(sel
.MainCaret(), -1), -1));
4913 case SCI_WORDLEFTEXTEND
:
4914 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordStart(sel
.MainCaret(), -1), -1), Selection::selStream
);
4918 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordStart(sel
.MainCaret(), 1), 1));
4921 case SCI_WORDRIGHTEXTEND
:
4922 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordStart(sel
.MainCaret(), 1), 1), Selection::selStream
);
4926 case SCI_WORDLEFTEND
:
4927 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordEnd(sel
.MainCaret(), -1), -1));
4930 case SCI_WORDLEFTENDEXTEND
:
4931 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordEnd(sel
.MainCaret(), -1), -1), Selection::selStream
);
4934 case SCI_WORDRIGHTEND
:
4935 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordEnd(sel
.MainCaret(), 1), 1));
4938 case SCI_WORDRIGHTENDEXTEND
:
4939 MovePositionTo(MovePositionSoVisible(pdoc
->NextWordEnd(sel
.MainCaret(), 1), 1), Selection::selStream
);
4944 MovePositionTo(pdoc
->LineStart(pdoc
->LineFromPosition(sel
.MainCaret())));
4947 case SCI_HOMEEXTEND
:
4948 MovePositionTo(pdoc
->LineStart(pdoc
->LineFromPosition(sel
.MainCaret())), Selection::selStream
);
4951 case SCI_HOMERECTEXTEND
:
4952 MovePositionTo(pdoc
->LineStart(pdoc
->LineFromPosition(sel
.MainCaret())), Selection::selRectangle
);
4956 MovePositionTo(pdoc
->LineEndPosition(sel
.MainCaret()));
4959 case SCI_LINEENDEXTEND
:
4960 MovePositionTo(pdoc
->LineEndPosition(sel
.MainCaret()), Selection::selStream
);
4963 case SCI_LINEENDRECTEXTEND
:
4964 MovePositionTo(pdoc
->LineEndPosition(sel
.MainCaret()), Selection::selRectangle
);
4967 case SCI_HOMEWRAP
: {
4968 SelectionPosition homePos
= MovePositionSoVisible(StartEndDisplayLine(sel
.MainCaret(), true), -1);
4969 if (sel
.RangeMain().caret
<= homePos
)
4970 homePos
= SelectionPosition(pdoc
->LineStart(pdoc
->LineFromPosition(sel
.MainCaret())));
4971 MovePositionTo(homePos
);
4975 case SCI_HOMEWRAPEXTEND
: {
4976 SelectionPosition homePos
= MovePositionSoVisible(StartEndDisplayLine(sel
.MainCaret(), true), -1);
4977 if (sel
.RangeMain().caret
<= homePos
)
4978 homePos
= SelectionPosition(pdoc
->LineStart(pdoc
->LineFromPosition(sel
.MainCaret())));
4979 MovePositionTo(homePos
, Selection::selStream
);
4983 case SCI_LINEENDWRAP
: {
4984 SelectionPosition endPos
= MovePositionSoVisible(StartEndDisplayLine(sel
.MainCaret(), false), 1);
4985 SelectionPosition realEndPos
= SelectionPosition(pdoc
->LineEndPosition(sel
.MainCaret()));
4986 if (endPos
> realEndPos
// if moved past visible EOLs
4987 || sel
.RangeMain().caret
>= endPos
) // if at end of display line already
4988 endPos
= realEndPos
;
4989 MovePositionTo(endPos
);
4993 case SCI_LINEENDWRAPEXTEND
: {
4994 SelectionPosition endPos
= MovePositionSoVisible(StartEndDisplayLine(sel
.MainCaret(), false), 1);
4995 SelectionPosition realEndPos
= SelectionPosition(pdoc
->LineEndPosition(sel
.MainCaret()));
4996 if (endPos
> realEndPos
// if moved past visible EOLs
4997 || sel
.RangeMain().caret
>= endPos
) // if at end of display line already
4998 endPos
= realEndPos
;
4999 MovePositionTo(endPos
, Selection::selStream
);
5003 case SCI_DOCUMENTSTART
:
5007 case SCI_DOCUMENTSTARTEXTEND
:
5008 MovePositionTo(0, Selection::selStream
);
5011 case SCI_DOCUMENTEND
:
5012 MovePositionTo(pdoc
->Length());
5015 case SCI_DOCUMENTENDEXTEND
:
5016 MovePositionTo(pdoc
->Length(), Selection::selStream
);
5019 case SCI_STUTTEREDPAGEUP
:
5020 PageMove(-1, Selection::noSel
, true);
5022 case SCI_STUTTEREDPAGEUPEXTEND
:
5023 PageMove(-1, Selection::selStream
, true);
5025 case SCI_STUTTEREDPAGEDOWN
:
5026 PageMove(1, Selection::noSel
, true);
5028 case SCI_STUTTEREDPAGEDOWNEXTEND
:
5029 PageMove(1, Selection::selStream
, true);
5034 case SCI_PAGEUPEXTEND
:
5035 PageMove(-1, Selection::selStream
);
5037 case SCI_PAGEUPRECTEXTEND
:
5038 PageMove(-1, Selection::selRectangle
);
5043 case SCI_PAGEDOWNEXTEND
:
5044 PageMove(1, Selection::selStream
);
5046 case SCI_PAGEDOWNRECTEXTEND
:
5047 PageMove(1, Selection::selRectangle
);
5049 case SCI_EDITTOGGLEOVERTYPE
:
5050 inOverstrike
= !inOverstrike
;
5052 ShowCaretAtCurrentPosition();
5055 case SCI_CANCEL
: // Cancel any modes - handled in subclass
5056 // Also unselect text
5059 case SCI_DELETEBACK
:
5064 EnsureCaretVisible();
5066 case SCI_DELETEBACKNOTLINE
:
5071 EnsureCaretVisible();
5078 EnsureCaretVisible();
5079 ShowCaretAtCurrentPosition(); // Avoid blinking
5086 EnsureCaretVisible();
5087 ShowCaretAtCurrentPosition(); // Avoid blinking
5096 MovePositionTo(pdoc
->VCHomePosition(sel
.MainCaret()));
5099 case SCI_VCHOMEEXTEND
:
5100 MovePositionTo(pdoc
->VCHomePosition(sel
.MainCaret()), Selection::selStream
);
5103 case SCI_VCHOMERECTEXTEND
:
5104 MovePositionTo(pdoc
->VCHomePosition(sel
.MainCaret()), Selection::selRectangle
);
5107 case SCI_VCHOMEWRAP
: {
5108 SelectionPosition homePos
= SelectionPosition(pdoc
->VCHomePosition(sel
.MainCaret()));
5109 SelectionPosition viewLineStart
= MovePositionSoVisible(StartEndDisplayLine(sel
.MainCaret(), true), -1);
5110 if ((viewLineStart
< sel
.RangeMain().caret
) && (viewLineStart
> homePos
))
5111 homePos
= viewLineStart
;
5113 MovePositionTo(homePos
);
5117 case SCI_VCHOMEWRAPEXTEND
: {
5118 SelectionPosition homePos
= SelectionPosition(pdoc
->VCHomePosition(sel
.MainCaret()));
5119 SelectionPosition viewLineStart
= MovePositionSoVisible(StartEndDisplayLine(sel
.MainCaret(), true), -1);
5120 if ((viewLineStart
< sel
.RangeMain().caret
) && (viewLineStart
> homePos
))
5121 homePos
= viewLineStart
;
5123 MovePositionTo(homePos
, Selection::selStream
);
5128 if (vs
.zoomLevel
< 20) {
5130 InvalidateStyleRedraw();
5135 if (vs
.zoomLevel
> -10) {
5137 InvalidateStyleRedraw();
5141 case SCI_DELWORDLEFT
: {
5142 int startWord
= pdoc
->NextWordStart(sel
.MainCaret(), -1);
5143 pdoc
->DeleteChars(startWord
, sel
.MainCaret() - startWord
);
5144 sel
.RangeMain().ClearVirtualSpace();
5148 case SCI_DELWORDRIGHT
: {
5150 sel
.RangeMain().caret
= SelectionPosition(
5151 InsertSpace(sel
.RangeMain().caret
.Position(), sel
.RangeMain().caret
.VirtualSpace()));
5152 int endWord
= pdoc
->NextWordStart(sel
.MainCaret(), 1);
5153 pdoc
->DeleteChars(sel
.MainCaret(), endWord
- sel
.MainCaret());
5156 case SCI_DELWORDRIGHTEND
: {
5158 sel
.RangeMain().caret
= SelectionPosition(
5159 InsertSpace(sel
.RangeMain().caret
.Position(), sel
.RangeMain().caret
.VirtualSpace()));
5160 int endWord
= pdoc
->NextWordEnd(sel
.MainCaret(), 1);
5161 pdoc
->DeleteChars(sel
.MainCaret(), endWord
- sel
.MainCaret());
5164 case SCI_DELLINELEFT
: {
5165 int line
= pdoc
->LineFromPosition(sel
.MainCaret());
5166 int start
= pdoc
->LineStart(line
);
5167 pdoc
->DeleteChars(start
, sel
.MainCaret() - start
);
5168 sel
.RangeMain().ClearVirtualSpace();
5172 case SCI_DELLINERIGHT
: {
5173 int line
= pdoc
->LineFromPosition(sel
.MainCaret());
5174 int end
= pdoc
->LineEnd(line
);
5175 pdoc
->DeleteChars(sel
.MainCaret(), end
- sel
.MainCaret());
5178 case SCI_LINECOPY
: {
5179 int lineStart
= pdoc
->LineFromPosition(SelectionStart().Position());
5180 int lineEnd
= pdoc
->LineFromPosition(SelectionEnd().Position());
5181 CopyRangeToClipboard(pdoc
->LineStart(lineStart
),
5182 pdoc
->LineStart(lineEnd
+ 1));
5186 int lineStart
= pdoc
->LineFromPosition(SelectionStart().Position());
5187 int lineEnd
= pdoc
->LineFromPosition(SelectionEnd().Position());
5188 int start
= pdoc
->LineStart(lineStart
);
5189 int end
= pdoc
->LineStart(lineEnd
+ 1);
5190 SetSelection(start
, end
);
5195 case SCI_LINEDELETE
: {
5196 int line
= pdoc
->LineFromPosition(sel
.MainCaret());
5197 int start
= pdoc
->LineStart(line
);
5198 int end
= pdoc
->LineStart(line
+ 1);
5199 pdoc
->DeleteChars(start
, end
- start
);
5202 case SCI_LINETRANSPOSE
:
5205 case SCI_LINEDUPLICATE
:
5208 case SCI_SELECTIONDUPLICATE
:
5212 ChangeCaseOfSelection(cmLower
);
5215 ChangeCaseOfSelection(cmUpper
);
5217 case SCI_WORDPARTLEFT
:
5218 MovePositionTo(MovePositionSoVisible(pdoc
->WordPartLeft(sel
.MainCaret()), -1));
5221 case SCI_WORDPARTLEFTEXTEND
:
5222 MovePositionTo(MovePositionSoVisible(pdoc
->WordPartLeft(sel
.MainCaret()), -1), Selection::selStream
);
5225 case SCI_WORDPARTRIGHT
:
5226 MovePositionTo(MovePositionSoVisible(pdoc
->WordPartRight(sel
.MainCaret()), 1));
5229 case SCI_WORDPARTRIGHTEXTEND
:
5230 MovePositionTo(MovePositionSoVisible(pdoc
->WordPartRight(sel
.MainCaret()), 1), Selection::selStream
);
5233 case SCI_HOMEDISPLAY
:
5234 MovePositionTo(MovePositionSoVisible(
5235 StartEndDisplayLine(sel
.MainCaret(), true), -1));
5238 case SCI_HOMEDISPLAYEXTEND
:
5239 MovePositionTo(MovePositionSoVisible(
5240 StartEndDisplayLine(sel
.MainCaret(), true), -1), Selection::selStream
);
5243 case SCI_LINEENDDISPLAY
:
5244 MovePositionTo(MovePositionSoVisible(
5245 StartEndDisplayLine(sel
.MainCaret(), false), 1));
5248 case SCI_LINEENDDISPLAYEXTEND
:
5249 MovePositionTo(MovePositionSoVisible(
5250 StartEndDisplayLine(sel
.MainCaret(), false), 1), Selection::selStream
);
5257 int Editor::KeyDefault(int, int) {
5261 int Editor::KeyDown(int key
, bool shift
, bool ctrl
, bool alt
, bool *consumed
) {
5263 int modifiers
= (shift
? SCI_SHIFT
: 0) | (ctrl
? SCI_CTRL
: 0) |
5264 (alt
? SCI_ALT
: 0);
5265 int msg
= kmap
.Find(key
, modifiers
);
5269 return WndProc(msg
, 0, 0);
5273 return KeyDefault(key
, modifiers
);
5277 void Editor::SetWhitespaceVisible(int view
) {
5278 vs
.viewWhitespace
= static_cast<WhiteSpaceVisibility
>(view
);
5281 int Editor::GetWhitespaceVisible() {
5282 return vs
.viewWhitespace
;
5285 void Editor::Indent(bool forwards
) {
5286 for (size_t r
=0; r
<sel
.Count(); r
++) {
5287 int lineOfAnchor
= pdoc
->LineFromPosition(sel
.Range(r
).anchor
.Position());
5288 int caretPosition
= sel
.Range(r
).caret
.Position();
5289 int lineCurrentPos
= pdoc
->LineFromPosition(caretPosition
);
5290 if (lineOfAnchor
== lineCurrentPos
) {
5293 pdoc
->DeleteChars(sel
.Range(r
).Start().Position(), sel
.Range(r
).Length());
5294 caretPosition
= sel
.Range(r
).caret
.Position();
5295 if (pdoc
->GetColumn(caretPosition
) <= pdoc
->GetColumn(pdoc
->GetLineIndentPosition(lineCurrentPos
)) &&
5297 int indentation
= pdoc
->GetLineIndentation(lineCurrentPos
);
5298 int indentationStep
= pdoc
->IndentSize();
5299 pdoc
->SetLineIndentation(lineCurrentPos
, indentation
+ indentationStep
- indentation
% indentationStep
);
5300 sel
.Range(r
) = SelectionRange(pdoc
->GetLineIndentPosition(lineCurrentPos
));
5302 if (pdoc
->useTabs
) {
5303 pdoc
->InsertChar(caretPosition
, '\t');
5304 sel
.Range(r
) = SelectionRange(caretPosition
+1);
5306 int numSpaces
= (pdoc
->tabInChars
) -
5307 (pdoc
->GetColumn(caretPosition
) % (pdoc
->tabInChars
));
5309 numSpaces
= pdoc
->tabInChars
;
5310 for (int i
= 0; i
< numSpaces
; i
++) {
5311 pdoc
->InsertChar(caretPosition
+ i
, ' ');
5313 sel
.Range(r
) = SelectionRange(caretPosition
+numSpaces
);
5317 if (pdoc
->GetColumn(caretPosition
) <= pdoc
->GetLineIndentation(lineCurrentPos
) &&
5320 int indentation
= pdoc
->GetLineIndentation(lineCurrentPos
);
5321 int indentationStep
= pdoc
->IndentSize();
5322 pdoc
->SetLineIndentation(lineCurrentPos
, indentation
- indentationStep
);
5323 sel
.Range(r
) = SelectionRange(pdoc
->GetLineIndentPosition(lineCurrentPos
));
5325 int newColumn
= ((pdoc
->GetColumn(caretPosition
) - 1) / pdoc
->tabInChars
) *
5329 int newPos
= caretPosition
;
5330 while (pdoc
->GetColumn(newPos
) > newColumn
)
5332 sel
.Range(r
) = SelectionRange(newPos
);
5335 } else { // Multiline
5336 int anchorPosOnLine
= sel
.Range(r
).anchor
.Position() - pdoc
->LineStart(lineOfAnchor
);
5337 int currentPosPosOnLine
= caretPosition
- pdoc
->LineStart(lineCurrentPos
);
5338 // Multiple lines selected so indent / dedent
5339 int lineTopSel
= Platform::Minimum(lineOfAnchor
, lineCurrentPos
);
5340 int lineBottomSel
= Platform::Maximum(lineOfAnchor
, lineCurrentPos
);
5341 if (pdoc
->LineStart(lineBottomSel
) == sel
.Range(r
).anchor
.Position() || pdoc
->LineStart(lineBottomSel
) == caretPosition
)
5342 lineBottomSel
--; // If not selecting any characters on a line, do not indent
5345 pdoc
->Indent(forwards
, lineBottomSel
, lineTopSel
);
5347 if (lineOfAnchor
< lineCurrentPos
) {
5348 if (currentPosPosOnLine
== 0)
5349 sel
.Range(r
) = SelectionRange(pdoc
->LineStart(lineCurrentPos
), pdoc
->LineStart(lineOfAnchor
));
5351 sel
.Range(r
) = SelectionRange(pdoc
->LineStart(lineCurrentPos
+ 1), pdoc
->LineStart(lineOfAnchor
));
5353 if (anchorPosOnLine
== 0)
5354 sel
.Range(r
) = SelectionRange(pdoc
->LineStart(lineCurrentPos
), pdoc
->LineStart(lineOfAnchor
));
5356 sel
.Range(r
) = SelectionRange(pdoc
->LineStart(lineCurrentPos
), pdoc
->LineStart(lineOfAnchor
+ 1));
5362 class CaseFolderASCII
: public CaseFolderTable
{
5367 ~CaseFolderASCII() {
5369 virtual size_t Fold(char *folded
, size_t sizeFolded
, const char *mixed
, size_t lenMixed
) {
5370 if (lenMixed
> sizeFolded
) {
5373 for (size_t i
=0; i
<lenMixed
; i
++) {
5374 folded
[i
] = mapping
[static_cast<unsigned char>(mixed
[i
])];
5382 CaseFolder
*Editor::CaseFolderForEncoding() {
5383 // Simple default that only maps ASCII upper case to lower case.
5384 return new CaseFolderASCII();
5388 * Search of a text in the document, in the given range.
5389 * @return The position of the found text, -1 if not found.
5391 long Editor::FindText(
5392 uptr_t wParam
, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5393 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5394 sptr_t lParam
) { ///< @c TextToFind structure: The text to search for in the given range.
5396 Sci_TextToFind
*ft
= reinterpret_cast<Sci_TextToFind
*>(lParam
);
5397 int lengthFound
= istrlen(ft
->lpstrText
);
5398 std::auto_ptr
<CaseFolder
> pcf(CaseFolderForEncoding());
5399 int pos
= pdoc
->FindText(ft
->chrg
.cpMin
, ft
->chrg
.cpMax
, ft
->lpstrText
,
5400 (wParam
& SCFIND_MATCHCASE
) != 0,
5401 (wParam
& SCFIND_WHOLEWORD
) != 0,
5402 (wParam
& SCFIND_WORDSTART
) != 0,
5403 (wParam
& SCFIND_REGEXP
) != 0,
5408 ft
->chrgText
.cpMin
= pos
;
5409 ft
->chrgText
.cpMax
= pos
+ lengthFound
;
5415 * Relocatable search support : Searches relative to current selection
5416 * point and sets the selection to the found text range with
5420 * Anchor following searches at current selection start: This allows
5421 * multiple incremental interactive searches to be macro recorded
5422 * while still setting the selection to found text so the find/select
5423 * operation is self-contained.
5425 void Editor::SearchAnchor() {
5426 searchAnchor
= SelectionStart().Position();
5430 * Find text from current search anchor: Must call @c SearchAnchor first.
5431 * Used for next text and previous text requests.
5432 * @return The position of the found text, -1 if not found.
5434 long Editor::SearchText(
5435 unsigned int iMessage
, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
5436 uptr_t wParam
, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5437 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5438 sptr_t lParam
) { ///< The text to search for.
5440 const char *txt
= reinterpret_cast<char *>(lParam
);
5442 int lengthFound
= istrlen(txt
);
5443 std::auto_ptr
<CaseFolder
> pcf(CaseFolderForEncoding());
5444 if (iMessage
== SCI_SEARCHNEXT
) {
5445 pos
= pdoc
->FindText(searchAnchor
, pdoc
->Length(), txt
,
5446 (wParam
& SCFIND_MATCHCASE
) != 0,
5447 (wParam
& SCFIND_WHOLEWORD
) != 0,
5448 (wParam
& SCFIND_WORDSTART
) != 0,
5449 (wParam
& SCFIND_REGEXP
) != 0,
5454 pos
= pdoc
->FindText(searchAnchor
, 0, txt
,
5455 (wParam
& SCFIND_MATCHCASE
) != 0,
5456 (wParam
& SCFIND_WHOLEWORD
) != 0,
5457 (wParam
& SCFIND_WORDSTART
) != 0,
5458 (wParam
& SCFIND_REGEXP
) != 0,
5464 SetSelection(pos
, pos
+ lengthFound
);
5470 std::string
Editor::CaseMapString(const std::string
&s
, int caseMapping
) {
5472 for (size_t i
=0; i
<ret
.size(); i
++) {
5473 switch (caseMapping
) {
5475 if (ret
[i
] >= 'a' && ret
[i
] <= 'z')
5476 ret
[i
] = static_cast<char>(ret
[i
] - 'a' + 'A');
5479 if (ret
[i
] >= 'A' && ret
[i
] <= 'Z')
5480 ret
[i
] = static_cast<char>(ret
[i
] - 'A' + 'a');
5488 * Search for text in the target range of the document.
5489 * @return The position of the found text, -1 if not found.
5491 long Editor::SearchInTarget(const char *text
, int length
) {
5492 int lengthFound
= length
;
5494 std::auto_ptr
<CaseFolder
> pcf(CaseFolderForEncoding());
5495 int pos
= pdoc
->FindText(targetStart
, targetEnd
, text
,
5496 (searchFlags
& SCFIND_MATCHCASE
) != 0,
5497 (searchFlags
& SCFIND_WHOLEWORD
) != 0,
5498 (searchFlags
& SCFIND_WORDSTART
) != 0,
5499 (searchFlags
& SCFIND_REGEXP
) != 0,
5505 targetEnd
= pos
+ lengthFound
;
5510 void Editor::GoToLine(int lineNo
) {
5511 if (lineNo
> pdoc
->LinesTotal())
5512 lineNo
= pdoc
->LinesTotal();
5515 SetEmptySelection(pdoc
->LineStart(lineNo
));
5516 ShowCaretAtCurrentPosition();
5517 EnsureCaretVisible();
5520 static bool Close(Point pt1
, Point pt2
) {
5521 if (abs(pt1
.x
- pt2
.x
) > 3)
5523 if (abs(pt1
.y
- pt2
.y
) > 3)
5528 char *Editor::CopyRange(int start
, int end
) {
5531 int len
= end
- start
;
5532 text
= new char[len
+ 1];
5533 for (int i
= 0; i
< len
; i
++) {
5534 text
[i
] = pdoc
->CharAt(start
+ i
);
5541 void Editor::CopySelectionRange(SelectionText
*ss
, bool allowLineCopy
) {
5543 if (allowLineCopy
) {
5544 int currentLine
= pdoc
->LineFromPosition(sel
.MainCaret());
5545 int start
= pdoc
->LineStart(currentLine
);
5546 int end
= pdoc
->LineEnd(currentLine
);
5548 char *text
= CopyRange(start
, end
);
5549 int textLen
= text
? strlen(text
) : 0;
5550 // include room for \r\n\0
5552 char *textWithEndl
= new char[textLen
];
5553 textWithEndl
[0] = '\0';
5555 strncat(textWithEndl
, text
, textLen
);
5556 if (pdoc
->eolMode
!= SC_EOL_LF
)
5557 strncat(textWithEndl
, "\r", textLen
);
5558 if (pdoc
->eolMode
!= SC_EOL_CR
)
5559 strncat(textWithEndl
, "\n", textLen
);
5560 ss
->Set(textWithEndl
, strlen(textWithEndl
) + 1,
5561 pdoc
->dbcsCodePage
, vs
.styles
[STYLE_DEFAULT
].characterSet
, false, true);
5565 int delimiterLength
= 0;
5566 if (sel
.selType
== Selection::selRectangle
) {
5567 if (pdoc
->eolMode
== SC_EOL_CRLF
) {
5568 delimiterLength
= 2;
5570 delimiterLength
= 1;
5573 int size
= sel
.Length() + delimiterLength
* sel
.Count();
5574 char *text
= new char[size
+ 1];
5576 std::vector
<SelectionRange
> rangesInOrder
= sel
.RangesCopy();
5577 if (sel
.selType
== Selection::selRectangle
)
5578 std::sort(rangesInOrder
.begin(), rangesInOrder
.end());
5579 for (size_t r
=0; r
<rangesInOrder
.size(); r
++) {
5580 SelectionRange current
= rangesInOrder
[r
];
5581 for (int i
= current
.Start().Position();
5582 i
< current
.End().Position();
5584 text
[j
++] = pdoc
->CharAt(i
);
5586 if (sel
.selType
== Selection::selRectangle
) {
5587 if (pdoc
->eolMode
!= SC_EOL_LF
) {
5590 if (pdoc
->eolMode
!= SC_EOL_CR
) {
5596 ss
->Set(text
, size
+ 1, pdoc
->dbcsCodePage
,
5597 vs
.styles
[STYLE_DEFAULT
].characterSet
, sel
.IsRectangular(), sel
.selType
== Selection::selLines
);
5601 void Editor::CopyRangeToClipboard(int start
, int end
) {
5602 start
= pdoc
->ClampPositionIntoDocument(start
);
5603 end
= pdoc
->ClampPositionIntoDocument(end
);
5604 SelectionText selectedText
;
5605 selectedText
.Set(CopyRange(start
, end
), end
- start
+ 1,
5606 pdoc
->dbcsCodePage
, vs
.styles
[STYLE_DEFAULT
].characterSet
, false, false);
5607 CopyToClipboard(selectedText
);
5610 void Editor::CopyText(int length
, const char *text
) {
5611 SelectionText selectedText
;
5612 selectedText
.Copy(text
, length
+ 1,
5613 pdoc
->dbcsCodePage
, vs
.styles
[STYLE_DEFAULT
].characterSet
, false, false);
5614 CopyToClipboard(selectedText
);
5617 void Editor::SetDragPosition(SelectionPosition newPos
) {
5618 if (newPos
.Position() >= 0) {
5619 newPos
= MovePositionOutsideChar(newPos
, 1);
5622 if (!(posDrag
== newPos
)) {
5631 void Editor::DisplayCursor(Window::Cursor c
) {
5632 if (cursorMode
== SC_CURSORNORMAL
)
5635 wMain
.SetCursor(static_cast<Window::Cursor
>(cursorMode
));
5638 bool Editor::DragThreshold(Point ptStart
, Point ptNow
) {
5639 int xMove
= ptStart
.x
- ptNow
.x
;
5640 int yMove
= ptStart
.y
- ptNow
.y
;
5641 int distanceSquared
= xMove
* xMove
+ yMove
* yMove
;
5642 return distanceSquared
> 16;
5645 void Editor::StartDrag() {
5646 // Always handled by subclasses
5647 //SetMouseCapture(true);
5648 //DisplayCursor(Window::cursorArrow);
5651 void Editor::DropAt(SelectionPosition position
, const char *value
, bool moving
, bool rectangular
) {
5652 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
5653 if (inDragDrop
== ddDragging
)
5654 dropWentOutside
= false;
5656 bool positionWasInSelection
= PositionInSelection(position
.Position());
5658 bool positionOnEdgeOfSelection
=
5659 (position
== SelectionStart()) || (position
== SelectionEnd());
5661 if ((inDragDrop
!= ddDragging
) || !(positionWasInSelection
) ||
5662 (positionOnEdgeOfSelection
&& !moving
)) {
5664 SelectionPosition selStart
= SelectionStart();
5665 SelectionPosition selEnd
= SelectionEnd();
5669 SelectionPosition positionAfterDeletion
= position
;
5670 if ((inDragDrop
== ddDragging
) && moving
) {
5671 // Remove dragged out text
5672 if (rectangular
|| sel
.selType
== Selection::selLines
) {
5673 for (size_t r
=0; r
<sel
.Count(); r
++) {
5674 if (position
>= sel
.Range(r
).Start()) {
5675 if (position
> sel
.Range(r
).End()) {
5676 positionAfterDeletion
.Add(-sel
.Range(r
).Length());
5678 positionAfterDeletion
.Add(-SelectionRange(position
, sel
.Range(r
).Start()).Length());
5683 if (position
> selStart
) {
5684 positionAfterDeletion
.Add(-SelectionRange(selEnd
, selStart
).Length());
5689 position
= positionAfterDeletion
;
5692 PasteRectangular(position
, value
, istrlen(value
));
5693 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
5694 SetEmptySelection(position
);
5696 position
= MovePositionOutsideChar(position
, sel
.MainCaret() - position
.Position());
5697 position
= SelectionPosition(InsertSpace(position
.Position(), position
.VirtualSpace()));
5698 if (pdoc
->InsertCString(position
.Position(), value
)) {
5699 SelectionPosition posAfterInsertion
= position
;
5700 posAfterInsertion
.Add(istrlen(value
));
5701 SetSelection(posAfterInsertion
, position
);
5704 } else if (inDragDrop
== ddDragging
) {
5705 SetEmptySelection(position
);
5710 * @return true if given position is inside the selection,
5712 bool Editor::PositionInSelection(int pos
) {
5713 pos
= MovePositionOutsideChar(pos
, sel
.MainCaret() - pos
);
5714 for (size_t r
=0; r
<sel
.Count(); r
++) {
5715 if (sel
.Range(r
).Contains(pos
))
5721 bool Editor::PointInSelection(Point pt
) {
5722 SelectionPosition pos
= SPositionFromLocation(pt
);
5723 int xPos
= XFromPosition(pos
);
5724 for (size_t r
=0; r
<sel
.Count(); r
++) {
5725 SelectionRange range
= sel
.Range(r
);
5726 if (range
.Contains(pos
)) {
5728 if (pos
== range
.Start()) {
5729 // see if just before selection
5734 if (pos
== range
.End()) {
5735 // see if just after selection
5747 bool Editor::PointInSelMargin(Point pt
) {
5748 // Really means: "Point in a margin"
5749 if (vs
.fixedColumnWidth
> 0) { // There is a margin
5750 PRectangle rcSelMargin
= GetClientRectangle();
5751 rcSelMargin
.right
= vs
.fixedColumnWidth
- vs
.leftMarginWidth
;
5752 return rcSelMargin
.Contains(pt
);
5758 void Editor::LineSelection(int lineCurrent_
, int lineAnchor_
) {
5759 if (lineAnchor_
< lineCurrent_
) {
5760 SetSelection(pdoc
->LineStart(lineCurrent_
+ 1),
5761 pdoc
->LineStart(lineAnchor_
));
5762 } else if (lineAnchor_
> lineCurrent_
) {
5763 SetSelection(pdoc
->LineStart(lineCurrent_
),
5764 pdoc
->LineStart(lineAnchor_
+ 1));
5765 } else { // Same line, select it
5766 SetSelection(pdoc
->LineStart(lineAnchor_
+ 1),
5767 pdoc
->LineStart(lineAnchor_
));
5771 void Editor::DwellEnd(bool mouseMoved
) {
5773 ticksToDwell
= dwellDelay
;
5775 ticksToDwell
= SC_TIME_FOREVER
;
5776 if (dwelling
&& (dwellDelay
< SC_TIME_FOREVER
)) {
5778 NotifyDwelling(ptMouseLast
, dwelling
);
5782 void Editor::MouseLeave() {
5783 SetHotSpotRange(NULL
);
5786 static bool AllowVirtualSpace(int virtualSpaceOptions
, bool rectangular
) {
5787 return ((virtualSpaceOptions
& SCVS_USERACCESSIBLE
) != 0)
5788 || (rectangular
&& ((virtualSpaceOptions
& SCVS_RECTANGULARSELECTION
) != 0));
5791 void Editor::ButtonDown(Point pt
, unsigned int curTime
, bool shift
, bool ctrl
, bool alt
) {
5792 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
5794 SelectionPosition newPos
= SPositionFromLocation(pt
, false, false, AllowVirtualSpace(virtualSpaceOptions
, alt
));
5795 newPos
= MovePositionOutsideChar(newPos
, sel
.MainCaret() - newPos
.Position());
5796 inDragDrop
= ddNone
;
5797 sel
.SetMoveExtends(false);
5799 bool processed
= NotifyMarginClick(pt
, shift
, ctrl
, alt
);
5803 NotifyIndicatorClick(true, newPos
.Position(), shift
, ctrl
, alt
);
5805 bool inSelMargin
= PointInSelMargin(pt
);
5806 if (shift
& !inSelMargin
) {
5807 SetSelection(newPos
.Position());
5809 if (((curTime
- lastClickTime
) < Platform::DoubleClickTime()) && Close(pt
, lastClick
)) {
5810 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
5811 SetMouseCapture(true);
5812 SetEmptySelection(newPos
.Position());
5813 bool doubleClick
= false;
5814 // Stop mouse button bounce changing selection type
5815 if (!Platform::MouseButtonBounce() || curTime
!= lastClickTime
) {
5816 if (selectionType
== selChar
) {
5817 selectionType
= selWord
;
5819 } else if (selectionType
== selWord
) {
5820 selectionType
= selLine
;
5822 selectionType
= selChar
;
5823 originalAnchorPos
= sel
.MainCaret();
5827 if (selectionType
== selWord
) {
5828 if (sel
.MainCaret() >= originalAnchorPos
) { // Moved forward
5829 SetSelection(pdoc
->ExtendWordSelect(sel
.MainCaret(), 1),
5830 pdoc
->ExtendWordSelect(originalAnchorPos
, -1));
5831 } else { // Moved backward
5832 SetSelection(pdoc
->ExtendWordSelect(sel
.MainCaret(), -1),
5833 pdoc
->ExtendWordSelect(originalAnchorPos
, 1));
5835 } else if (selectionType
== selLine
) {
5836 lineAnchor
= LineFromLocation(pt
);
5837 SetSelection(pdoc
->LineStart(lineAnchor
+ 1), pdoc
->LineStart(lineAnchor
));
5838 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
5840 SetEmptySelection(sel
.MainCaret());
5842 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
5844 NotifyDoubleClick(pt
, shift
, ctrl
, alt
);
5845 if (PositionIsHotspot(newPos
.Position()))
5846 NotifyHotSpotDoubleClicked(newPos
.Position(), shift
, ctrl
, alt
);
5848 } else { // Single click
5850 sel
.selType
= Selection::selStream
;
5853 lastClickTime
= curTime
;
5857 lineAnchor
= LineFromLocation(pt
);
5858 // Single click in margin: select whole line
5859 LineSelection(lineAnchor
, lineAnchor
);
5860 SetSelection(pdoc
->LineStart(lineAnchor
+ 1),
5861 pdoc
->LineStart(lineAnchor
));
5863 // Single shift+click in margin: select from line anchor to clicked line
5864 if (sel
.MainAnchor() > sel
.MainCaret())
5865 lineAnchor
= pdoc
->LineFromPosition(sel
.MainAnchor() - 1);
5867 lineAnchor
= pdoc
->LineFromPosition(sel
.MainAnchor());
5868 int lineStart
= LineFromLocation(pt
);
5869 LineSelection(lineStart
, lineAnchor
);
5870 //lineAnchor = lineStart; // Keep the same anchor for ButtonMove
5873 SetDragPosition(SelectionPosition(invalidPosition
));
5874 SetMouseCapture(true);
5875 selectionType
= selLine
;
5877 if (PointIsHotspot(pt
)) {
5878 NotifyHotSpotClicked(newPos
.Position(), shift
, ctrl
, alt
);
5881 if (PointInSelection(pt
) && !SelectionEmpty())
5882 inDragDrop
= ddInitial
;
5884 inDragDrop
= ddNone
;
5886 SetMouseCapture(true);
5887 if (inDragDrop
!= ddInitial
) {
5888 SetDragPosition(SelectionPosition(invalidPosition
));
5890 if (ctrl
&& multipleSelection
) {
5891 SelectionRange
range(newPos
);
5892 sel
.TentativeSelection(range
);
5893 InvalidateSelection(range
, true);
5895 InvalidateSelection(SelectionRange(newPos
), true);
5896 if (sel
.Count() > 1)
5899 sel
.selType
= alt
? Selection::selRectangle
: Selection::selStream
;
5900 SetSelection(newPos
, newPos
);
5903 SelectionPosition anchorCurrent
= newPos
;
5905 anchorCurrent
= sel
.IsRectangular() ?
5906 sel
.Rectangular().anchor
: sel
.RangeMain().anchor
;
5907 sel
.selType
= alt
? Selection::selRectangle
: Selection::selStream
;
5908 selectionType
= selChar
;
5909 originalAnchorPos
= sel
.MainCaret();
5910 sel
.Rectangular() = SelectionRange(newPos
, anchorCurrent
);
5911 SetRectangularRange();
5915 lastClickTime
= curTime
;
5916 lastXChosen
= pt
.x
+ xOffset
;
5917 ShowCaretAtCurrentPosition();
5920 bool Editor::PositionIsHotspot(int position
) {
5921 return vs
.styles
[pdoc
->StyleAt(position
) & pdoc
->stylingBitsMask
].hotspot
;
5924 bool Editor::PointIsHotspot(Point pt
) {
5925 int pos
= PositionFromLocation(pt
, true);
5926 if (pos
== INVALID_POSITION
)
5928 return PositionIsHotspot(pos
);
5931 void Editor::SetHotSpotRange(Point
*pt
) {
5933 int pos
= PositionFromLocation(*pt
);
5935 // If we don't limit this to word characters then the
5936 // range can encompass more than the run range and then
5937 // the underline will not be drawn properly.
5938 int hsStart_
= pdoc
->ExtendStyleRange(pos
, -1, vs
.hotspotSingleLine
);
5939 int hsEnd_
= pdoc
->ExtendStyleRange(pos
, 1, vs
.hotspotSingleLine
);
5941 // Only invalidate the range if the hotspot range has changed...
5942 if (hsStart_
!= hsStart
|| hsEnd_
!= hsEnd
) {
5943 if (hsStart
!= -1) {
5944 InvalidateRange(hsStart
, hsEnd
);
5948 InvalidateRange(hsStart
, hsEnd
);
5951 if (hsStart
!= -1) {
5952 int hsStart_
= hsStart
;
5956 InvalidateRange(hsStart_
, hsEnd_
);
5964 void Editor::GetHotSpotRange(int &hsStart_
, int &hsEnd_
) {
5969 void Editor::ButtonMove(Point pt
) {
5970 if ((ptMouseLast
.x
!= pt
.x
) || (ptMouseLast
.y
!= pt
.y
)) {
5974 SelectionPosition movePos
= SPositionFromLocation(pt
, false, false,
5975 AllowVirtualSpace(virtualSpaceOptions
, sel
.IsRectangular()));
5976 movePos
= MovePositionOutsideChar(movePos
, sel
.MainCaret() - movePos
.Position());
5978 if (inDragDrop
== ddInitial
) {
5979 if (DragThreshold(ptMouseLast
, pt
)) {
5980 SetMouseCapture(false);
5981 SetDragPosition(movePos
);
5982 CopySelectionRange(&drag
);
5989 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
5990 if (HaveMouseCapture()) {
5992 // Slow down autoscrolling/selection
5993 autoScrollTimer
.ticksToWait
-= timer
.tickSize
;
5994 if (autoScrollTimer
.ticksToWait
> 0)
5996 autoScrollTimer
.ticksToWait
= autoScrollDelay
;
5999 if (posDrag
.IsValid()) {
6000 SetDragPosition(movePos
);
6002 if (selectionType
== selChar
) {
6003 if (sel
.IsRectangular()) {
6004 sel
.Rectangular() = SelectionRange(movePos
, sel
.Rectangular().anchor
);
6005 SetSelection(movePos
, sel
.RangeMain().anchor
);
6006 } else if (sel
.Count() > 1) {
6007 SelectionRange
range(movePos
, sel
.RangeMain().anchor
);
6008 sel
.TentativeSelection(range
);
6009 InvalidateSelection(range
, true);
6011 SetSelection(movePos
, sel
.RangeMain().anchor
);
6013 } else if (selectionType
== selWord
) {
6014 // Continue selecting by word
6015 if (movePos
.Position() == originalAnchorPos
) { // Didn't move
6016 // No need to do anything. Previously this case was lumped
6017 // in with "Moved forward", but that can be harmful in this
6018 // case: a handler for the NotifyDoubleClick re-adjusts
6019 // the selection for a fancier definition of "word" (for
6020 // example, in Perl it is useful to include the leading
6021 // '$', '%' or '@' on variables for word selection). In this
6022 // the ButtonMove() called via Tick() for auto-scrolling
6023 // could result in the fancier word selection adjustment
6025 } else if (movePos
.Position() > originalAnchorPos
) { // Moved forward
6026 SetSelection(pdoc
->ExtendWordSelect(movePos
.Position(), 1),
6027 pdoc
->ExtendWordSelect(originalAnchorPos
, -1));
6028 } else { // Moved backward
6029 SetSelection(pdoc
->ExtendWordSelect(movePos
.Position(), -1),
6030 pdoc
->ExtendWordSelect(originalAnchorPos
, 1));
6033 // Continue selecting by line
6034 int lineMove
= LineFromLocation(pt
);
6035 LineSelection(lineMove
, lineAnchor
);
6040 PRectangle rcClient
= GetClientRectangle();
6041 if (pt
.y
> rcClient
.bottom
) {
6042 int lineMove
= cs
.DisplayFromDoc(LineFromLocation(pt
));
6044 lineMove
= cs
.DisplayFromDoc(pdoc
->LinesTotal() - 1);
6046 ScrollTo(lineMove
- LinesOnScreen() + 1);
6048 } else if (pt
.y
< rcClient
.top
) {
6049 int lineMove
= cs
.DisplayFromDoc(LineFromLocation(pt
));
6050 ScrollTo(lineMove
- 1);
6053 EnsureCaretVisible(false, false, true);
6055 if (hsStart
!= -1 && !PositionIsHotspot(movePos
.Position()))
6056 SetHotSpotRange(NULL
);
6059 if (vs
.fixedColumnWidth
> 0) { // There is a margin
6060 if (PointInSelMargin(pt
)) {
6061 DisplayCursor(Window::cursorReverseArrow
);
6062 SetHotSpotRange(NULL
);
6063 return; // No need to test for selection
6066 // Display regular (drag) cursor over selection
6067 if (PointInSelection(pt
) && !SelectionEmpty()) {
6068 DisplayCursor(Window::cursorArrow
);
6069 } else if (PointIsHotspot(pt
)) {
6070 DisplayCursor(Window::cursorHand
);
6071 SetHotSpotRange(&pt
);
6073 DisplayCursor(Window::cursorText
);
6074 SetHotSpotRange(NULL
);
6079 void Editor::ButtonUp(Point pt
, unsigned int curTime
, bool ctrl
) {
6080 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
6081 SelectionPosition newPos
= SPositionFromLocation(pt
, false, false,
6082 AllowVirtualSpace(virtualSpaceOptions
, sel
.IsRectangular()));
6083 newPos
= MovePositionOutsideChar(newPos
, sel
.MainCaret() - newPos
.Position());
6084 if (inDragDrop
== ddInitial
) {
6085 inDragDrop
= ddNone
;
6086 SetEmptySelection(newPos
.Position());
6088 if (HaveMouseCapture()) {
6089 if (PointInSelMargin(pt
)) {
6090 DisplayCursor(Window::cursorReverseArrow
);
6092 DisplayCursor(Window::cursorText
);
6093 SetHotSpotRange(NULL
);
6096 SetMouseCapture(false);
6097 NotifyIndicatorClick(false, newPos
.Position(), false, false, false);
6098 if (inDragDrop
== ddDragging
) {
6099 SelectionPosition selStart
= SelectionStart();
6100 SelectionPosition selEnd
= SelectionEnd();
6101 if (selStart
< selEnd
) {
6104 if (pdoc
->InsertString(newPos
.Position(), drag
.s
, drag
.len
)) {
6105 SetSelection(newPos
.Position(), newPos
.Position() + drag
.len
);
6107 } else if (newPos
< selStart
) {
6108 pdoc
->DeleteChars(selStart
.Position(), drag
.len
);
6109 if (pdoc
->InsertString(newPos
.Position(), drag
.s
, drag
.len
)) {
6110 SetSelection(newPos
.Position(), newPos
.Position() + drag
.len
);
6112 } else if (newPos
> selEnd
) {
6113 pdoc
->DeleteChars(selStart
.Position(), drag
.len
);
6114 newPos
.Add(-drag
.len
);
6115 if (pdoc
->InsertString(newPos
.Position(), drag
.s
, drag
.len
)) {
6116 SetSelection(newPos
.Position(), newPos
.Position() + drag
.len
);
6119 SetEmptySelection(newPos
.Position());
6123 selectionType
= selChar
;
6126 if (selectionType
== selChar
) {
6127 if (sel
.Count() > 1) {
6129 SelectionRange(newPos
, sel
.Range(sel
.Count() - 1).anchor
);
6130 InvalidateSelection(sel
.RangeMain(), true);
6132 SetSelection(newPos
, sel
.RangeMain().anchor
);
6135 sel
.CommitTentative();
6137 SetRectangularRange();
6138 lastClickTime
= curTime
;
6140 lastXChosen
= pt
.x
+ xOffset
;
6141 if (sel
.selType
== Selection::selStream
) {
6144 inDragDrop
= ddNone
;
6145 EnsureCaretVisible(false);
6149 // Called frequently to perform background UI including
6150 // caret blinking and automatic scrolling.
6151 void Editor::Tick() {
6152 if (HaveMouseCapture()) {
6154 ButtonMove(ptMouseLast
);
6156 if (caret
.period
> 0) {
6157 timer
.ticksToWait
-= timer
.tickSize
;
6158 if (timer
.ticksToWait
<= 0) {
6159 caret
.on
= !caret
.on
;
6160 timer
.ticksToWait
= caret
.period
;
6166 if (horizontalScrollBarVisible
&& trackLineWidth
&& (lineWidthMaxSeen
> scrollWidth
)) {
6167 scrollWidth
= lineWidthMaxSeen
;
6170 if ((dwellDelay
< SC_TIME_FOREVER
) &&
6171 (ticksToDwell
> 0) &&
6172 (!HaveMouseCapture())) {
6173 ticksToDwell
-= timer
.tickSize
;
6174 if (ticksToDwell
<= 0) {
6176 NotifyDwelling(ptMouseLast
, dwelling
);
6181 bool Editor::Idle() {
6185 bool wrappingDone
= wrapState
== eWrapNone
;
6187 if (!wrappingDone
) {
6188 // Wrap lines during idle.
6189 WrapLines(false, -1);
6191 if (wrapStart
== wrapEnd
)
6192 wrappingDone
= true;
6195 // Add more idle things to do here, but make sure idleDone is
6196 // set correctly before the function returns. returning
6197 // false will stop calling this idle funtion until SetIdle() is
6200 idleDone
= wrappingDone
; // && thatDone && theOtherThingDone...
6205 void Editor::SetFocusState(bool focusState
) {
6206 hasFocus
= focusState
;
6207 NotifyFocus(hasFocus
);
6209 ShowCaretAtCurrentPosition();
6216 int Editor::PositionAfterArea(PRectangle rcArea
) {
6217 // The start of the document line after the display line after the area
6218 // This often means that the line after a modification is restyled which helps
6219 // detect multiline comment additions and heals single line comments
6220 int lineAfter
= topLine
+ (rcArea
.bottom
- 1) / vs
.lineHeight
+ 1;
6221 if (lineAfter
< cs
.LinesDisplayed())
6222 return pdoc
->LineStart(cs
.DocFromDisplay(lineAfter
) + 1);
6224 return pdoc
->Length();
6227 // Style to a position within the view. If this causes a change at end of last line then
6228 // affects later lines so style all the viewed text.
6229 void Editor::StyleToPositionInView(Position pos
) {
6230 int endWindow
= PositionAfterArea(GetClientRectangle());
6231 if (pos
> endWindow
)
6233 int styleAtEnd
= pdoc
->StyleAt(pos
-1);
6234 pdoc
->EnsureStyledTo(pos
);
6235 if ((endWindow
> pos
) && (styleAtEnd
!= pdoc
->StyleAt(pos
-1))) {
6236 // Style at end of line changed so is multi-line change like starting a comment
6237 // so require rest of window to be styled.
6238 pdoc
->EnsureStyledTo(endWindow
);
6242 void Editor::IdleStyling() {
6243 // Style the line after the modification as this allows modifications that change just the
6244 // line of the modification to heal instead of propagating to the rest of the window.
6245 StyleToPositionInView(pdoc
->LineStart(pdoc
->LineFromPosition(styleNeeded
.upTo
) + 2));
6249 needUpdateUI
= false;
6251 styleNeeded
.Reset();
6254 void Editor::QueueStyling(int upTo
) {
6255 styleNeeded
.NeedUpTo(upTo
);
6258 bool Editor::PaintContains(PRectangle rc
) {
6262 return rcPaint
.Contains(rc
);
6266 bool Editor::PaintContainsMargin() {
6267 PRectangle rcSelMargin
= GetClientRectangle();
6268 rcSelMargin
.right
= vs
.fixedColumnWidth
;
6269 return PaintContains(rcSelMargin
);
6272 void Editor::CheckForChangeOutsidePaint(Range r
) {
6273 if (paintState
== painting
&& !paintingAllText
) {
6274 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
6278 PRectangle rcRange
= RectangleFromRange(r
.start
, r
.end
);
6279 PRectangle rcText
= GetTextRectangle();
6280 if (rcRange
.top
< rcText
.top
) {
6281 rcRange
.top
= rcText
.top
;
6283 if (rcRange
.bottom
> rcText
.bottom
) {
6284 rcRange
.bottom
= rcText
.bottom
;
6287 if (!PaintContains(rcRange
)) {
6293 void Editor::SetBraceHighlight(Position pos0
, Position pos1
, int matchStyle
) {
6294 if ((pos0
!= braces
[0]) || (pos1
!= braces
[1]) || (matchStyle
!= bracesMatchStyle
)) {
6295 if ((braces
[0] != pos0
) || (matchStyle
!= bracesMatchStyle
)) {
6296 CheckForChangeOutsidePaint(Range(braces
[0]));
6297 CheckForChangeOutsidePaint(Range(pos0
));
6300 if ((braces
[1] != pos1
) || (matchStyle
!= bracesMatchStyle
)) {
6301 CheckForChangeOutsidePaint(Range(braces
[1]));
6302 CheckForChangeOutsidePaint(Range(pos1
));
6305 bracesMatchStyle
= matchStyle
;
6306 if (paintState
== notPainting
) {
6312 void Editor::SetAnnotationHeights(int start
, int end
) {
6313 if (vs
.annotationVisible
) {
6314 for (int line
=start
; line
<end
; line
++) {
6315 cs
.SetHeight(line
, pdoc
->AnnotationLines(line
) + 1);
6320 void Editor::SetDocPointer(Document
*document
) {
6321 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
6322 pdoc
->RemoveWatcher(this, 0);
6324 if (document
== NULL
) {
6325 pdoc
= new Document();
6331 // Ensure all positions within document
6336 braces
[0] = invalidPosition
;
6337 braces
[1] = invalidPosition
;
6339 // Reset the contraction state to fully shown.
6341 cs
.InsertLines(0, pdoc
->LinesTotal() - 1);
6342 SetAnnotationHeights(0, pdoc
->LinesTotal());
6346 pdoc
->AddWatcher(this, 0);
6351 void Editor::SetAnnotationVisible(int visible
) {
6352 if (vs
.annotationVisible
!= visible
) {
6353 bool changedFromOrToHidden
= ((vs
.annotationVisible
!= 0) != (visible
!= 0));
6354 vs
.annotationVisible
= visible
;
6355 if (changedFromOrToHidden
) {
6356 int dir
= vs
.annotationVisible
? 1 : -1;
6357 for (int line
=0; line
<pdoc
->LinesTotal(); line
++) {
6358 int annotationLines
= pdoc
->AnnotationLines(line
);
6359 if (annotationLines
> 0) {
6360 cs
.SetHeight(line
, cs
.GetHeight(line
) + annotationLines
* dir
);
6368 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
6370 void Editor::Expand(int &line
, bool doExpand
) {
6371 int lineMaxSubord
= pdoc
->GetLastChild(line
);
6373 while (line
<= lineMaxSubord
) {
6375 cs
.SetVisible(line
, line
, true);
6376 int level
= pdoc
->GetLevel(line
);
6377 if (level
& SC_FOLDLEVELHEADERFLAG
) {
6378 if (doExpand
&& cs
.GetExpanded(line
)) {
6381 Expand(line
, false);
6389 void Editor::ToggleContraction(int line
) {
6391 if ((pdoc
->GetLevel(line
) & SC_FOLDLEVELHEADERFLAG
) == 0) {
6392 line
= pdoc
->GetFoldParent(line
);
6397 if (cs
.GetExpanded(line
)) {
6398 int lineMaxSubord
= pdoc
->GetLastChild(line
);
6399 cs
.SetExpanded(line
, 0);
6400 if (lineMaxSubord
> line
) {
6401 cs
.SetVisible(line
+ 1, lineMaxSubord
, false);
6403 int lineCurrent
= pdoc
->LineFromPosition(sel
.MainCaret());
6404 if (lineCurrent
> line
&& lineCurrent
<= lineMaxSubord
) {
6405 // This does not re-expand the fold
6406 EnsureCaretVisible();
6414 if (!(cs
.GetVisible(line
))) {
6415 EnsureLineVisible(line
, false);
6418 cs
.SetExpanded(line
, 1);
6427 * Recurse up from this line to find any folds that prevent this line from being visible
6428 * and unfold them all.
6430 void Editor::EnsureLineVisible(int lineDoc
, bool enforcePolicy
) {
6432 // In case in need of wrapping to ensure DisplayFromDoc works.
6433 WrapLines(true, -1);
6435 if (!cs
.GetVisible(lineDoc
)) {
6436 int lineParent
= pdoc
->GetFoldParent(lineDoc
);
6437 if (lineParent
>= 0) {
6438 if (lineDoc
!= lineParent
)
6439 EnsureLineVisible(lineParent
, enforcePolicy
);
6440 if (!cs
.GetExpanded(lineParent
)) {
6441 cs
.SetExpanded(lineParent
, 1);
6442 Expand(lineParent
, true);
6448 if (enforcePolicy
) {
6449 int lineDisplay
= cs
.DisplayFromDoc(lineDoc
);
6450 if (visiblePolicy
& VISIBLE_SLOP
) {
6451 if ((topLine
> lineDisplay
) || ((visiblePolicy
& VISIBLE_STRICT
) && (topLine
+ visibleSlop
> lineDisplay
))) {
6452 SetTopLine(Platform::Clamp(lineDisplay
- visibleSlop
, 0, MaxScrollPos()));
6453 SetVerticalScrollPos();
6455 } else if ((lineDisplay
> topLine
+ LinesOnScreen() - 1) ||
6456 ((visiblePolicy
& VISIBLE_STRICT
) && (lineDisplay
> topLine
+ LinesOnScreen() - 1 - visibleSlop
))) {
6457 SetTopLine(Platform::Clamp(lineDisplay
- LinesOnScreen() + 1 + visibleSlop
, 0, MaxScrollPos()));
6458 SetVerticalScrollPos();
6462 if ((topLine
> lineDisplay
) || (lineDisplay
> topLine
+ LinesOnScreen() - 1) || (visiblePolicy
& VISIBLE_STRICT
)) {
6463 SetTopLine(Platform::Clamp(lineDisplay
- LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
6464 SetVerticalScrollPos();
6471 int Editor::GetTag(char *tagValue
, int tagNumber
) {
6472 char name
[3] = "\\?";
6473 const char *text
= 0;
6475 if ((tagNumber
>= 1) && (tagNumber
<= 9)) {
6476 name
[1] = static_cast<char>(tagNumber
+ '0');
6478 text
= pdoc
->SubstituteByPosition(name
, &length
);
6482 memcpy(tagValue
, text
, length
+ 1);
6489 int Editor::ReplaceTarget(bool replacePatterns
, const char *text
, int length
) {
6492 length
= istrlen(text
);
6493 if (replacePatterns
) {
6494 text
= pdoc
->SubstituteByPosition(text
, &length
);
6499 if (targetStart
!= targetEnd
)
6500 pdoc
->DeleteChars(targetStart
, targetEnd
- targetStart
);
6501 targetEnd
= targetStart
;
6502 pdoc
->InsertString(targetStart
, text
, length
);
6503 targetEnd
= targetStart
+ length
;
6507 bool Editor::IsUnicodeMode() const {
6508 return pdoc
&& (SC_CP_UTF8
== pdoc
->dbcsCodePage
);
6511 int Editor::CodePage() const {
6513 return pdoc
->dbcsCodePage
;
6518 int Editor::WrapCount(int line
) {
6519 AutoSurface
surface(this);
6520 AutoLineLayout
ll(llc
, RetrieveLineLayout(line
));
6522 if (surface
&& ll
) {
6523 LayoutLine(line
, surface
, vs
, ll
, wrapWidth
);
6530 void Editor::AddStyledText(char *buffer
, int appendLength
) {
6531 // The buffer consists of alternating character bytes and style bytes
6532 size_t textLength
= appendLength
/ 2;
6533 char *text
= new char[textLength
];
6535 for (i
= 0; i
< textLength
; i
++) {
6536 text
[i
] = buffer
[i
*2];
6538 pdoc
->InsertString(CurrentPosition(), text
, textLength
);
6539 for (i
= 0; i
< textLength
; i
++) {
6540 text
[i
] = buffer
[i
*2+1];
6542 pdoc
->StartStyling(CurrentPosition(), static_cast<char>(0xff));
6543 pdoc
->SetStyles(textLength
, text
);
6545 SetEmptySelection(sel
.MainCaret() + textLength
);
6548 static bool ValidMargin(unsigned long wParam
) {
6549 return wParam
< ViewStyle::margins
;
6552 static char *CharPtrFromSPtr(sptr_t lParam
) {
6553 return reinterpret_cast<char *>(lParam
);
6556 void Editor::StyleSetMessage(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
6557 vs
.EnsureStyle(wParam
);
6559 case SCI_STYLESETFORE
:
6560 vs
.styles
[wParam
].fore
.desired
= ColourDesired(lParam
);
6562 case SCI_STYLESETBACK
:
6563 vs
.styles
[wParam
].back
.desired
= ColourDesired(lParam
);
6565 case SCI_STYLESETBOLD
:
6566 vs
.styles
[wParam
].bold
= lParam
!= 0;
6568 case SCI_STYLESETITALIC
:
6569 vs
.styles
[wParam
].italic
= lParam
!= 0;
6571 case SCI_STYLESETEOLFILLED
:
6572 vs
.styles
[wParam
].eolFilled
= lParam
!= 0;
6574 case SCI_STYLESETSIZE
:
6575 vs
.styles
[wParam
].size
= lParam
;
6577 case SCI_STYLESETFONT
:
6579 vs
.SetStyleFontName(wParam
, CharPtrFromSPtr(lParam
));
6582 case SCI_STYLESETUNDERLINE
:
6583 vs
.styles
[wParam
].underline
= lParam
!= 0;
6585 case SCI_STYLESETCASE
:
6586 vs
.styles
[wParam
].caseForce
= static_cast<Style::ecaseForced
>(lParam
);
6588 case SCI_STYLESETCHARACTERSET
:
6589 vs
.styles
[wParam
].characterSet
= lParam
;
6591 case SCI_STYLESETVISIBLE
:
6592 vs
.styles
[wParam
].visible
= lParam
!= 0;
6594 case SCI_STYLESETCHANGEABLE
:
6595 vs
.styles
[wParam
].changeable
= lParam
!= 0;
6597 case SCI_STYLESETHOTSPOT
:
6598 vs
.styles
[wParam
].hotspot
= lParam
!= 0;
6601 InvalidateStyleRedraw();
6604 sptr_t
Editor::StyleGetMessage(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
6605 vs
.EnsureStyle(wParam
);
6607 case SCI_STYLEGETFORE
:
6608 return vs
.styles
[wParam
].fore
.desired
.AsLong();
6609 case SCI_STYLEGETBACK
:
6610 return vs
.styles
[wParam
].back
.desired
.AsLong();
6611 case SCI_STYLEGETBOLD
:
6612 return vs
.styles
[wParam
].bold
? 1 : 0;
6613 case SCI_STYLEGETITALIC
:
6614 return vs
.styles
[wParam
].italic
? 1 : 0;
6615 case SCI_STYLEGETEOLFILLED
:
6616 return vs
.styles
[wParam
].eolFilled
? 1 : 0;
6617 case SCI_STYLEGETSIZE
:
6618 return vs
.styles
[wParam
].size
;
6619 case SCI_STYLEGETFONT
:
6620 if (!vs
.styles
[wParam
].fontName
)
6623 strcpy(CharPtrFromSPtr(lParam
), vs
.styles
[wParam
].fontName
);
6624 return strlen(vs
.styles
[wParam
].fontName
);
6625 case SCI_STYLEGETUNDERLINE
:
6626 return vs
.styles
[wParam
].underline
? 1 : 0;
6627 case SCI_STYLEGETCASE
:
6628 return static_cast<int>(vs
.styles
[wParam
].caseForce
);
6629 case SCI_STYLEGETCHARACTERSET
:
6630 return vs
.styles
[wParam
].characterSet
;
6631 case SCI_STYLEGETVISIBLE
:
6632 return vs
.styles
[wParam
].visible
? 1 : 0;
6633 case SCI_STYLEGETCHANGEABLE
:
6634 return vs
.styles
[wParam
].changeable
? 1 : 0;
6635 case SCI_STYLEGETHOTSPOT
:
6636 return vs
.styles
[wParam
].hotspot
? 1 : 0;
6641 sptr_t
Editor::StringResult(sptr_t lParam
, const char *val
) {
6642 const int n
= strlen(val
);
6644 char *ptr
= reinterpret_cast<char *>(lParam
);
6647 return n
; // Not including NUL
6650 sptr_t
Editor::WndProc(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
6651 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
6653 // Optional macro recording hook
6655 NotifyMacroRecord(iMessage
, wParam
, lParam
);
6661 return pdoc
->Length() + 1;
6664 char *ptr
= CharPtrFromSPtr(lParam
);
6665 unsigned int iChar
= 0;
6666 for (; iChar
< wParam
- 1; iChar
++)
6667 ptr
[iChar
] = pdoc
->CharAt(iChar
);
6676 pdoc
->DeleteChars(0, pdoc
->Length());
6677 SetEmptySelection(0);
6678 pdoc
->InsertCString(0, CharPtrFromSPtr(lParam
));
6682 case SCI_GETTEXTLENGTH
:
6683 return pdoc
->Length();
6694 case SCI_COPYALLOWLINE
:
6699 CopyRangeToClipboard(wParam
, lParam
);
6703 CopyText(wParam
, CharPtrFromSPtr(lParam
));
6711 EnsureCaretVisible();
6717 EnsureCaretVisible();
6726 return (pdoc
->CanUndo() && !pdoc
->IsReadOnly()) ? 1 : 0;
6728 case SCI_EMPTYUNDOBUFFER
:
6729 pdoc
->DeleteUndoHistory();
6732 case SCI_GETFIRSTVISIBLELINE
:
6735 case SCI_SETFIRSTVISIBLELINE
:
6739 case SCI_GETLINE
: { // Risk of overwriting the end of the buffer
6740 int lineStart
= pdoc
->LineStart(wParam
);
6741 int lineEnd
= pdoc
->LineStart(wParam
+ 1);
6743 return lineEnd
- lineStart
;
6745 char *ptr
= CharPtrFromSPtr(lParam
);
6747 for (int iChar
= lineStart
; iChar
< lineEnd
; iChar
++) {
6748 ptr
[iPlace
++] = pdoc
->CharAt(iChar
);
6753 case SCI_GETLINECOUNT
:
6754 if (pdoc
->LinesTotal() == 0)
6757 return pdoc
->LinesTotal();
6760 return !pdoc
->IsSavePoint();
6763 int nStart
= static_cast<int>(wParam
);
6764 int nEnd
= static_cast<int>(lParam
);
6766 nEnd
= pdoc
->Length();
6768 nStart
= nEnd
; // Remove selection
6769 InvalidateSelection(SelectionRange(nStart
, nEnd
));
6771 sel
.selType
= Selection::selStream
;
6772 SetSelection(nEnd
, nStart
);
6773 EnsureCaretVisible();
6777 case SCI_GETSELTEXT
: {
6778 SelectionText selectedText
;
6779 CopySelectionRange(&selectedText
);
6781 return selectedText
.len
? selectedText
.len
: 1;
6783 char *ptr
= CharPtrFromSPtr(lParam
);
6785 if (selectedText
.len
) {
6786 for (; iChar
< selectedText
.len
; iChar
++)
6787 ptr
[iChar
] = selectedText
.s
[iChar
];
6795 case SCI_LINEFROMPOSITION
:
6796 if (static_cast<int>(wParam
) < 0)
6798 return pdoc
->LineFromPosition(wParam
);
6800 case SCI_POSITIONFROMLINE
:
6801 if (static_cast<int>(wParam
) < 0)
6802 wParam
= pdoc
->LineFromPosition(SelectionStart().Position());
6804 return 0; // Even if there is no text, there is a first line that starts at 0
6805 if (static_cast<int>(wParam
) > pdoc
->LinesTotal())
6807 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
6809 return pdoc
->LineStart(wParam
);
6811 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
6812 case SCI_LINELENGTH
:
6813 if ((static_cast<int>(wParam
) < 0) ||
6814 (static_cast<int>(wParam
) > pdoc
->LineFromPosition(pdoc
->Length())))
6816 return pdoc
->LineStart(wParam
+ 1) - pdoc
->LineStart(wParam
);
6818 case SCI_REPLACESEL
: {
6823 char *replacement
= CharPtrFromSPtr(lParam
);
6824 pdoc
->InsertCString(sel
.MainCaret(), replacement
);
6825 SetEmptySelection(sel
.MainCaret() + istrlen(replacement
));
6826 EnsureCaretVisible();
6830 case SCI_SETTARGETSTART
:
6831 targetStart
= wParam
;
6834 case SCI_GETTARGETSTART
:
6837 case SCI_SETTARGETEND
:
6841 case SCI_GETTARGETEND
:
6844 case SCI_TARGETFROMSELECTION
:
6845 if (sel
.MainCaret() < sel
.MainAnchor()) {
6846 targetStart
= sel
.MainCaret();
6847 targetEnd
= sel
.MainAnchor();
6849 targetStart
= sel
.MainAnchor();
6850 targetEnd
= sel
.MainCaret();
6854 case SCI_REPLACETARGET
:
6855 PLATFORM_ASSERT(lParam
);
6856 return ReplaceTarget(false, CharPtrFromSPtr(lParam
), wParam
);
6858 case SCI_REPLACETARGETRE
:
6859 PLATFORM_ASSERT(lParam
);
6860 return ReplaceTarget(true, CharPtrFromSPtr(lParam
), wParam
);
6862 case SCI_SEARCHINTARGET
:
6863 PLATFORM_ASSERT(lParam
);
6864 return SearchInTarget(CharPtrFromSPtr(lParam
), wParam
);
6866 case SCI_SETSEARCHFLAGS
:
6867 searchFlags
= wParam
;
6870 case SCI_GETSEARCHFLAGS
:
6874 return GetTag(CharPtrFromSPtr(lParam
), wParam
);
6876 case SCI_POSITIONBEFORE
:
6877 return pdoc
->MovePositionOutsideChar(wParam
- 1, -1, true);
6879 case SCI_POSITIONAFTER
:
6880 return pdoc
->MovePositionOutsideChar(wParam
+ 1, 1, true);
6882 case SCI_LINESCROLL
:
6883 ScrollTo(topLine
+ lParam
);
6884 HorizontalScrollTo(xOffset
+ wParam
* vs
.spaceWidth
);
6887 case SCI_SETXOFFSET
:
6889 SetHorizontalScrollPos();
6893 case SCI_GETXOFFSET
:
6896 case SCI_CHOOSECARETX
:
6900 case SCI_SCROLLCARET
:
6901 EnsureCaretVisible();
6904 case SCI_SETREADONLY
:
6905 pdoc
->SetReadOnly(wParam
!= 0);
6908 case SCI_GETREADONLY
:
6909 return pdoc
->IsReadOnly();
6914 case SCI_POINTXFROMPOSITION
:
6918 Point pt
= LocationFromPosition(lParam
);
6922 case SCI_POINTYFROMPOSITION
:
6926 Point pt
= LocationFromPosition(lParam
);
6931 return FindText(wParam
, lParam
);
6933 case SCI_GETTEXTRANGE
: {
6936 Sci_TextRange
*tr
= reinterpret_cast<Sci_TextRange
*>(lParam
);
6937 int cpMax
= tr
->chrg
.cpMax
;
6939 cpMax
= pdoc
->Length();
6940 PLATFORM_ASSERT(cpMax
<= pdoc
->Length());
6941 int len
= cpMax
- tr
->chrg
.cpMin
; // No -1 as cpMin and cpMax are referring to inter character positions
6942 pdoc
->GetCharRange(tr
->lpstrText
, tr
->chrg
.cpMin
, len
);
6943 // Spec says copied text is terminated with a NUL
6944 tr
->lpstrText
[len
] = '\0';
6945 return len
; // Not including NUL
6948 case SCI_HIDESELECTION
:
6949 hideSelection
= wParam
!= 0;
6953 case SCI_FORMATRANGE
:
6954 return FormatRange(wParam
!= 0, reinterpret_cast<Sci_RangeToFormat
*>(lParam
));
6956 case SCI_GETMARGINLEFT
:
6957 return vs
.leftMarginWidth
;
6959 case SCI_GETMARGINRIGHT
:
6960 return vs
.rightMarginWidth
;
6962 case SCI_SETMARGINLEFT
:
6963 vs
.leftMarginWidth
= lParam
;
6964 InvalidateStyleRedraw();
6967 case SCI_SETMARGINRIGHT
:
6968 vs
.rightMarginWidth
= lParam
;
6969 InvalidateStyleRedraw();
6972 // Control specific mesages
6977 pdoc
->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam
), wParam
);
6978 SetEmptySelection(sel
.MainCaret() + wParam
);
6982 case SCI_ADDSTYLEDTEXT
:
6984 AddStyledText(CharPtrFromSPtr(lParam
), wParam
);
6987 case SCI_INSERTTEXT
: {
6990 int insertPos
= wParam
;
6991 if (static_cast<int>(wParam
) == -1)
6992 insertPos
= CurrentPosition();
6993 int newCurrent
= CurrentPosition();
6994 char *sz
= CharPtrFromSPtr(lParam
);
6995 pdoc
->InsertCString(insertPos
, sz
);
6996 if (newCurrent
> insertPos
)
6997 newCurrent
+= istrlen(sz
);
6998 SetEmptySelection(newCurrent
);
7002 case SCI_APPENDTEXT
:
7003 pdoc
->InsertString(pdoc
->Length(), CharPtrFromSPtr(lParam
), wParam
);
7010 case SCI_CLEARDOCUMENTSTYLE
:
7011 ClearDocumentStyle();
7014 case SCI_SETUNDOCOLLECTION
:
7015 pdoc
->SetUndoCollection(wParam
!= 0);
7018 case SCI_GETUNDOCOLLECTION
:
7019 return pdoc
->IsCollectingUndo();
7021 case SCI_BEGINUNDOACTION
:
7022 pdoc
->BeginUndoAction();
7025 case SCI_ENDUNDOACTION
:
7026 pdoc
->EndUndoAction();
7029 case SCI_GETCARETPERIOD
:
7030 return caret
.period
;
7032 case SCI_SETCARETPERIOD
:
7033 caret
.period
= wParam
;
7036 case SCI_SETWORDCHARS
: {
7037 pdoc
->SetDefaultCharClasses(false);
7040 pdoc
->SetCharClasses(reinterpret_cast<unsigned char *>(lParam
), CharClassify::ccWord
);
7044 case SCI_SETWHITESPACECHARS
: {
7047 pdoc
->SetCharClasses(reinterpret_cast<unsigned char *>(lParam
), CharClassify::ccSpace
);
7051 case SCI_SETCHARSDEFAULT
:
7052 pdoc
->SetDefaultCharClasses(true);
7056 return pdoc
->Length();
7059 pdoc
->Allocate(wParam
);
7063 return pdoc
->CharAt(wParam
);
7065 case SCI_SETCURRENTPOS
:
7066 if (sel
.IsRectangular()) {
7067 sel
.Rectangular().caret
.SetPosition(wParam
);
7068 SetRectangularRange();
7071 SetSelection(wParam
, sel
.MainAnchor());
7075 case SCI_GETCURRENTPOS
:
7076 return sel
.IsRectangular() ? sel
.Rectangular().caret
.Position() : sel
.MainCaret();
7079 if (sel
.IsRectangular()) {
7080 sel
.Rectangular().anchor
.SetPosition(wParam
);
7081 SetRectangularRange();
7084 SetSelection(sel
.MainCaret(), wParam
);
7089 return sel
.IsRectangular() ? sel
.Rectangular().anchor
.Position() : sel
.MainAnchor();
7091 case SCI_SETSELECTIONSTART
:
7092 SetSelection(Platform::Maximum(sel
.MainCaret(), wParam
), wParam
);
7095 case SCI_GETSELECTIONSTART
:
7096 return sel
.LimitsForRectangularElseMain().start
.Position();
7098 case SCI_SETSELECTIONEND
:
7099 SetSelection(wParam
, Platform::Minimum(sel
.MainAnchor(), wParam
));
7102 case SCI_GETSELECTIONEND
:
7103 return sel
.LimitsForRectangularElseMain().end
.Position();
7105 case SCI_SETPRINTMAGNIFICATION
:
7106 printMagnification
= wParam
;
7109 case SCI_GETPRINTMAGNIFICATION
:
7110 return printMagnification
;
7112 case SCI_SETPRINTCOLOURMODE
:
7113 printColourMode
= wParam
;
7116 case SCI_GETPRINTCOLOURMODE
:
7117 return printColourMode
;
7119 case SCI_SETPRINTWRAPMODE
:
7120 printWrapState
= (wParam
== SC_WRAP_WORD
) ? eWrapWord
: eWrapNone
;
7123 case SCI_GETPRINTWRAPMODE
:
7124 return printWrapState
;
7126 case SCI_GETSTYLEAT
:
7127 if (static_cast<int>(wParam
) >= pdoc
->Length())
7130 return pdoc
->StyleAt(wParam
);
7140 case SCI_SETSAVEPOINT
:
7141 pdoc
->SetSavePoint();
7144 case SCI_GETSTYLEDTEXT
: {
7147 Sci_TextRange
*tr
= reinterpret_cast<Sci_TextRange
*>(lParam
);
7149 for (int iChar
= tr
->chrg
.cpMin
; iChar
< tr
->chrg
.cpMax
; iChar
++) {
7150 tr
->lpstrText
[iPlace
++] = pdoc
->CharAt(iChar
);
7151 tr
->lpstrText
[iPlace
++] = pdoc
->StyleAt(iChar
);
7153 tr
->lpstrText
[iPlace
] = '\0';
7154 tr
->lpstrText
[iPlace
+ 1] = '\0';
7159 return (pdoc
->CanRedo() && !pdoc
->IsReadOnly()) ? 1 : 0;
7161 case SCI_MARKERLINEFROMHANDLE
:
7162 return pdoc
->LineFromHandle(wParam
);
7164 case SCI_MARKERDELETEHANDLE
:
7165 pdoc
->DeleteMarkFromHandle(wParam
);
7169 return vs
.viewWhitespace
;
7172 vs
.viewWhitespace
= static_cast<WhiteSpaceVisibility
>(wParam
);
7176 case SCI_GETWHITESPACESIZE
:
7177 return vs
.whitespaceSize
;
7179 case SCI_SETWHITESPACESIZE
:
7180 vs
.whitespaceSize
= static_cast<int>(wParam
);
7184 case SCI_POSITIONFROMPOINT
:
7185 return PositionFromLocation(Point(wParam
, lParam
), false, false);
7187 case SCI_POSITIONFROMPOINTCLOSE
:
7188 return PositionFromLocation(Point(wParam
, lParam
), true, false);
7190 case SCI_CHARPOSITIONFROMPOINT
:
7191 return PositionFromLocation(Point(wParam
, lParam
), false, true);
7193 case SCI_CHARPOSITIONFROMPOINTCLOSE
:
7194 return PositionFromLocation(Point(wParam
, lParam
), true, true);
7201 SetEmptySelection(wParam
);
7202 EnsureCaretVisible();
7206 case SCI_GETCURLINE
: {
7207 int lineCurrentPos
= pdoc
->LineFromPosition(sel
.MainCaret());
7208 int lineStart
= pdoc
->LineStart(lineCurrentPos
);
7209 unsigned int lineEnd
= pdoc
->LineStart(lineCurrentPos
+ 1);
7211 return 1 + lineEnd
- lineStart
;
7213 PLATFORM_ASSERT(wParam
> 0);
7214 char *ptr
= CharPtrFromSPtr(lParam
);
7215 unsigned int iPlace
= 0;
7216 for (unsigned int iChar
= lineStart
; iChar
< lineEnd
&& iPlace
< wParam
- 1; iChar
++) {
7217 ptr
[iPlace
++] = pdoc
->CharAt(iChar
);
7220 return sel
.MainCaret() - lineStart
;
7223 case SCI_GETENDSTYLED
:
7224 return pdoc
->GetEndStyled();
7226 case SCI_GETEOLMODE
:
7227 return pdoc
->eolMode
;
7229 case SCI_SETEOLMODE
:
7230 pdoc
->eolMode
= wParam
;
7233 case SCI_STARTSTYLING
:
7234 pdoc
->StartStyling(wParam
, static_cast<char>(lParam
));
7237 case SCI_SETSTYLING
:
7238 pdoc
->SetStyleFor(wParam
, static_cast<char>(lParam
));
7241 case SCI_SETSTYLINGEX
: // Specify a complete styling buffer
7244 pdoc
->SetStyles(wParam
, CharPtrFromSPtr(lParam
));
7247 case SCI_SETBUFFEREDDRAW
:
7248 bufferedDraw
= wParam
!= 0;
7251 case SCI_GETBUFFEREDDRAW
:
7252 return bufferedDraw
;
7254 case SCI_GETTWOPHASEDRAW
:
7255 return twoPhaseDraw
;
7257 case SCI_SETTWOPHASEDRAW
:
7258 twoPhaseDraw
= wParam
!= 0;
7259 InvalidateStyleRedraw();
7262 case SCI_SETFONTQUALITY
:
7263 vs
.extraFontFlag
&= ~SC_EFF_QUALITY_MASK
;
7264 vs
.extraFontFlag
|= (wParam
& SC_EFF_QUALITY_MASK
);
7265 InvalidateStyleRedraw();
7268 case SCI_GETFONTQUALITY
:
7269 return (vs
.extraFontFlag
& SC_EFF_QUALITY_MASK
);
7271 case SCI_SETTABWIDTH
:
7273 pdoc
->tabInChars
= wParam
;
7274 if (pdoc
->indentInChars
== 0)
7275 pdoc
->actualIndentInChars
= pdoc
->tabInChars
;
7277 InvalidateStyleRedraw();
7280 case SCI_GETTABWIDTH
:
7281 return pdoc
->tabInChars
;
7284 pdoc
->indentInChars
= wParam
;
7285 if (pdoc
->indentInChars
!= 0)
7286 pdoc
->actualIndentInChars
= pdoc
->indentInChars
;
7288 pdoc
->actualIndentInChars
= pdoc
->tabInChars
;
7289 InvalidateStyleRedraw();
7293 return pdoc
->indentInChars
;
7295 case SCI_SETUSETABS
:
7296 pdoc
->useTabs
= wParam
!= 0;
7297 InvalidateStyleRedraw();
7300 case SCI_GETUSETABS
:
7301 return pdoc
->useTabs
;
7303 case SCI_SETLINEINDENTATION
:
7304 pdoc
->SetLineIndentation(wParam
, lParam
);
7307 case SCI_GETLINEINDENTATION
:
7308 return pdoc
->GetLineIndentation(wParam
);
7310 case SCI_GETLINEINDENTPOSITION
:
7311 return pdoc
->GetLineIndentPosition(wParam
);
7313 case SCI_SETTABINDENTS
:
7314 pdoc
->tabIndents
= wParam
!= 0;
7317 case SCI_GETTABINDENTS
:
7318 return pdoc
->tabIndents
;
7320 case SCI_SETBACKSPACEUNINDENTS
:
7321 pdoc
->backspaceUnindents
= wParam
!= 0;
7324 case SCI_GETBACKSPACEUNINDENTS
:
7325 return pdoc
->backspaceUnindents
;
7327 case SCI_SETMOUSEDWELLTIME
:
7328 dwellDelay
= wParam
;
7329 ticksToDwell
= dwellDelay
;
7332 case SCI_GETMOUSEDWELLTIME
:
7335 case SCI_WORDSTARTPOSITION
:
7336 return pdoc
->ExtendWordSelect(wParam
, -1, lParam
!= 0);
7338 case SCI_WORDENDPOSITION
:
7339 return pdoc
->ExtendWordSelect(wParam
, 1, lParam
!= 0);
7341 case SCI_SETWRAPMODE
:
7344 wrapState
= eWrapWord
;
7347 wrapState
= eWrapChar
;
7350 wrapState
= eWrapNone
;
7354 InvalidateStyleRedraw();
7355 ReconfigureScrollBars();
7358 case SCI_GETWRAPMODE
:
7361 case SCI_SETWRAPVISUALFLAGS
:
7362 if (wrapVisualFlags
!= static_cast<int>(wParam
)) {
7363 wrapVisualFlags
= wParam
;
7364 InvalidateStyleRedraw();
7365 ReconfigureScrollBars();
7369 case SCI_GETWRAPVISUALFLAGS
:
7370 return wrapVisualFlags
;
7372 case SCI_SETWRAPVISUALFLAGSLOCATION
:
7373 wrapVisualFlagsLocation
= wParam
;
7374 InvalidateStyleRedraw();
7377 case SCI_GETWRAPVISUALFLAGSLOCATION
:
7378 return wrapVisualFlagsLocation
;
7380 case SCI_SETWRAPSTARTINDENT
:
7381 if (wrapVisualStartIndent
!= static_cast<int>(wParam
)) {
7382 wrapVisualStartIndent
= wParam
;
7383 InvalidateStyleRedraw();
7384 ReconfigureScrollBars();
7388 case SCI_GETWRAPSTARTINDENT
:
7389 return wrapVisualStartIndent
;
7391 case SCI_SETWRAPINDENTMODE
:
7392 if (wrapIndentMode
!= static_cast<int>(wParam
)) {
7393 wrapIndentMode
= wParam
;
7394 InvalidateStyleRedraw();
7395 ReconfigureScrollBars();
7399 case SCI_GETWRAPINDENTMODE
:
7400 return wrapIndentMode
;
7402 case SCI_SETLAYOUTCACHE
:
7403 llc
.SetLevel(wParam
);
7406 case SCI_GETLAYOUTCACHE
:
7407 return llc
.GetLevel();
7409 case SCI_SETPOSITIONCACHE
:
7410 posCache
.SetSize(wParam
);
7413 case SCI_GETPOSITIONCACHE
:
7414 return posCache
.GetSize();
7416 case SCI_SETSCROLLWIDTH
:
7417 PLATFORM_ASSERT(wParam
> 0);
7418 if ((wParam
> 0) && (wParam
!= static_cast<unsigned int >(scrollWidth
))) {
7419 lineWidthMaxSeen
= 0;
7420 scrollWidth
= wParam
;
7425 case SCI_GETSCROLLWIDTH
:
7428 case SCI_SETSCROLLWIDTHTRACKING
:
7429 trackLineWidth
= wParam
!= 0;
7432 case SCI_GETSCROLLWIDTHTRACKING
:
7433 return trackLineWidth
;
7439 case SCI_LINESSPLIT
:
7444 PLATFORM_ASSERT(wParam
< vs
.stylesSize
);
7445 PLATFORM_ASSERT(lParam
);
7446 return TextWidth(wParam
, CharPtrFromSPtr(lParam
));
7448 case SCI_TEXTHEIGHT
:
7449 return vs
.lineHeight
;
7451 case SCI_SETENDATLASTLINE
:
7452 PLATFORM_ASSERT((wParam
== 0) || (wParam
== 1));
7453 if (endAtLastLine
!= (wParam
!= 0)) {
7454 endAtLastLine
= wParam
!= 0;
7459 case SCI_GETENDATLASTLINE
:
7460 return endAtLastLine
;
7462 case SCI_SETCARETSTICKY
:
7463 PLATFORM_ASSERT((wParam
== 0) || (wParam
== 1));
7464 if (caretSticky
!= (wParam
!= 0)) {
7465 caretSticky
= wParam
!= 0;
7469 case SCI_GETCARETSTICKY
:
7472 case SCI_TOGGLECARETSTICKY
:
7473 caretSticky
= !caretSticky
;
7477 return pdoc
->GetColumn(wParam
);
7479 case SCI_FINDCOLUMN
:
7480 return pdoc
->FindColumn(wParam
, lParam
);
7482 case SCI_SETHSCROLLBAR
:
7483 if (horizontalScrollBarVisible
!= (wParam
!= 0)) {
7484 horizontalScrollBarVisible
= wParam
!= 0;
7486 ReconfigureScrollBars();
7490 case SCI_GETHSCROLLBAR
:
7491 return horizontalScrollBarVisible
;
7493 case SCI_SETVSCROLLBAR
:
7494 if (verticalScrollBarVisible
!= (wParam
!= 0)) {
7495 verticalScrollBarVisible
= wParam
!= 0;
7497 ReconfigureScrollBars();
7501 case SCI_GETVSCROLLBAR
:
7502 return verticalScrollBarVisible
;
7504 case SCI_SETINDENTATIONGUIDES
:
7505 vs
.viewIndentationGuides
= IndentView(wParam
);
7509 case SCI_GETINDENTATIONGUIDES
:
7510 return vs
.viewIndentationGuides
;
7512 case SCI_SETHIGHLIGHTGUIDE
:
7513 if ((highlightGuideColumn
!= static_cast<int>(wParam
)) || (wParam
> 0)) {
7514 highlightGuideColumn
= wParam
;
7519 case SCI_GETHIGHLIGHTGUIDE
:
7520 return highlightGuideColumn
;
7522 case SCI_GETLINEENDPOSITION
:
7523 return pdoc
->LineEnd(wParam
);
7525 case SCI_SETCODEPAGE
:
7526 if (ValidCodePage(wParam
)) {
7527 pdoc
->dbcsCodePage
= wParam
;
7528 InvalidateStyleRedraw();
7532 case SCI_GETCODEPAGE
:
7533 return pdoc
->dbcsCodePage
;
7535 case SCI_SETUSEPALETTE
:
7536 palette
.allowRealization
= wParam
!= 0;
7537 InvalidateStyleRedraw();
7540 case SCI_GETUSEPALETTE
:
7541 return palette
.allowRealization
;
7543 // Marker definition and setting
7544 case SCI_MARKERDEFINE
:
7545 if (wParam
<= MARKER_MAX
)
7546 vs
.markers
[wParam
].markType
= lParam
;
7547 InvalidateStyleData();
7551 case SCI_MARKERSYMBOLDEFINED
:
7552 if (wParam
<= MARKER_MAX
)
7553 return vs
.markers
[wParam
].markType
;
7557 case SCI_MARKERSETFORE
:
7558 if (wParam
<= MARKER_MAX
)
7559 vs
.markers
[wParam
].fore
.desired
= ColourDesired(lParam
);
7560 InvalidateStyleData();
7563 case SCI_MARKERSETBACK
:
7564 if (wParam
<= MARKER_MAX
)
7565 vs
.markers
[wParam
].back
.desired
= ColourDesired(lParam
);
7566 InvalidateStyleData();
7569 case SCI_MARKERSETALPHA
:
7570 if (wParam
<= MARKER_MAX
)
7571 vs
.markers
[wParam
].alpha
= lParam
;
7572 InvalidateStyleRedraw();
7574 case SCI_MARKERADD
: {
7575 int markerID
= pdoc
->AddMark(wParam
, lParam
);
7578 case SCI_MARKERADDSET
:
7580 pdoc
->AddMarkSet(wParam
, lParam
);
7583 case SCI_MARKERDELETE
:
7584 pdoc
->DeleteMark(wParam
, lParam
);
7587 case SCI_MARKERDELETEALL
:
7588 pdoc
->DeleteAllMarks(static_cast<int>(wParam
));
7592 return pdoc
->GetMark(wParam
);
7594 case SCI_MARKERNEXT
: {
7595 int lt
= pdoc
->LinesTotal();
7596 for (int iLine
= wParam
; iLine
< lt
; iLine
++) {
7597 if ((pdoc
->GetMark(iLine
) & lParam
) != 0)
7603 case SCI_MARKERPREVIOUS
: {
7604 for (int iLine
= wParam
; iLine
>= 0; iLine
--) {
7605 if ((pdoc
->GetMark(iLine
) & lParam
) != 0)
7611 case SCI_MARKERDEFINEPIXMAP
:
7612 if (wParam
<= MARKER_MAX
) {
7613 vs
.markers
[wParam
].SetXPM(CharPtrFromSPtr(lParam
));
7615 InvalidateStyleData();
7619 case SCI_SETMARGINTYPEN
:
7620 if (ValidMargin(wParam
)) {
7621 vs
.ms
[wParam
].style
= lParam
;
7622 InvalidateStyleRedraw();
7626 case SCI_GETMARGINTYPEN
:
7627 if (ValidMargin(wParam
))
7628 return vs
.ms
[wParam
].style
;
7632 case SCI_SETMARGINWIDTHN
:
7633 if (ValidMargin(wParam
)) {
7634 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
7635 if (vs
.ms
[wParam
].width
!= lParam
) {
7636 vs
.ms
[wParam
].width
= lParam
;
7637 InvalidateStyleRedraw();
7642 case SCI_GETMARGINWIDTHN
:
7643 if (ValidMargin(wParam
))
7644 return vs
.ms
[wParam
].width
;
7648 case SCI_SETMARGINMASKN
:
7649 if (ValidMargin(wParam
)) {
7650 vs
.ms
[wParam
].mask
= lParam
;
7651 InvalidateStyleRedraw();
7655 case SCI_GETMARGINMASKN
:
7656 if (ValidMargin(wParam
))
7657 return vs
.ms
[wParam
].mask
;
7661 case SCI_SETMARGINSENSITIVEN
:
7662 if (ValidMargin(wParam
)) {
7663 vs
.ms
[wParam
].sensitive
= lParam
!= 0;
7664 InvalidateStyleRedraw();
7668 case SCI_GETMARGINSENSITIVEN
:
7669 if (ValidMargin(wParam
))
7670 return vs
.ms
[wParam
].sensitive
? 1 : 0;
7674 case SCI_STYLECLEARALL
:
7676 InvalidateStyleRedraw();
7679 case SCI_STYLESETFORE
:
7680 case SCI_STYLESETBACK
:
7681 case SCI_STYLESETBOLD
:
7682 case SCI_STYLESETITALIC
:
7683 case SCI_STYLESETEOLFILLED
:
7684 case SCI_STYLESETSIZE
:
7685 case SCI_STYLESETFONT
:
7686 case SCI_STYLESETUNDERLINE
:
7687 case SCI_STYLESETCASE
:
7688 case SCI_STYLESETCHARACTERSET
:
7689 case SCI_STYLESETVISIBLE
:
7690 case SCI_STYLESETCHANGEABLE
:
7691 case SCI_STYLESETHOTSPOT
:
7692 StyleSetMessage(iMessage
, wParam
, lParam
);
7695 case SCI_STYLEGETFORE
:
7696 case SCI_STYLEGETBACK
:
7697 case SCI_STYLEGETBOLD
:
7698 case SCI_STYLEGETITALIC
:
7699 case SCI_STYLEGETEOLFILLED
:
7700 case SCI_STYLEGETSIZE
:
7701 case SCI_STYLEGETFONT
:
7702 case SCI_STYLEGETUNDERLINE
:
7703 case SCI_STYLEGETCASE
:
7704 case SCI_STYLEGETCHARACTERSET
:
7705 case SCI_STYLEGETVISIBLE
:
7706 case SCI_STYLEGETCHANGEABLE
:
7707 case SCI_STYLEGETHOTSPOT
:
7708 return StyleGetMessage(iMessage
, wParam
, lParam
);
7710 case SCI_STYLERESETDEFAULT
:
7711 vs
.ResetDefaultStyle();
7712 InvalidateStyleRedraw();
7714 case SCI_SETSTYLEBITS
:
7715 vs
.EnsureStyle((1 << wParam
) - 1);
7716 pdoc
->SetStylingBits(wParam
);
7719 case SCI_GETSTYLEBITS
:
7720 return pdoc
->stylingBits
;
7722 case SCI_SETLINESTATE
:
7723 return pdoc
->SetLineState(wParam
, lParam
);
7725 case SCI_GETLINESTATE
:
7726 return pdoc
->GetLineState(wParam
);
7728 case SCI_GETMAXLINESTATE
:
7729 return pdoc
->GetMaxLineState();
7731 case SCI_GETCARETLINEVISIBLE
:
7732 return vs
.showCaretLineBackground
;
7733 case SCI_SETCARETLINEVISIBLE
:
7734 vs
.showCaretLineBackground
= wParam
!= 0;
7735 InvalidateStyleRedraw();
7737 case SCI_GETCARETLINEBACK
:
7738 return vs
.caretLineBackground
.desired
.AsLong();
7739 case SCI_SETCARETLINEBACK
:
7740 vs
.caretLineBackground
.desired
= wParam
;
7741 InvalidateStyleRedraw();
7743 case SCI_GETCARETLINEBACKALPHA
:
7744 return vs
.caretLineAlpha
;
7745 case SCI_SETCARETLINEBACKALPHA
:
7746 vs
.caretLineAlpha
= wParam
;
7747 InvalidateStyleRedraw();
7752 case SCI_VISIBLEFROMDOCLINE
:
7753 return cs
.DisplayFromDoc(wParam
);
7755 case SCI_DOCLINEFROMVISIBLE
:
7756 return cs
.DocFromDisplay(wParam
);
7759 return WrapCount(wParam
);
7761 case SCI_SETFOLDLEVEL
: {
7762 int prev
= pdoc
->SetLevel(wParam
, lParam
);
7768 case SCI_GETFOLDLEVEL
:
7769 return pdoc
->GetLevel(wParam
);
7771 case SCI_GETLASTCHILD
:
7772 return pdoc
->GetLastChild(wParam
, lParam
);
7774 case SCI_GETFOLDPARENT
:
7775 return pdoc
->GetFoldParent(wParam
);
7778 cs
.SetVisible(wParam
, lParam
, true);
7785 cs
.SetVisible(wParam
, lParam
, false);
7790 case SCI_GETLINEVISIBLE
:
7791 return cs
.GetVisible(wParam
);
7793 case SCI_SETFOLDEXPANDED
:
7794 if (cs
.SetExpanded(wParam
, lParam
!= 0)) {
7799 case SCI_GETFOLDEXPANDED
:
7800 return cs
.GetExpanded(wParam
);
7802 case SCI_SETFOLDFLAGS
:
7807 case SCI_TOGGLEFOLD
:
7808 ToggleContraction(wParam
);
7811 case SCI_ENSUREVISIBLE
:
7812 EnsureLineVisible(wParam
, false);
7815 case SCI_ENSUREVISIBLEENFORCEPOLICY
:
7816 EnsureLineVisible(wParam
, true);
7819 case SCI_SEARCHANCHOR
:
7823 case SCI_SEARCHNEXT
:
7824 case SCI_SEARCHPREV
:
7825 return SearchText(iMessage
, wParam
, lParam
);
7827 case SCI_SETXCARETPOLICY
:
7828 caretXPolicy
= wParam
;
7829 caretXSlop
= lParam
;
7832 case SCI_SETYCARETPOLICY
:
7833 caretYPolicy
= wParam
;
7834 caretYSlop
= lParam
;
7837 case SCI_SETVISIBLEPOLICY
:
7838 visiblePolicy
= wParam
;
7839 visibleSlop
= lParam
;
7842 case SCI_LINESONSCREEN
:
7843 return LinesOnScreen();
7845 case SCI_SETSELFORE
:
7846 vs
.selforeset
= wParam
!= 0;
7847 vs
.selforeground
.desired
= ColourDesired(lParam
);
7848 vs
.selAdditionalForeground
.desired
= ColourDesired(lParam
);
7849 InvalidateStyleRedraw();
7852 case SCI_SETSELBACK
:
7853 vs
.selbackset
= wParam
!= 0;
7854 vs
.selbackground
.desired
= ColourDesired(lParam
);
7855 vs
.selAdditionalBackground
.desired
= ColourDesired(lParam
);
7856 InvalidateStyleRedraw();
7859 case SCI_SETSELALPHA
:
7860 vs
.selAlpha
= wParam
;
7861 vs
.selAdditionalAlpha
= wParam
;
7862 InvalidateStyleRedraw();
7865 case SCI_GETSELALPHA
:
7868 case SCI_GETSELEOLFILLED
:
7869 return vs
.selEOLFilled
;
7871 case SCI_SETSELEOLFILLED
:
7872 vs
.selEOLFilled
= wParam
!= 0;
7873 InvalidateStyleRedraw();
7876 case SCI_SETWHITESPACEFORE
:
7877 vs
.whitespaceForegroundSet
= wParam
!= 0;
7878 vs
.whitespaceForeground
.desired
= ColourDesired(lParam
);
7879 InvalidateStyleRedraw();
7882 case SCI_SETWHITESPACEBACK
:
7883 vs
.whitespaceBackgroundSet
= wParam
!= 0;
7884 vs
.whitespaceBackground
.desired
= ColourDesired(lParam
);
7885 InvalidateStyleRedraw();
7888 case SCI_SETCARETFORE
:
7889 vs
.caretcolour
.desired
= ColourDesired(wParam
);
7890 InvalidateStyleRedraw();
7893 case SCI_GETCARETFORE
:
7894 return vs
.caretcolour
.desired
.AsLong();
7896 case SCI_SETCARETSTYLE
:
7897 if (wParam
>= CARETSTYLE_INVISIBLE
&& wParam
<= CARETSTYLE_BLOCK
)
7898 vs
.caretStyle
= wParam
;
7900 /* Default to the line caret */
7901 vs
.caretStyle
= CARETSTYLE_LINE
;
7902 InvalidateStyleRedraw();
7905 case SCI_GETCARETSTYLE
:
7906 return vs
.caretStyle
;
7908 case SCI_SETCARETWIDTH
:
7911 else if (wParam
>= 3)
7914 vs
.caretWidth
= wParam
;
7915 InvalidateStyleRedraw();
7918 case SCI_GETCARETWIDTH
:
7919 return vs
.caretWidth
;
7921 case SCI_ASSIGNCMDKEY
:
7922 kmap
.AssignCmdKey(Platform::LowShortFromLong(wParam
),
7923 Platform::HighShortFromLong(wParam
), lParam
);
7926 case SCI_CLEARCMDKEY
:
7927 kmap
.AssignCmdKey(Platform::LowShortFromLong(wParam
),
7928 Platform::HighShortFromLong(wParam
), SCI_NULL
);
7931 case SCI_CLEARALLCMDKEYS
:
7935 case SCI_INDICSETSTYLE
:
7936 if (wParam
<= INDIC_MAX
) {
7937 vs
.indicators
[wParam
].style
= lParam
;
7938 InvalidateStyleRedraw();
7942 case SCI_INDICGETSTYLE
:
7943 return (wParam
<= INDIC_MAX
) ? vs
.indicators
[wParam
].style
: 0;
7945 case SCI_INDICSETFORE
:
7946 if (wParam
<= INDIC_MAX
) {
7947 vs
.indicators
[wParam
].fore
.desired
= ColourDesired(lParam
);
7948 InvalidateStyleRedraw();
7952 case SCI_INDICGETFORE
:
7953 return (wParam
<= INDIC_MAX
) ? vs
.indicators
[wParam
].fore
.desired
.AsLong() : 0;
7955 case SCI_INDICSETUNDER
:
7956 if (wParam
<= INDIC_MAX
) {
7957 vs
.indicators
[wParam
].under
= lParam
!= 0;
7958 InvalidateStyleRedraw();
7962 case SCI_INDICGETUNDER
:
7963 return (wParam
<= INDIC_MAX
) ? vs
.indicators
[wParam
].under
: 0;
7965 case SCI_INDICSETALPHA
:
7966 if (wParam
<= INDIC_MAX
&& lParam
>=0 && lParam
<= 100) {
7967 vs
.indicators
[wParam
].fillAlpha
= lParam
;
7968 InvalidateStyleRedraw();
7972 case SCI_INDICGETALPHA
:
7973 return (wParam
<= INDIC_MAX
) ? vs
.indicators
[wParam
].fillAlpha
: 0;
7975 case SCI_SETINDICATORCURRENT
:
7976 pdoc
->decorations
.SetCurrentIndicator(wParam
);
7978 case SCI_GETINDICATORCURRENT
:
7979 return pdoc
->decorations
.GetCurrentIndicator();
7980 case SCI_SETINDICATORVALUE
:
7981 pdoc
->decorations
.SetCurrentValue(wParam
);
7983 case SCI_GETINDICATORVALUE
:
7984 return pdoc
->decorations
.GetCurrentValue();
7986 case SCI_INDICATORFILLRANGE
:
7987 pdoc
->DecorationFillRange(wParam
, pdoc
->decorations
.GetCurrentValue(), lParam
);
7990 case SCI_INDICATORCLEARRANGE
:
7991 pdoc
->DecorationFillRange(wParam
, 0, lParam
);
7994 case SCI_INDICATORALLONFOR
:
7995 return pdoc
->decorations
.AllOnFor(wParam
);
7997 case SCI_INDICATORVALUEAT
:
7998 return pdoc
->decorations
.ValueAt(wParam
, lParam
);
8000 case SCI_INDICATORSTART
:
8001 return pdoc
->decorations
.Start(wParam
, lParam
);
8003 case SCI_INDICATOREND
:
8004 return pdoc
->decorations
.End(wParam
, lParam
);
8007 case SCI_LINEDOWNEXTEND
:
8009 case SCI_PARADOWNEXTEND
:
8011 case SCI_LINEUPEXTEND
:
8013 case SCI_PARAUPEXTEND
:
8015 case SCI_CHARLEFTEXTEND
:
8017 case SCI_CHARRIGHTEXTEND
:
8019 case SCI_WORDLEFTEXTEND
:
8021 case SCI_WORDRIGHTEXTEND
:
8022 case SCI_WORDLEFTEND
:
8023 case SCI_WORDLEFTENDEXTEND
:
8024 case SCI_WORDRIGHTEND
:
8025 case SCI_WORDRIGHTENDEXTEND
:
8027 case SCI_HOMEEXTEND
:
8029 case SCI_LINEENDEXTEND
:
8031 case SCI_HOMEWRAPEXTEND
:
8032 case SCI_LINEENDWRAP
:
8033 case SCI_LINEENDWRAPEXTEND
:
8034 case SCI_DOCUMENTSTART
:
8035 case SCI_DOCUMENTSTARTEXTEND
:
8036 case SCI_DOCUMENTEND
:
8037 case SCI_DOCUMENTENDEXTEND
:
8039 case SCI_STUTTEREDPAGEUP
:
8040 case SCI_STUTTEREDPAGEUPEXTEND
:
8041 case SCI_STUTTEREDPAGEDOWN
:
8042 case SCI_STUTTEREDPAGEDOWNEXTEND
:
8045 case SCI_PAGEUPEXTEND
:
8047 case SCI_PAGEDOWNEXTEND
:
8048 case SCI_EDITTOGGLEOVERTYPE
:
8050 case SCI_DELETEBACK
:
8056 case SCI_VCHOMEEXTEND
:
8057 case SCI_VCHOMEWRAP
:
8058 case SCI_VCHOMEWRAPEXTEND
:
8061 case SCI_DELWORDLEFT
:
8062 case SCI_DELWORDRIGHT
:
8063 case SCI_DELWORDRIGHTEND
:
8064 case SCI_DELLINELEFT
:
8065 case SCI_DELLINERIGHT
:
8068 case SCI_LINEDELETE
:
8069 case SCI_LINETRANSPOSE
:
8070 case SCI_LINEDUPLICATE
:
8073 case SCI_LINESCROLLDOWN
:
8074 case SCI_LINESCROLLUP
:
8075 case SCI_WORDPARTLEFT
:
8076 case SCI_WORDPARTLEFTEXTEND
:
8077 case SCI_WORDPARTRIGHT
:
8078 case SCI_WORDPARTRIGHTEXTEND
:
8079 case SCI_DELETEBACKNOTLINE
:
8080 case SCI_HOMEDISPLAY
:
8081 case SCI_HOMEDISPLAYEXTEND
:
8082 case SCI_LINEENDDISPLAY
:
8083 case SCI_LINEENDDISPLAYEXTEND
:
8084 case SCI_LINEDOWNRECTEXTEND
:
8085 case SCI_LINEUPRECTEXTEND
:
8086 case SCI_CHARLEFTRECTEXTEND
:
8087 case SCI_CHARRIGHTRECTEXTEND
:
8088 case SCI_HOMERECTEXTEND
:
8089 case SCI_VCHOMERECTEXTEND
:
8090 case SCI_LINEENDRECTEXTEND
:
8091 case SCI_PAGEUPRECTEXTEND
:
8092 case SCI_PAGEDOWNRECTEXTEND
:
8093 case SCI_SELECTIONDUPLICATE
:
8094 return KeyCommand(iMessage
);
8096 case SCI_BRACEHIGHLIGHT
:
8097 SetBraceHighlight(static_cast<int>(wParam
), lParam
, STYLE_BRACELIGHT
);
8100 case SCI_BRACEBADLIGHT
:
8101 SetBraceHighlight(static_cast<int>(wParam
), -1, STYLE_BRACEBAD
);
8104 case SCI_BRACEMATCH
:
8105 // wParam is position of char to find brace for,
8106 // lParam is maximum amount of text to restyle to find it
8107 return pdoc
->BraceMatch(wParam
, lParam
);
8109 case SCI_GETVIEWEOL
:
8112 case SCI_SETVIEWEOL
:
8113 vs
.viewEOL
= wParam
!= 0;
8114 InvalidateStyleRedraw();
8118 vs
.zoomLevel
= wParam
;
8119 InvalidateStyleRedraw();
8124 return vs
.zoomLevel
;
8126 case SCI_GETEDGECOLUMN
:
8129 case SCI_SETEDGECOLUMN
:
8131 InvalidateStyleRedraw();
8134 case SCI_GETEDGEMODE
:
8135 return vs
.edgeState
;
8137 case SCI_SETEDGEMODE
:
8138 vs
.edgeState
= wParam
;
8139 InvalidateStyleRedraw();
8142 case SCI_GETEDGECOLOUR
:
8143 return vs
.edgecolour
.desired
.AsLong();
8145 case SCI_SETEDGECOLOUR
:
8146 vs
.edgecolour
.desired
= ColourDesired(wParam
);
8147 InvalidateStyleRedraw();
8150 case SCI_GETDOCPOINTER
:
8151 return reinterpret_cast<sptr_t
>(pdoc
);
8153 case SCI_SETDOCPOINTER
:
8155 SetDocPointer(reinterpret_cast<Document
*>(lParam
));
8158 case SCI_CREATEDOCUMENT
: {
8159 Document
*doc
= new Document();
8163 return reinterpret_cast<sptr_t
>(doc
);
8166 case SCI_ADDREFDOCUMENT
:
8167 (reinterpret_cast<Document
*>(lParam
))->AddRef();
8170 case SCI_RELEASEDOCUMENT
:
8171 (reinterpret_cast<Document
*>(lParam
))->Release();
8174 case SCI_SETMODEVENTMASK
:
8175 modEventMask
= wParam
;
8178 case SCI_GETMODEVENTMASK
:
8179 return modEventMask
;
8181 case SCI_CONVERTEOLS
:
8182 pdoc
->ConvertLineEnds(wParam
);
8183 SetSelection(sel
.MainCaret(), sel
.MainAnchor()); // Ensure selection inside document
8186 case SCI_SETLENGTHFORENCODE
:
8187 lengthForEncode
= wParam
;
8190 case SCI_SELECTIONISRECTANGLE
:
8191 return sel
.selType
== Selection::selRectangle
? 1 : 0;
8193 case SCI_SETSELECTIONMODE
: {
8196 sel
.SetMoveExtends(!sel
.MoveExtends() || (sel
.selType
!= Selection::selStream
));
8197 sel
.selType
= Selection::selStream
;
8199 case SC_SEL_RECTANGLE
:
8200 sel
.SetMoveExtends(!sel
.MoveExtends() || (sel
.selType
!= Selection::selRectangle
));
8201 sel
.selType
= Selection::selRectangle
;
8204 sel
.SetMoveExtends(!sel
.MoveExtends() || (sel
.selType
!= Selection::selLines
));
8205 sel
.selType
= Selection::selLines
;
8208 sel
.SetMoveExtends(!sel
.MoveExtends() || (sel
.selType
!= Selection::selThin
));
8209 sel
.selType
= Selection::selThin
;
8212 sel
.SetMoveExtends(!sel
.MoveExtends() || (sel
.selType
!= Selection::selStream
));
8213 sel
.selType
= Selection::selStream
;
8215 InvalidateSelection(sel
.RangeMain(), true);
8217 case SCI_GETSELECTIONMODE
:
8218 switch (sel
.selType
) {
8219 case Selection::selStream
:
8220 return SC_SEL_STREAM
;
8221 case Selection::selRectangle
:
8222 return SC_SEL_RECTANGLE
;
8223 case Selection::selLines
:
8224 return SC_SEL_LINES
;
8225 case Selection::selThin
:
8228 return SC_SEL_STREAM
;
8230 case SCI_GETLINESELSTARTPOSITION
:
8231 case SCI_GETLINESELENDPOSITION
: {
8232 SelectionSegment
segmentLine(SelectionPosition(pdoc
->LineStart(wParam
)),
8233 SelectionPosition(pdoc
->LineEnd(wParam
)));
8234 for (size_t r
=0; r
<sel
.Count(); r
++) {
8235 SelectionSegment portion
= sel
.Range(r
).Intersect(segmentLine
);
8236 if (portion
.start
.IsValid()) {
8237 return (iMessage
== SCI_GETLINESELSTARTPOSITION
) ? portion
.start
.Position() : portion
.end
.Position();
8240 return INVALID_POSITION
;
8243 case SCI_SETOVERTYPE
:
8244 inOverstrike
= wParam
!= 0;
8247 case SCI_GETOVERTYPE
:
8248 return inOverstrike
? 1 : 0;
8251 SetFocusState(wParam
!= 0);
8258 errorStatus
= wParam
;
8264 case SCI_SETMOUSEDOWNCAPTURES
:
8265 mouseDownCaptures
= wParam
!= 0;
8268 case SCI_GETMOUSEDOWNCAPTURES
:
8269 return mouseDownCaptures
;
8272 cursorMode
= wParam
;
8273 DisplayCursor(Window::cursorText
);
8279 case SCI_SETCONTROLCHARSYMBOL
:
8280 controlCharSymbol
= wParam
;
8283 case SCI_GETCONTROLCHARSYMBOL
:
8284 return controlCharSymbol
;
8286 case SCI_STARTRECORD
:
8287 recordingMacro
= true;
8290 case SCI_STOPRECORD
:
8291 recordingMacro
= false;
8294 case SCI_MOVECARETINSIDEVIEW
:
8295 MoveCaretInsideView();
8298 case SCI_SETFOLDMARGINCOLOUR
:
8299 vs
.foldmarginColourSet
= wParam
!= 0;
8300 vs
.foldmarginColour
.desired
= ColourDesired(lParam
);
8301 InvalidateStyleRedraw();
8304 case SCI_SETFOLDMARGINHICOLOUR
:
8305 vs
.foldmarginHighlightColourSet
= wParam
!= 0;
8306 vs
.foldmarginHighlightColour
.desired
= ColourDesired(lParam
);
8307 InvalidateStyleRedraw();
8310 case SCI_SETHOTSPOTACTIVEFORE
:
8311 vs
.hotspotForegroundSet
= wParam
!= 0;
8312 vs
.hotspotForeground
.desired
= ColourDesired(lParam
);
8313 InvalidateStyleRedraw();
8316 case SCI_GETHOTSPOTACTIVEFORE
:
8317 return vs
.hotspotForeground
.desired
.AsLong();
8319 case SCI_SETHOTSPOTACTIVEBACK
:
8320 vs
.hotspotBackgroundSet
= wParam
!= 0;
8321 vs
.hotspotBackground
.desired
= ColourDesired(lParam
);
8322 InvalidateStyleRedraw();
8325 case SCI_GETHOTSPOTACTIVEBACK
:
8326 return vs
.hotspotBackground
.desired
.AsLong();
8328 case SCI_SETHOTSPOTACTIVEUNDERLINE
:
8329 vs
.hotspotUnderline
= wParam
!= 0;
8330 InvalidateStyleRedraw();
8333 case SCI_GETHOTSPOTACTIVEUNDERLINE
:
8334 return vs
.hotspotUnderline
? 1 : 0;
8336 case SCI_SETHOTSPOTSINGLELINE
:
8337 vs
.hotspotSingleLine
= wParam
!= 0;
8338 InvalidateStyleRedraw();
8341 case SCI_GETHOTSPOTSINGLELINE
:
8342 return vs
.hotspotSingleLine
? 1 : 0;
8344 case SCI_SETPASTECONVERTENDINGS
:
8345 convertPastes
= wParam
!= 0;
8348 case SCI_GETPASTECONVERTENDINGS
:
8349 return convertPastes
? 1 : 0;
8351 case SCI_GETCHARACTERPOINTER
:
8352 return reinterpret_cast<sptr_t
>(pdoc
->BufferPointer());
8354 case SCI_SETEXTRAASCENT
:
8355 vs
.extraAscent
= wParam
;
8356 InvalidateStyleRedraw();
8359 case SCI_GETEXTRAASCENT
:
8360 return vs
.extraAscent
;
8362 case SCI_SETEXTRADESCENT
:
8363 vs
.extraDescent
= wParam
;
8364 InvalidateStyleRedraw();
8367 case SCI_GETEXTRADESCENT
:
8368 return vs
.extraDescent
;
8370 case SCI_MARGINSETSTYLEOFFSET
:
8371 vs
.marginStyleOffset
= wParam
;
8372 InvalidateStyleRedraw();
8375 case SCI_MARGINGETSTYLEOFFSET
:
8376 return vs
.marginStyleOffset
;
8378 case SCI_MARGINSETTEXT
:
8379 pdoc
->MarginSetText(wParam
, CharPtrFromSPtr(lParam
));
8382 case SCI_MARGINGETTEXT
: {
8383 const StyledText st
= pdoc
->MarginStyledText(wParam
);
8386 memcpy(CharPtrFromSPtr(lParam
), st
.text
, st
.length
);
8388 strcpy(CharPtrFromSPtr(lParam
), "");
8393 case SCI_MARGINSETSTYLE
:
8394 pdoc
->MarginSetStyle(wParam
, lParam
);
8397 case SCI_MARGINGETSTYLE
: {
8398 const StyledText st
= pdoc
->MarginStyledText(wParam
);
8402 case SCI_MARGINSETSTYLES
:
8403 pdoc
->MarginSetStyles(wParam
, reinterpret_cast<const unsigned char *>(lParam
));
8406 case SCI_MARGINGETSTYLES
: {
8407 const StyledText st
= pdoc
->MarginStyledText(wParam
);
8410 memcpy(CharPtrFromSPtr(lParam
), st
.styles
, st
.length
);
8412 strcpy(CharPtrFromSPtr(lParam
), "");
8414 return st
.styles
? st
.length
: 0;
8417 case SCI_MARGINTEXTCLEARALL
:
8418 pdoc
->MarginClearAll();
8421 case SCI_ANNOTATIONSETTEXT
:
8422 pdoc
->AnnotationSetText(wParam
, CharPtrFromSPtr(lParam
));
8425 case SCI_ANNOTATIONGETTEXT
: {
8426 const StyledText st
= pdoc
->AnnotationStyledText(wParam
);
8429 memcpy(CharPtrFromSPtr(lParam
), st
.text
, st
.length
);
8431 strcpy(CharPtrFromSPtr(lParam
), "");
8436 case SCI_ANNOTATIONGETSTYLE
: {
8437 const StyledText st
= pdoc
->AnnotationStyledText(wParam
);
8441 case SCI_ANNOTATIONSETSTYLE
:
8442 pdoc
->AnnotationSetStyle(wParam
, lParam
);
8445 case SCI_ANNOTATIONSETSTYLES
:
8446 pdoc
->AnnotationSetStyles(wParam
, reinterpret_cast<const unsigned char *>(lParam
));
8449 case SCI_ANNOTATIONGETSTYLES
: {
8450 const StyledText st
= pdoc
->AnnotationStyledText(wParam
);
8453 memcpy(CharPtrFromSPtr(lParam
), st
.styles
, st
.length
);
8455 strcpy(CharPtrFromSPtr(lParam
), "");
8457 return st
.styles
? st
.length
: 0;
8460 case SCI_ANNOTATIONGETLINES
:
8461 return pdoc
->AnnotationLines(wParam
);
8463 case SCI_ANNOTATIONCLEARALL
:
8464 pdoc
->AnnotationClearAll();
8467 case SCI_ANNOTATIONSETVISIBLE
:
8468 SetAnnotationVisible(wParam
);
8471 case SCI_ANNOTATIONGETVISIBLE
:
8472 return vs
.annotationVisible
;
8474 case SCI_ANNOTATIONSETSTYLEOFFSET
:
8475 vs
.annotationStyleOffset
= wParam
;
8476 InvalidateStyleRedraw();
8479 case SCI_ANNOTATIONGETSTYLEOFFSET
:
8480 return vs
.annotationStyleOffset
;
8482 case SCI_ADDUNDOACTION
:
8483 pdoc
->AddUndoAction(wParam
, lParam
& UNDO_MAY_COALESCE
);
8486 case SCI_SETMULTIPLESELECTION
:
8487 multipleSelection
= wParam
!= 0;
8491 case SCI_GETMULTIPLESELECTION
:
8492 return multipleSelection
;
8494 case SCI_SETADDITIONALSELECTIONTYPING
:
8495 additionalSelectionTyping
= wParam
!= 0;
8499 case SCI_GETADDITIONALSELECTIONTYPING
:
8500 return additionalSelectionTyping
;
8502 case SCI_SETMULTIPASTE
:
8503 multiPasteMode
= wParam
;
8506 case SCI_GETMULTIPASTE
:
8507 return multiPasteMode
;
8509 case SCI_SETADDITIONALCARETSBLINK
:
8510 additionalCaretsBlink
= wParam
!= 0;
8514 case SCI_GETADDITIONALCARETSBLINK
:
8515 return additionalCaretsBlink
;
8517 case SCI_SETADDITIONALCARETSVISIBLE
:
8518 additionalCaretsVisible
= wParam
!= 0;
8522 case SCI_GETADDITIONALCARETSVISIBLE
:
8523 return additionalCaretsVisible
;
8525 case SCI_GETSELECTIONS
:
8528 case SCI_CLEARSELECTIONS
:
8533 case SCI_SETSELECTION
:
8534 sel
.SetSelection(SelectionRange(wParam
, lParam
));
8538 case SCI_ADDSELECTION
:
8539 sel
.AddSelection(SelectionRange(wParam
, lParam
));
8543 case SCI_SETMAINSELECTION
:
8544 sel
.SetMain(wParam
);
8548 case SCI_GETMAINSELECTION
:
8551 case SCI_SETSELECTIONNCARET
:
8552 sel
.Range(wParam
).caret
.SetPosition(lParam
);
8556 case SCI_GETSELECTIONNCARET
:
8557 return sel
.Range(wParam
).caret
.Position();
8559 case SCI_SETSELECTIONNANCHOR
:
8560 sel
.Range(wParam
).anchor
.SetPosition(lParam
);
8563 case SCI_GETSELECTIONNANCHOR
:
8564 return sel
.Range(wParam
).anchor
.Position();
8566 case SCI_SETSELECTIONNCARETVIRTUALSPACE
:
8567 sel
.Range(wParam
).caret
.SetVirtualSpace(lParam
);
8571 case SCI_GETSELECTIONNCARETVIRTUALSPACE
:
8572 return sel
.Range(wParam
).caret
.VirtualSpace();
8574 case SCI_SETSELECTIONNANCHORVIRTUALSPACE
:
8575 sel
.Range(wParam
).anchor
.SetVirtualSpace(lParam
);
8579 case SCI_GETSELECTIONNANCHORVIRTUALSPACE
:
8580 return sel
.Range(wParam
).anchor
.VirtualSpace();
8582 case SCI_SETSELECTIONNSTART
:
8583 sel
.Range(wParam
).anchor
.SetPosition(lParam
);
8587 case SCI_GETSELECTIONNSTART
:
8588 return sel
.Range(wParam
).Start().Position();
8590 case SCI_SETSELECTIONNEND
:
8591 sel
.Range(wParam
).caret
.SetPosition(lParam
);
8595 case SCI_GETSELECTIONNEND
:
8596 return sel
.Range(wParam
).End().Position();
8598 case SCI_SETRECTANGULARSELECTIONCARET
:
8599 if (!sel
.IsRectangular())
8601 sel
.selType
= Selection::selRectangle
;
8602 sel
.Rectangular().caret
.SetPosition(wParam
);
8603 SetRectangularRange();
8607 case SCI_GETRECTANGULARSELECTIONCARET
:
8608 return sel
.Rectangular().caret
.Position();
8610 case SCI_SETRECTANGULARSELECTIONANCHOR
:
8611 if (!sel
.IsRectangular())
8613 sel
.selType
= Selection::selRectangle
;
8614 sel
.Rectangular().anchor
.SetPosition(wParam
);
8615 SetRectangularRange();
8619 case SCI_GETRECTANGULARSELECTIONANCHOR
:
8620 return sel
.Rectangular().anchor
.Position();
8622 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE
:
8623 if (!sel
.IsRectangular())
8625 sel
.selType
= Selection::selRectangle
;
8626 sel
.Rectangular().caret
.SetVirtualSpace(wParam
);
8627 SetRectangularRange();
8631 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE
:
8632 return sel
.Rectangular().caret
.VirtualSpace();
8634 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE
:
8635 if (!sel
.IsRectangular())
8637 sel
.selType
= Selection::selRectangle
;
8638 sel
.Rectangular().anchor
.SetVirtualSpace(wParam
);
8639 SetRectangularRange();
8643 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE
:
8644 return sel
.Rectangular().anchor
.VirtualSpace();
8646 case SCI_SETVIRTUALSPACEOPTIONS
:
8647 virtualSpaceOptions
= wParam
;
8650 case SCI_GETVIRTUALSPACEOPTIONS
:
8651 return virtualSpaceOptions
;
8653 case SCI_SETADDITIONALSELFORE
:
8654 vs
.selAdditionalForeground
.desired
= ColourDesired(wParam
);
8655 InvalidateStyleRedraw();
8658 case SCI_SETADDITIONALSELBACK
:
8659 vs
.selAdditionalBackground
.desired
= ColourDesired(wParam
);
8660 InvalidateStyleRedraw();
8663 case SCI_SETADDITIONALSELALPHA
:
8664 vs
.selAdditionalAlpha
= wParam
;
8665 InvalidateStyleRedraw();
8668 case SCI_GETADDITIONALSELALPHA
:
8669 return vs
.selAdditionalAlpha
;
8671 case SCI_SETADDITIONALCARETFORE
:
8672 vs
.additionalCaretColour
.desired
= ColourDesired(wParam
);
8673 InvalidateStyleRedraw();
8676 case SCI_GETADDITIONALCARETFORE
:
8677 return vs
.additionalCaretColour
.desired
.AsLong();
8679 case SCI_ROTATESELECTION
:
8681 InvalidateSelection(sel
.RangeMain(), true);
8684 case SCI_SWAPMAINANCHORCARET
:
8685 InvalidateSelection(sel
.RangeMain());
8686 sel
.RangeMain() = SelectionRange(sel
.RangeMain().anchor
, sel
.RangeMain().caret
);
8690 return DefWndProc(iMessage
, wParam
, lParam
);
8692 //Platform::DebugPrintf("end wnd proc\n");