Minor edits of Template Wildcards table:
[geany-mirror.git] / scintilla / Editor.cxx
blob5797715df17a10fe4d14715c57c9f700db6a8fb0
1 // Scintilla source code edit control
2 /** @file Editor.cxx
3 ** Main code for the edit control.
4 **/
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.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <ctype.h>
13 #include <string>
14 #include <vector>
15 #include <algorithm>
17 // With Borland C++ 5.5, including <string> includes Windows.h leading to defining
18 // FindText to FindTextA which makes calls here to Document::FindText fail.
19 #ifdef __BORLANDC__
20 #ifdef FindText
21 #undef FindText
22 #endif
23 #endif
25 #include "Platform.h"
27 #include "Scintilla.h"
29 #include "SplitVector.h"
30 #include "Partitioning.h"
31 #include "RunStyles.h"
32 #include "ContractionState.h"
33 #include "CellBuffer.h"
34 #include "KeyMap.h"
35 #include "Indicator.h"
36 #include "XPM.h"
37 #include "LineMarker.h"
38 #include "Style.h"
39 #include "ViewStyle.h"
40 #include "CharClassify.h"
41 #include "Decoration.h"
42 #include "Document.h"
43 #include "Selection.h"
44 #include "PositionCache.h"
45 #include "Editor.h"
47 #ifdef SCI_NAMESPACE
48 using namespace Scintilla;
49 #endif
52 return whether this modification represents an operation that
53 may reasonably be deferred (not done now OR [possibly] at all)
55 static bool CanDeferToLastStep(const DocModification& mh) {
56 if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
57 return true; // CAN skip
58 if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)))
59 return false; // MUST do
60 if (mh.modificationType & SC_MULTISTEPUNDOREDO)
61 return true; // CAN skip
62 return false; // PRESUMABLY must do
65 static bool CanEliminate(const DocModification& mh) {
66 return
67 (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
71 return whether this modification represents the FINAL step
72 in a [possibly lengthy] multi-step Undo/Redo sequence
74 static bool IsLastStep(const DocModification& mh) {
75 return
76 (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0
77 && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
78 && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
79 && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
82 Caret::Caret() :
83 active(false), on(false), period(500) {}
85 Timer::Timer() :
86 ticking(false), ticksToWait(0), tickerID(0) {}
88 Idler::Idler() :
89 state(false), idlerID(0) {}
91 static inline bool IsControlCharacter(int ch) {
92 // iscntrl returns true for lots of chars > 127 which are displayable
93 return ch >= 0 && ch < ' ';
96 Editor::Editor() {
97 ctrlID = 0;
99 stylesValid = false;
101 printMagnification = 0;
102 printColourMode = SC_PRINT_NORMAL;
103 printWrapState = eWrapWord;
104 cursorMode = SC_CURSORNORMAL;
105 controlCharSymbol = 0; /* Draw the control characters */
107 hasFocus = false;
108 hideSelection = false;
109 inOverstrike = false;
110 errorStatus = 0;
111 mouseDownCaptures = true;
113 bufferedDraw = true;
114 twoPhaseDraw = true;
116 lastClickTime = 0;
117 dwellDelay = SC_TIME_FOREVER;
118 ticksToDwell = SC_TIME_FOREVER;
119 dwelling = false;
120 ptMouseLast.x = 0;
121 ptMouseLast.y = 0;
122 inDragDrop = ddNone;
123 dropWentOutside = false;
124 posDrag = SelectionPosition(invalidPosition);
125 posDrop = SelectionPosition(invalidPosition);
126 selectionType = selChar;
128 lastXChosen = 0;
129 lineAnchor = 0;
130 originalAnchorPos = 0;
132 primarySelection = true;
134 caretXPolicy = CARET_SLOP | CARET_EVEN;
135 caretXSlop = 50;
137 caretYPolicy = CARET_EVEN;
138 caretYSlop = 0;
140 searchAnchor = 0;
142 xOffset = 0;
143 xCaretMargin = 50;
144 horizontalScrollBarVisible = true;
145 scrollWidth = 2000;
146 trackLineWidth = false;
147 lineWidthMaxSeen = 0;
148 verticalScrollBarVisible = true;
149 endAtLastLine = true;
150 caretSticky = false;
151 multipleSelection = false;
152 additionalSelectionTyping = false;
153 additionalCaretsBlink = true;
154 additionalCaretsVisible = true;
155 virtualSpaceOptions = SCVS_NONE;
157 pixmapLine = Surface::Allocate();
158 pixmapSelMargin = Surface::Allocate();
159 pixmapSelPattern = Surface::Allocate();
160 pixmapIndentGuide = Surface::Allocate();
161 pixmapIndentGuideHighlight = Surface::Allocate();
163 targetStart = 0;
164 targetEnd = 0;
165 searchFlags = 0;
167 topLine = 0;
168 posTopLine = 0;
170 lengthForEncode = -1;
172 needUpdateUI = true;
173 braces[0] = invalidPosition;
174 braces[1] = invalidPosition;
175 bracesMatchStyle = STYLE_BRACEBAD;
176 highlightGuideColumn = 0;
178 theEdge = 0;
180 paintState = notPainting;
182 modEventMask = SC_MODEVENTMASKALL;
184 pdoc = new Document();
185 pdoc->AddRef();
186 pdoc->AddWatcher(this, 0);
188 recordingMacro = false;
189 foldFlags = 0;
191 wrapState = eWrapNone;
192 wrapWidth = LineLayout::wrapWidthInfinite;
193 wrapStart = wrapLineLarge;
194 wrapEnd = wrapLineLarge;
195 wrapVisualFlags = 0;
196 wrapVisualFlagsLocation = 0;
197 wrapVisualStartIndent = 0;
198 wrapIndentMode = SC_WRAPINDENT_FIXED;
199 wrapAddIndent = 0;
201 convertPastes = true;
203 hsStart = -1;
204 hsEnd = -1;
206 llc.SetLevel(LineLayoutCache::llcCaret);
207 posCache.SetSize(0x400);
210 Editor::~Editor() {
211 pdoc->RemoveWatcher(this, 0);
212 pdoc->Release();
213 pdoc = 0;
214 DropGraphics();
215 delete pixmapLine;
216 delete pixmapSelMargin;
217 delete pixmapSelPattern;
218 delete pixmapIndentGuide;
219 delete pixmapIndentGuideHighlight;
222 void Editor::Finalise() {
223 SetIdle(false);
224 CancelModes();
227 void Editor::DropGraphics() {
228 pixmapLine->Release();
229 pixmapSelMargin->Release();
230 pixmapSelPattern->Release();
231 pixmapIndentGuide->Release();
232 pixmapIndentGuideHighlight->Release();
235 void Editor::InvalidateStyleData() {
236 stylesValid = false;
237 DropGraphics();
238 palette.Release();
239 llc.Invalidate(LineLayout::llInvalid);
240 posCache.Clear();
243 void Editor::InvalidateStyleRedraw() {
244 NeedWrapping();
245 InvalidateStyleData();
246 Redraw();
249 void Editor::RefreshColourPalette(Palette &pal, bool want) {
250 vs.RefreshColourPalette(pal, want);
253 void Editor::RefreshStyleData() {
254 if (!stylesValid) {
255 stylesValid = true;
256 AutoSurface surface(this);
257 if (surface) {
258 vs.Refresh(*surface);
259 RefreshColourPalette(palette, true);
260 palette.Allocate(wMain);
261 RefreshColourPalette(palette, false);
263 if (wrapIndentMode == SC_WRAPINDENT_INDENT) {
264 wrapAddIndent = pdoc->IndentSize() * vs.spaceWidth;
265 } else if (wrapIndentMode == SC_WRAPINDENT_SAME) {
266 wrapAddIndent = 0;
267 } else { //SC_WRAPINDENT_FIXED
268 wrapAddIndent = wrapVisualStartIndent * vs.aveCharWidth;
269 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (wrapAddIndent <= 0))
270 wrapAddIndent = vs.aveCharWidth; // must indent to show start visual
272 SetScrollBars();
273 SetRectangularRange();
277 PRectangle Editor::GetClientRectangle() {
278 return wMain.GetClientPosition();
281 PRectangle Editor::GetTextRectangle() {
282 PRectangle rc = GetClientRectangle();
283 rc.left += vs.fixedColumnWidth;
284 rc.right -= vs.rightMarginWidth;
285 return rc;
288 int Editor::LinesOnScreen() {
289 PRectangle rcClient = GetClientRectangle();
290 int htClient = rcClient.bottom - rcClient.top;
291 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
292 return htClient / vs.lineHeight;
295 int Editor::LinesToScroll() {
296 int retVal = LinesOnScreen() - 1;
297 if (retVal < 1)
298 return 1;
299 else
300 return retVal;
303 int Editor::MaxScrollPos() {
304 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
305 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
306 int retVal = cs.LinesDisplayed();
307 if (endAtLastLine) {
308 retVal -= LinesOnScreen();
309 } else {
310 retVal--;
312 if (retVal < 0) {
313 return 0;
314 } else {
315 return retVal;
319 const char *ControlCharacterString(unsigned char ch) {
320 const char *reps[] = {
321 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
322 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
323 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
324 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
326 if (ch < (sizeof(reps) / sizeof(reps[0]))) {
327 return reps[ch];
328 } else {
329 return "BAD";
334 * Convenience class to ensure LineLayout objects are always disposed.
336 class AutoLineLayout {
337 LineLayoutCache &llc;
338 LineLayout *ll;
339 AutoLineLayout &operator=(const AutoLineLayout &);
340 public:
341 AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
342 ~AutoLineLayout() {
343 llc.Dispose(ll);
344 ll = 0;
346 LineLayout *operator->() const {
347 return ll;
349 operator LineLayout *() const {
350 return ll;
352 void Set(LineLayout *ll_) {
353 llc.Dispose(ll);
354 ll = ll_;
358 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
359 if (sp.Position() < 0) {
360 return SelectionPosition(0);
361 } else if (sp.Position() > pdoc->Length()) {
362 return SelectionPosition(pdoc->Length());
363 } else {
364 // If not at end of line then set offset to 0
365 if (!pdoc->IsLineEndPosition(sp.Position()))
366 sp.SetVirtualSpace(0);
367 return sp;
371 Point Editor::LocationFromPosition(SelectionPosition pos) {
372 Point pt;
373 RefreshStyleData();
374 if (pos.Position() == INVALID_POSITION)
375 return pt;
376 int line = pdoc->LineFromPosition(pos.Position());
377 int lineVisible = cs.DisplayFromDoc(line);
378 //Platform::DebugPrintf("line=%d\n", line);
379 AutoSurface surface(this);
380 AutoLineLayout ll(llc, RetrieveLineLayout(line));
381 if (surface && ll) {
382 // -1 because of adding in for visible lines in following loop.
383 pt.y = (lineVisible - topLine - 1) * vs.lineHeight;
384 pt.x = 0;
385 unsigned int posLineStart = pdoc->LineStart(line);
386 LayoutLine(line, surface, vs, ll, wrapWidth);
387 int posInLine = pos.Position() - posLineStart;
388 // In case of very long line put x at arbitrary large position
389 if (posInLine > ll->maxLineLength) {
390 pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->LineStart(ll->lines)];
393 for (int subLine = 0; subLine < ll->lines; subLine++) {
394 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
395 pt.x = ll->positions[posInLine] - ll->positions[ll->LineStart(subLine)];
396 if (ll->wrapIndent != 0) {
397 int lineStart = ll->LineStart(subLine);
398 if (lineStart != 0) // Wrapped
399 pt.x += ll->wrapIndent;
402 if (posInLine >= ll->LineStart(subLine)) {
403 pt.y += vs.lineHeight;
406 pt.x += vs.fixedColumnWidth - xOffset;
408 pt.x += pos.VirtualSpace() * static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
409 return pt;
412 Point Editor::LocationFromPosition(int pos) {
413 return LocationFromPosition(SelectionPosition(pos));
416 int Editor::XFromPosition(int pos) {
417 Point pt = LocationFromPosition(pos);
418 return pt.x - vs.fixedColumnWidth + xOffset;
421 int Editor::XFromPosition(SelectionPosition sp) {
422 Point pt = LocationFromPosition(sp);
423 return pt.x - vs.fixedColumnWidth + xOffset;
426 int Editor::LineFromLocation(Point pt) {
427 return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
430 void Editor::SetTopLine(int topLineNew) {
431 topLine = topLineNew;
432 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
435 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
436 RefreshStyleData();
437 if (canReturnInvalid) {
438 PRectangle rcClient = GetTextRectangle();
439 if (!rcClient.Contains(pt))
440 return SelectionPosition(INVALID_POSITION);
441 if (pt.x < vs.fixedColumnWidth)
442 return SelectionPosition(INVALID_POSITION);
443 if (pt.y < 0)
444 return SelectionPosition(INVALID_POSITION);
446 pt.x = pt.x - vs.fixedColumnWidth + xOffset;
447 int visibleLine = pt.y / vs.lineHeight + topLine;
448 if (pt.y < 0) { // Division rounds towards 0
449 visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine;
451 if (!canReturnInvalid && (visibleLine < 0))
452 visibleLine = 0;
453 int lineDoc = cs.DocFromDisplay(visibleLine);
454 if (canReturnInvalid && (lineDoc < 0))
455 return SelectionPosition(INVALID_POSITION);
456 if (lineDoc >= pdoc->LinesTotal())
457 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length());
458 unsigned int posLineStart = pdoc->LineStart(lineDoc);
459 SelectionPosition retVal(canReturnInvalid ? INVALID_POSITION : static_cast<int>(posLineStart));
460 AutoSurface surface(this);
461 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
462 if (surface && ll) {
463 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
464 int lineStartSet = cs.DisplayFromDoc(lineDoc);
465 int subLine = visibleLine - lineStartSet;
466 if (subLine < ll->lines) {
467 int lineStart = ll->LineStart(subLine);
468 int lineEnd = ll->LineLastVisible(subLine);
469 int subLineStart = ll->positions[lineStart];
471 if (ll->wrapIndent != 0) {
472 if (lineStart != 0) // Wrapped
473 pt.x -= ll->wrapIndent;
475 int i = ll->FindBefore(pt.x + subLineStart, lineStart, lineEnd);
476 while (i < lineEnd) {
477 if (charPosition) {
478 if ((pt.x + subLineStart) < (ll->positions[i + 1])) {
479 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
481 } else {
482 if ((pt.x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
483 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
486 i++;
488 if (virtualSpace) {
489 const int spaceWidth = static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
490 int spaceOffset = (pt.x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) /
491 spaceWidth;
492 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
493 } else if (canReturnInvalid) {
494 if (pt.x < (ll->positions[lineEnd] - subLineStart)) {
495 return SelectionPosition(pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1));
497 } else {
498 return SelectionPosition(lineEnd + posLineStart);
501 if (!canReturnInvalid)
502 return SelectionPosition(ll->numCharsInLine + posLineStart);
504 return retVal;
507 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
508 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
512 * Find the document position corresponding to an x coordinate on a particular document line.
513 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
515 int Editor::PositionFromLineX(int lineDoc, int x) {
516 RefreshStyleData();
517 if (lineDoc >= pdoc->LinesTotal())
518 return pdoc->Length();
519 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
520 AutoSurface surface(this);
521 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
522 int retVal = 0;
523 if (surface && ll) {
524 unsigned int posLineStart = pdoc->LineStart(lineDoc);
525 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
526 retVal = ll->numCharsBeforeEOL + posLineStart;
527 int subLine = 0;
528 int lineStart = ll->LineStart(subLine);
529 int lineEnd = ll->LineLastVisible(subLine);
530 int subLineStart = ll->positions[lineStart];
532 if (ll->wrapIndent != 0) {
533 if (lineStart != 0) // Wrapped
534 x -= ll->wrapIndent;
536 int i = ll->FindBefore(x + subLineStart, lineStart, lineEnd);
537 while (i < lineEnd) {
538 if ((x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
539 retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
540 break;
542 i++;
545 return retVal;
549 * Find the document position corresponding to an x coordinate on a particular document line.
550 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
552 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
553 RefreshStyleData();
554 if (lineDoc >= pdoc->LinesTotal())
555 return SelectionPosition(pdoc->Length());
556 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
557 AutoSurface surface(this);
558 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
559 int retVal = 0;
560 if (surface && ll) {
561 unsigned int posLineStart = pdoc->LineStart(lineDoc);
562 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
563 int subLine = 0;
564 int lineStart = ll->LineStart(subLine);
565 int lineEnd = ll->LineLastVisible(subLine);
566 int subLineStart = ll->positions[lineStart];
568 if (ll->wrapIndent != 0) {
569 if (lineStart != 0) // Wrapped
570 x -= ll->wrapIndent;
572 int i = ll->FindBefore(x + subLineStart, lineStart, lineEnd);
573 while (i < lineEnd) {
574 if ((x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
575 retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
576 return SelectionPosition(retVal);
578 i++;
580 const int spaceWidth = static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
581 int spaceOffset = (x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) / spaceWidth;
582 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
584 return SelectionPosition(retVal);
588 * If painting then abandon the painting because a wider redraw is needed.
589 * @return true if calling code should stop drawing.
591 bool Editor::AbandonPaint() {
592 if ((paintState == painting) && !paintingAllText) {
593 paintState = paintAbandoned;
595 return paintState == paintAbandoned;
598 void Editor::RedrawRect(PRectangle rc) {
599 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
601 // Clip the redraw rectangle into the client area
602 PRectangle rcClient = GetClientRectangle();
603 if (rc.top < rcClient.top)
604 rc.top = rcClient.top;
605 if (rc.bottom > rcClient.bottom)
606 rc.bottom = rcClient.bottom;
607 if (rc.left < rcClient.left)
608 rc.left = rcClient.left;
609 if (rc.right > rcClient.right)
610 rc.right = rcClient.right;
612 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
613 wMain.InvalidateRectangle(rc);
617 void Editor::Redraw() {
618 //Platform::DebugPrintf("Redraw all\n");
619 PRectangle rcClient = GetClientRectangle();
620 wMain.InvalidateRectangle(rcClient);
621 //wMain.InvalidateAll();
624 void Editor::RedrawSelMargin(int line) {
625 if (!AbandonPaint()) {
626 if (vs.maskInLine) {
627 Redraw();
628 } else {
629 PRectangle rcSelMargin = GetClientRectangle();
630 rcSelMargin.right = vs.fixedColumnWidth;
631 if (line != -1) {
632 int position = pdoc->LineStart(line);
633 PRectangle rcLine = RectangleFromRange(position, position);
634 rcSelMargin.top = rcLine.top;
635 rcSelMargin.bottom = rcLine.bottom;
637 wMain.InvalidateRectangle(rcSelMargin);
642 PRectangle Editor::RectangleFromRange(int start, int end) {
643 int minPos = start;
644 if (minPos > end)
645 minPos = end;
646 int maxPos = start;
647 if (maxPos < end)
648 maxPos = end;
649 int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos));
650 int lineDocMax = pdoc->LineFromPosition(maxPos);
651 int maxLine = cs.DisplayFromDoc(lineDocMax) + cs.GetHeight(lineDocMax) - 1;
652 PRectangle rcClient = GetTextRectangle();
653 PRectangle rc;
654 rc.left = vs.fixedColumnWidth;
655 rc.top = (minLine - topLine) * vs.lineHeight;
656 if (rc.top < 0)
657 rc.top = 0;
658 rc.right = rcClient.right;
659 rc.bottom = (maxLine - topLine + 1) * vs.lineHeight;
660 // Ensure PRectangle is within 16 bit space
661 rc.top = Platform::Clamp(rc.top, -32000, 32000);
662 rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000);
664 return rc;
667 void Editor::InvalidateRange(int start, int end) {
668 RedrawRect(RectangleFromRange(start, end));
671 int Editor::CurrentPosition() {
672 return sel.MainCaret();
675 bool Editor::SelectionEmpty() {
676 return sel.Empty();
679 SelectionPosition Editor::SelectionStart() {
680 return sel.RangeMain().Start();
683 SelectionPosition Editor::SelectionEnd() {
684 return sel.RangeMain().End();
687 void Editor::SetRectangularRange() {
688 if (sel.IsRectangular()) {
689 int xAnchor = XFromPosition(sel.Rectangular().anchor);
690 int xCaret = XFromPosition(sel.Rectangular().caret);
691 if (sel.selType == Selection::selThin) {
692 xCaret = xAnchor;
694 int lineAnchor = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
695 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
696 int increment = (lineCaret > lineAnchor) ? 1 : -1;
697 for (int line=lineAnchor; line != lineCaret+increment; line += increment) {
698 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
699 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
700 range.ClearVirtualSpace();
701 if (line == lineAnchor)
702 sel.SetSelection(range);
703 else
704 sel.AddSelection(range);
709 void Editor::ThinRectangularRange() {
710 if (sel.IsRectangular()) {
711 sel.selType = Selection::selThin;
712 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
713 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
714 } else {
715 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
717 SetRectangularRange();
721 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
722 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
723 invalidateWholeSelection = true;
725 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
726 // +1 for lastAffected ensures caret repainted
727 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
728 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
729 if (invalidateWholeSelection) {
730 for (size_t r=0; r<sel.Count(); r++) {
731 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
732 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
733 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
734 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
737 needUpdateUI = true;
738 InvalidateRange(firstAffected, lastAffected);
741 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
742 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_),
743 ClampPositionIntoDocument(anchor_));
744 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
745 InvalidateSelection(rangeNew);
747 sel.RangeMain() = rangeNew;
748 SetRectangularRange();
749 ClaimSelection();
752 void Editor::SetSelection(int currentPos_, int anchor_) {
753 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
756 // Just move the caret on the main selection
757 void Editor::SetSelection(SelectionPosition currentPos_) {
758 currentPos_ = ClampPositionIntoDocument(currentPos_);
759 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
760 InvalidateSelection(SelectionRange(currentPos_));
762 if (sel.IsRectangular()) {
763 sel.Rectangular() =
764 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
765 SetRectangularRange();
766 } else {
767 sel.RangeMain() =
768 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
770 ClaimSelection();
773 void Editor::SetSelection(int currentPos_) {
774 SetSelection(SelectionPosition(currentPos_));
777 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
778 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
779 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
780 InvalidateSelection(rangeNew);
782 sel.Clear();
783 sel.RangeMain() = rangeNew;
784 SetRectangularRange();
785 ClaimSelection();
789 void Editor::SetEmptySelection(int currentPos_) {
790 SetEmptySelection(SelectionPosition(currentPos_));
793 bool Editor::RangeContainsProtected(int start, int end) const {
794 if (vs.ProtectionActive()) {
795 if (start > end) {
796 int t = start;
797 start = end;
798 end = t;
800 int mask = pdoc->stylingBitsMask;
801 for (int pos = start; pos < end; pos++) {
802 if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())
803 return true;
806 return false;
809 bool Editor::SelectionContainsProtected() {
810 for (size_t r=0; r<sel.Count(); r++) {
811 if (RangeContainsProtected(sel.Range(r).Start().Position(),
812 sel.Range(r).End().Position())) {
813 return true;
816 return false;
820 * Asks document to find a good position and then moves out of any invisible positions.
822 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
823 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
826 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
827 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
828 if (posMoved != pos.Position())
829 pos.SetPosition(posMoved);
830 if (vs.ProtectionActive()) {
831 int mask = pdoc->stylingBitsMask;
832 if (moveDir > 0) {
833 if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()) {
834 while ((pos.Position() < pdoc->Length()) &&
835 (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()))
836 pos.Add(1);
838 } else if (moveDir < 0) {
839 if (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()) {
840 while ((pos.Position() > 0) &&
841 (vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()))
842 pos.Add(-1);
846 return pos;
849 int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
850 int delta = newPos.Position() - sel.MainCaret();
851 newPos = ClampPositionIntoDocument(newPos);
852 newPos = MovePositionOutsideChar(newPos, delta);
853 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
854 // Switching to rectangular
855 SelectionRange rangeMain = sel.RangeMain();
856 sel.Clear();
857 sel.Rectangular() = rangeMain;
859 if (selt != Selection::noSel) {
860 sel.selType = selt;
862 if (selt != Selection::noSel || sel.MoveExtends()) {
863 SetSelection(newPos);
864 } else {
865 SetEmptySelection(newPos);
867 ShowCaretAtCurrentPosition();
868 if (ensureVisible) {
869 EnsureCaretVisible();
871 return 0;
874 int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
875 return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
878 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
879 pos = ClampPositionIntoDocument(pos);
880 pos = MovePositionOutsideChar(pos, moveDir);
881 int lineDoc = pdoc->LineFromPosition(pos.Position());
882 if (cs.GetVisible(lineDoc)) {
883 return pos;
884 } else {
885 int lineDisplay = cs.DisplayFromDoc(lineDoc);
886 if (moveDir > 0) {
887 // lineDisplay is already line before fold as lines in fold use display line of line after fold
888 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
889 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
890 } else {
891 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
892 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
897 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
898 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
901 Point Editor::PointMainCaret() {
902 return LocationFromPosition(sel.Range(sel.Main()).caret);
906 * Choose the x position that the caret will try to stick to
907 * as it moves up and down.
909 void Editor::SetLastXChosen() {
910 Point pt = PointMainCaret();
911 lastXChosen = pt.x;
914 void Editor::ScrollTo(int line, bool moveThumb) {
915 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
916 if (topLineNew != topLine) {
917 // Try to optimise small scrolls
918 int linesToMove = topLine - topLineNew;
919 SetTopLine(topLineNew);
920 ShowCaretAtCurrentPosition();
921 // Perform redraw rather than scroll if many lines would be redrawn anyway.
922 #ifndef UNDER_CE
923 if ((abs(linesToMove) <= 10) && (paintState == notPainting)) {
924 ScrollText(linesToMove);
925 } else {
926 Redraw();
928 #else
929 Redraw();
930 #endif
931 if (moveThumb) {
932 SetVerticalScrollPos();
937 void Editor::ScrollText(int /* linesToMove */) {
938 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
939 Redraw();
942 void Editor::HorizontalScrollTo(int xPos) {
943 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
944 if (xPos < 0)
945 xPos = 0;
946 if ((wrapState == eWrapNone) && (xOffset != xPos)) {
947 xOffset = xPos;
948 SetHorizontalScrollPos();
949 RedrawRect(GetClientRectangle());
953 void Editor::MoveCaretInsideView(bool ensureVisible) {
954 PRectangle rcClient = GetTextRectangle();
955 Point pt = PointMainCaret();
956 if (pt.y < rcClient.top) {
957 MovePositionTo(SPositionFromLocation(
958 Point(lastXChosen, rcClient.top)),
959 Selection::noSel, ensureVisible);
960 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
961 int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
962 MovePositionTo(SPositionFromLocation(
963 Point(lastXChosen, rcClient.top + yOfLastLineFullyDisplayed)),
964 Selection::noSel, ensureVisible);
968 int Editor::DisplayFromPosition(int pos) {
969 int lineDoc = pdoc->LineFromPosition(pos);
970 int lineDisplay = cs.DisplayFromDoc(lineDoc);
971 AutoSurface surface(this);
972 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
973 if (surface && ll) {
974 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
975 unsigned int posLineStart = pdoc->LineStart(lineDoc);
976 int posInLine = pos - posLineStart;
977 lineDisplay--; // To make up for first increment ahead.
978 for (int subLine = 0; subLine < ll->lines; subLine++) {
979 if (posInLine >= ll->LineStart(subLine)) {
980 lineDisplay++;
984 return lineDisplay;
988 * Ensure the caret is reasonably visible in context.
990 Caret policy in SciTE
992 If slop is set, we can define a slop value.
993 This value defines an unwanted zone (UZ) where the caret is... unwanted.
994 This zone is defined as a number of pixels near the vertical margins,
995 and as a number of lines near the horizontal margins.
996 By keeping the caret away from the edges, it is seen within its context,
997 so it is likely that the identifier that the caret is on can be completely seen,
998 and that the current line is seen with some of the lines following it which are
999 often dependent on that line.
1001 If strict is set, the policy is enforced... strictly.
1002 The caret is centred on the display if slop is not set,
1003 and cannot go in the UZ if slop is set.
1005 If jumps is set, the display is moved more energetically
1006 so the caret can move in the same direction longer before the policy is applied again.
1007 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1009 If even is not set, instead of having symmetrical UZs,
1010 the left and bottom UZs are extended up to right and top UZs respectively.
1011 This way, we favour the displaying of useful information: the begining of lines,
1012 where most code reside, and the lines after the caret, eg. the body of a function.
1014 | | | | |
1015 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1016 | | | | | visibility or going into the UZ) display is...
1017 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1018 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1019 0 | 0 | 0 | 1 | Yes | moved by one position
1020 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1021 0 | 0 | 1 | 1 | Yes | centred on the caret
1022 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1023 0 | 1 | - | 1 | No, caret is always centred | -
1024 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1025 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1026 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1027 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1028 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1029 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1030 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1032 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1033 //Platform::DebugPrintf("EnsureCaretVisible %d %s\n", xOffset, useMargin ? " margin" : " ");
1034 PRectangle rcClient = GetTextRectangle();
1035 //int rcClientFullWidth = rcClient.Width();
1036 SelectionPosition posCaret = sel.RangeMain().caret;
1037 if (posDrag.IsValid()) {
1038 posCaret = posDrag;
1040 Point pt = LocationFromPosition(posCaret);
1041 Point ptBottomCaret = pt;
1042 ptBottomCaret.y += vs.lineHeight - 1;
1043 int lineCaret = DisplayFromPosition(posCaret.Position());
1044 bool bSlop, bStrict, bJump, bEven;
1046 // Vertical positioning
1047 if (vert && (pt.y < rcClient.top || ptBottomCaret.y > rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1048 int linesOnScreen = LinesOnScreen();
1049 int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1050 int newTopLine = topLine;
1051 bSlop = (caretYPolicy & CARET_SLOP) != 0;
1052 bStrict = (caretYPolicy & CARET_STRICT) != 0;
1053 bJump = (caretYPolicy & CARET_JUMPS) != 0;
1054 bEven = (caretYPolicy & CARET_EVEN) != 0;
1056 // It should be possible to scroll the window to show the caret,
1057 // but this fails to remove the caret on GTK+
1058 if (bSlop) { // A margin is defined
1059 int yMoveT, yMoveB;
1060 if (bStrict) {
1061 int yMarginT, yMarginB;
1062 if (!useMargin) {
1063 // In drag mode, avoid moves
1064 // otherwise, a double click will select several lines.
1065 yMarginT = yMarginB = 0;
1066 } else {
1067 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1068 // a maximum of slightly less than half the heigth of the text area.
1069 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1070 if (bEven) {
1071 yMarginB = yMarginT;
1072 } else {
1073 yMarginB = linesOnScreen - yMarginT - 1;
1076 yMoveT = yMarginT;
1077 if (bEven) {
1078 if (bJump) {
1079 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1081 yMoveB = yMoveT;
1082 } else {
1083 yMoveB = linesOnScreen - yMoveT - 1;
1085 if (lineCaret < topLine + yMarginT) {
1086 // Caret goes too high
1087 newTopLine = lineCaret - yMoveT;
1088 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1089 // Caret goes too low
1090 newTopLine = lineCaret - linesOnScreen + 1 + yMoveB;
1092 } else { // Not strict
1093 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1094 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1095 if (bEven) {
1096 yMoveB = yMoveT;
1097 } else {
1098 yMoveB = linesOnScreen - yMoveT - 1;
1100 if (lineCaret < topLine) {
1101 // Caret goes too high
1102 newTopLine = lineCaret - yMoveT;
1103 } else if (lineCaret > topLine + linesOnScreen - 1) {
1104 // Caret goes too low
1105 newTopLine = lineCaret - linesOnScreen + 1 + yMoveB;
1108 } else { // No slop
1109 if (!bStrict && !bJump) {
1110 // Minimal move
1111 if (lineCaret < topLine) {
1112 // Caret goes too high
1113 newTopLine = lineCaret;
1114 } else if (lineCaret > topLine + linesOnScreen - 1) {
1115 // Caret goes too low
1116 if (bEven) {
1117 newTopLine = lineCaret - linesOnScreen + 1;
1118 } else {
1119 newTopLine = lineCaret;
1122 } else { // Strict or going out of display
1123 if (bEven) {
1124 // Always center caret
1125 newTopLine = lineCaret - halfScreen;
1126 } else {
1127 // Always put caret on top of display
1128 newTopLine = lineCaret;
1132 newTopLine = Platform::Clamp(newTopLine, 0, MaxScrollPos());
1133 if (newTopLine != topLine) {
1134 Redraw();
1135 SetTopLine(newTopLine);
1136 SetVerticalScrollPos();
1140 // Horizontal positioning
1141 if (horiz && (wrapState == eWrapNone)) {
1142 int halfScreen = Platform::Maximum(rcClient.Width() - 4, 4) / 2;
1143 int xOffsetNew = xOffset;
1144 bSlop = (caretXPolicy & CARET_SLOP) != 0;
1145 bStrict = (caretXPolicy & CARET_STRICT) != 0;
1146 bJump = (caretXPolicy & CARET_JUMPS) != 0;
1147 bEven = (caretXPolicy & CARET_EVEN) != 0;
1149 if (bSlop) { // A margin is defined
1150 int xMoveL, xMoveR;
1151 if (bStrict) {
1152 int xMarginL, xMarginR;
1153 if (!useMargin) {
1154 // In drag mode, avoid moves unless very near of the margin
1155 // otherwise, a simple click will select text.
1156 xMarginL = xMarginR = 2;
1157 } else {
1158 // xMargin must equal to caretXSlop, with a minimum of 2 and
1159 // a maximum of slightly less than half the width of the text area.
1160 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1161 if (bEven) {
1162 xMarginL = xMarginR;
1163 } else {
1164 xMarginL = rcClient.Width() - xMarginR - 4;
1167 if (bJump && bEven) {
1168 // Jump is used only in even mode
1169 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1170 } else {
1171 xMoveL = xMoveR = 0; // Not used, avoid a warning
1173 if (pt.x < rcClient.left + xMarginL) {
1174 // Caret is on the left of the display
1175 if (bJump && bEven) {
1176 xOffsetNew -= xMoveL;
1177 } else {
1178 // Move just enough to allow to display the caret
1179 xOffsetNew -= (rcClient.left + xMarginL) - pt.x;
1181 } else if (pt.x >= rcClient.right - xMarginR) {
1182 // Caret is on the right of the display
1183 if (bJump && bEven) {
1184 xOffsetNew += xMoveR;
1185 } else {
1186 // Move just enough to allow to display the caret
1187 xOffsetNew += pt.x - (rcClient.right - xMarginR) + 1;
1190 } else { // Not strict
1191 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1192 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1193 if (bEven) {
1194 xMoveL = xMoveR;
1195 } else {
1196 xMoveL = rcClient.Width() - xMoveR - 4;
1198 if (pt.x < rcClient.left) {
1199 // Caret is on the left of the display
1200 xOffsetNew -= xMoveL;
1201 } else if (pt.x >= rcClient.right) {
1202 // Caret is on the right of the display
1203 xOffsetNew += xMoveR;
1206 } else { // No slop
1207 if (bStrict ||
1208 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1209 // Strict or going out of display
1210 if (bEven) {
1211 // Center caret
1212 xOffsetNew += pt.x - rcClient.left - halfScreen;
1213 } else {
1214 // Put caret on right
1215 xOffsetNew += pt.x - rcClient.right + 1;
1217 } else {
1218 // Move just enough to allow to display the caret
1219 if (pt.x < rcClient.left) {
1220 // Caret is on the left of the display
1221 if (bEven) {
1222 xOffsetNew -= rcClient.left - pt.x;
1223 } else {
1224 xOffsetNew += pt.x - rcClient.right + 1;
1226 } else if (pt.x >= rcClient.right) {
1227 // Caret is on the right of the display
1228 xOffsetNew += pt.x - rcClient.right + 1;
1232 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1233 if (pt.x + xOffset < rcClient.left + xOffsetNew) {
1234 xOffsetNew = pt.x + xOffset - rcClient.left;
1235 } else if (pt.x + xOffset >= rcClient.right + xOffsetNew) {
1236 xOffsetNew = pt.x + xOffset - rcClient.right + 1;
1237 if (vs.caretStyle == CARETSTYLE_BLOCK) {
1238 // Ensure we can see a good portion of the block caret
1239 xOffsetNew += vs.aveCharWidth;
1242 if (xOffsetNew < 0) {
1243 xOffsetNew = 0;
1245 if (xOffset != xOffsetNew) {
1246 xOffset = xOffsetNew;
1247 if (xOffsetNew > 0) {
1248 PRectangle rcText = GetTextRectangle();
1249 if (horizontalScrollBarVisible &&
1250 rcText.Width() + xOffset > scrollWidth) {
1251 scrollWidth = xOffset + rcText.Width();
1252 SetScrollBars();
1255 SetHorizontalScrollPos();
1256 Redraw();
1259 UpdateSystemCaret();
1262 void Editor::ShowCaretAtCurrentPosition() {
1263 if (hasFocus) {
1264 caret.active = true;
1265 caret.on = true;
1266 SetTicking(true);
1267 } else {
1268 caret.active = false;
1269 caret.on = false;
1271 InvalidateCaret();
1274 void Editor::DropCaret() {
1275 caret.active = false;
1276 InvalidateCaret();
1279 void Editor::InvalidateCaret() {
1280 if (posDrag.IsValid()) {
1281 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1282 } else {
1283 for (size_t r=0; r<sel.Count(); r++) {
1284 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1287 UpdateSystemCaret();
1290 void Editor::UpdateSystemCaret() {
1293 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1294 docLineStart = Platform::Clamp(docLineStart, 0, pdoc->LinesTotal());
1295 if (wrapStart > docLineStart) {
1296 wrapStart = docLineStart;
1297 llc.Invalidate(LineLayout::llPositions);
1299 if (wrapEnd < docLineEnd) {
1300 wrapEnd = docLineEnd;
1302 wrapEnd = Platform::Clamp(wrapEnd, 0, pdoc->LinesTotal());
1303 // Wrap lines during idle.
1304 if ((wrapState != eWrapNone) && (wrapEnd != wrapStart)) {
1305 SetIdle(true);
1309 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1310 AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
1311 int linesWrapped = 1;
1312 if (ll) {
1313 LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
1314 linesWrapped = ll->lines;
1316 return cs.SetHeight(lineToWrap, linesWrapped +
1317 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1320 // Check if wrapping needed and perform any needed wrapping.
1321 // fullwrap: if true, all lines which need wrapping will be done,
1322 // in this single call.
1323 // priorityWrapLineStart: If greater than or equal to zero, all lines starting from
1324 // here to 1 page + 100 lines past will be wrapped (even if there are
1325 // more lines under wrapping process in idle).
1326 // If it is neither fullwrap, nor priorityWrap, then 1 page + 100 lines will be
1327 // wrapped, if there are any wrapping going on in idle. (Generally this
1328 // condition is called only from idler).
1329 // Return true if wrapping occurred.
1330 bool Editor::WrapLines(bool fullWrap, int priorityWrapLineStart) {
1331 // If there are any pending wraps, do them during idle if possible.
1332 int linesInOneCall = LinesOnScreen() + 100;
1333 if (wrapState != eWrapNone) {
1334 if (wrapStart < wrapEnd) {
1335 if (!SetIdle(true)) {
1336 // Idle processing not supported so full wrap required.
1337 fullWrap = true;
1340 if (!fullWrap && priorityWrapLineStart >= 0 &&
1341 // .. and if the paint window is outside pending wraps
1342 (((priorityWrapLineStart + linesInOneCall) < wrapStart) ||
1343 (priorityWrapLineStart > wrapEnd))) {
1344 // No priority wrap pending
1345 return false;
1348 int goodTopLine = topLine;
1349 bool wrapOccurred = false;
1350 if (wrapStart <= pdoc->LinesTotal()) {
1351 if (wrapState == eWrapNone) {
1352 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1353 wrapWidth = LineLayout::wrapWidthInfinite;
1354 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1355 cs.SetHeight(lineDoc, 1 +
1356 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1358 wrapOccurred = true;
1360 wrapStart = wrapLineLarge;
1361 wrapEnd = wrapLineLarge;
1362 } else {
1363 if (wrapEnd >= pdoc->LinesTotal())
1364 wrapEnd = pdoc->LinesTotal();
1365 //ElapsedTime et;
1366 int lineDocTop = cs.DocFromDisplay(topLine);
1367 int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1368 PRectangle rcTextArea = GetClientRectangle();
1369 rcTextArea.left = vs.fixedColumnWidth;
1370 rcTextArea.right -= vs.rightMarginWidth;
1371 wrapWidth = rcTextArea.Width();
1372 // Ensure all of the document is styled.
1373 pdoc->EnsureStyledTo(pdoc->Length());
1374 RefreshStyleData();
1375 AutoSurface surface(this);
1376 if (surface) {
1377 bool priorityWrap = false;
1378 int lastLineToWrap = wrapEnd;
1379 int lineToWrap = wrapStart;
1380 if (!fullWrap) {
1381 if (priorityWrapLineStart >= 0) {
1382 // This is a priority wrap.
1383 lineToWrap = priorityWrapLineStart;
1384 lastLineToWrap = priorityWrapLineStart + linesInOneCall;
1385 priorityWrap = true;
1386 } else {
1387 // This is idle wrap.
1388 lastLineToWrap = wrapStart + linesInOneCall;
1390 if (lastLineToWrap >= wrapEnd)
1391 lastLineToWrap = wrapEnd;
1392 } // else do a fullWrap.
1394 // Platform::DebugPrintf("Wraplines: full = %d, priorityStart = %d (wrapping: %d to %d)\n", fullWrap, priorityWrapLineStart, lineToWrap, lastLineToWrap);
1395 // Platform::DebugPrintf("Pending wraps: %d to %d\n", wrapStart, wrapEnd);
1396 while (lineToWrap < lastLineToWrap) {
1397 if (WrapOneLine(surface, lineToWrap)) {
1398 wrapOccurred = true;
1400 lineToWrap++;
1402 if (!priorityWrap)
1403 wrapStart = lineToWrap;
1404 // If wrapping is done, bring it to resting position
1405 if (wrapStart >= wrapEnd) {
1406 wrapStart = wrapLineLarge;
1407 wrapEnd = wrapLineLarge;
1410 goodTopLine = cs.DisplayFromDoc(lineDocTop);
1411 if (subLineTop < cs.GetHeight(lineDocTop))
1412 goodTopLine += subLineTop;
1413 else
1414 goodTopLine += cs.GetHeight(lineDocTop);
1415 //double durWrap = et.Duration(true);
1416 //Platform::DebugPrintf("Wrap:%9.6g \n", durWrap);
1419 if (wrapOccurred) {
1420 SetScrollBars();
1421 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1422 SetVerticalScrollPos();
1424 return wrapOccurred;
1427 void Editor::LinesJoin() {
1428 if (!RangeContainsProtected(targetStart, targetEnd)) {
1429 UndoGroup ug(pdoc);
1430 bool prevNonWS = true;
1431 for (int pos = targetStart; pos < targetEnd; pos++) {
1432 if (IsEOLChar(pdoc->CharAt(pos))) {
1433 targetEnd -= pdoc->LenChar(pos);
1434 pdoc->DelChar(pos);
1435 if (prevNonWS) {
1436 // Ensure at least one space separating previous lines
1437 pdoc->InsertChar(pos, ' ');
1438 targetEnd++;
1440 } else {
1441 prevNonWS = pdoc->CharAt(pos) != ' ';
1447 const char *Editor::StringFromEOLMode(int eolMode) {
1448 if (eolMode == SC_EOL_CRLF) {
1449 return "\r\n";
1450 } else if (eolMode == SC_EOL_CR) {
1451 return "\r";
1452 } else {
1453 return "\n";
1457 void Editor::LinesSplit(int pixelWidth) {
1458 if (!RangeContainsProtected(targetStart, targetEnd)) {
1459 if (pixelWidth == 0) {
1460 PRectangle rcText = GetTextRectangle();
1461 pixelWidth = rcText.Width();
1463 int lineStart = pdoc->LineFromPosition(targetStart);
1464 int lineEnd = pdoc->LineFromPosition(targetEnd);
1465 const char *eol = StringFromEOLMode(pdoc->eolMode);
1466 UndoGroup ug(pdoc);
1467 for (int line = lineStart; line <= lineEnd; line++) {
1468 AutoSurface surface(this);
1469 AutoLineLayout ll(llc, RetrieveLineLayout(line));
1470 if (surface && ll) {
1471 unsigned int posLineStart = pdoc->LineStart(line);
1472 LayoutLine(line, surface, vs, ll, pixelWidth);
1473 for (int subLine = 1; subLine < ll->lines; subLine++) {
1474 pdoc->InsertCString(posLineStart + (subLine - 1) * strlen(eol) +
1475 ll->LineStart(subLine), eol);
1476 targetEnd += static_cast<int>(strlen(eol));
1479 lineEnd = pdoc->LineFromPosition(targetEnd);
1484 int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) {
1485 if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
1486 return markerDefault;
1487 return markerCheck;
1490 // Avoid 64 bit compiler warnings.
1491 // Scintilla does not support text buffers larger than 2**31
1492 static int istrlen(const char *s) {
1493 return static_cast<int>(strlen(s));
1496 bool ValidStyledText(ViewStyle &vs, size_t styleOffset, const StyledText &st) {
1497 if (st.multipleStyles) {
1498 for (size_t iStyle=0;iStyle<st.length; iStyle++) {
1499 if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
1500 return false;
1502 } else {
1503 if (!vs.ValidStyle(styleOffset + st.style))
1504 return false;
1506 return true;
1509 static int WidthStyledText(Surface *surface, ViewStyle &vs, int styleOffset,
1510 const char *text, const unsigned char *styles, size_t len) {
1511 int width = 0;
1512 size_t start = 0;
1513 while (start < len) {
1514 size_t style = styles[start];
1515 size_t endSegment = start;
1516 while ((endSegment+1 < len) && (static_cast<size_t>(styles[endSegment+1]) == style))
1517 endSegment++;
1518 width += surface->WidthText(vs.styles[style+styleOffset].font, text + start, endSegment - start + 1);
1519 start = endSegment + 1;
1521 return width;
1524 static int WidestLineWidth(Surface *surface, ViewStyle &vs, int styleOffset, const StyledText &st) {
1525 int widthMax = 0;
1526 size_t start = 0;
1527 while (start < st.length) {
1528 size_t lenLine = st.LineLength(start);
1529 int widthSubLine;
1530 if (st.multipleStyles) {
1531 widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
1532 } else {
1533 widthSubLine = surface->WidthText(vs.styles[styleOffset + st.style].font, st.text + start, lenLine);
1535 if (widthSubLine > widthMax)
1536 widthMax = widthSubLine;
1537 start += lenLine + 1;
1539 return widthMax;
1542 void DrawStyledText(Surface *surface, ViewStyle &vs, int styleOffset, PRectangle rcText, int ascent,
1543 const StyledText &st, size_t start, size_t length) {
1545 if (st.multipleStyles) {
1546 int x = rcText.left;
1547 size_t i = 0;
1548 while (i < length) {
1549 size_t end = i;
1550 int style = st.styles[i + start];
1551 while (end < length-1 && st.styles[start+end+1] == style)
1552 end++;
1553 style += styleOffset;
1554 int width = surface->WidthText(vs.styles[style].font, st.text + start + i, end - i + 1);
1555 PRectangle rcSegment = rcText;
1556 rcSegment.left = x;
1557 rcSegment.right = x + width + 1;
1558 surface->DrawTextNoClip(rcSegment, vs.styles[style].font,
1559 ascent, st.text + start + i, end - i + 1,
1560 vs.styles[style].fore.allocated,
1561 vs.styles[style].back.allocated);
1562 x += width;
1563 i = end + 1;
1565 } else {
1566 int style = st.style + styleOffset;
1567 surface->DrawTextNoClip(rcText, vs.styles[style].font,
1568 rcText.top + vs.maxAscent, st.text + start, length,
1569 vs.styles[style].fore.allocated,
1570 vs.styles[style].back.allocated);
1574 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1575 if (vs.fixedColumnWidth == 0)
1576 return;
1578 PRectangle rcMargin = GetClientRectangle();
1579 rcMargin.right = vs.fixedColumnWidth;
1581 if (!rc.Intersects(rcMargin))
1582 return;
1584 Surface *surface;
1585 if (bufferedDraw) {
1586 surface = pixmapSelMargin;
1587 } else {
1588 surface = surfWindow;
1591 PRectangle rcSelMargin = rcMargin;
1592 rcSelMargin.right = rcMargin.left;
1594 for (int margin = 0; margin < vs.margins; margin++) {
1595 if (vs.ms[margin].width > 0) {
1597 rcSelMargin.left = rcSelMargin.right;
1598 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
1600 if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
1601 /* alternate scheme:
1602 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
1603 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_DEFAULT].back.allocated);
1604 else
1605 // Required because of special way brush is created for selection margin
1606 surface->FillRectangle(rcSelMargin, pixmapSelPattern);
1608 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
1609 // Required because of special way brush is created for selection margin
1610 surface->FillRectangle(rcSelMargin, *pixmapSelPattern);
1611 else {
1612 ColourAllocated colour;
1613 switch (vs.ms[margin].style) {
1614 case SC_MARGIN_BACK:
1615 colour = vs.styles[STYLE_DEFAULT].back.allocated;
1616 break;
1617 case SC_MARGIN_FORE:
1618 colour = vs.styles[STYLE_DEFAULT].fore.allocated;
1619 break;
1620 default:
1621 colour = vs.styles[STYLE_LINENUMBER].back.allocated;
1622 break;
1624 surface->FillRectangle(rcSelMargin, colour);
1626 } else {
1627 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated);
1630 int visibleLine = topLine;
1631 int yposScreen = 0;
1633 // Work out whether the top line is whitespace located after a
1634 // lessening of fold level which implies a 'fold tail' but which should not
1635 // be displayed until the last of a sequence of whitespace.
1636 bool needWhiteClosure = false;
1637 int level = pdoc->GetLevel(cs.DocFromDisplay(topLine));
1638 if (level & SC_FOLDLEVELWHITEFLAG) {
1639 int lineBack = cs.DocFromDisplay(topLine);
1640 int levelPrev = level;
1641 while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
1642 lineBack--;
1643 levelPrev = pdoc->GetLevel(lineBack);
1645 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
1646 if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK))
1647 needWhiteClosure = true;
1651 // Old code does not know about new markers needed to distinguish all cases
1652 int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
1653 SC_MARKNUM_FOLDEROPEN);
1654 int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
1655 SC_MARKNUM_FOLDER);
1657 while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rcMargin.bottom) {
1659 PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed());
1661 int lineDoc = cs.DocFromDisplay(visibleLine);
1662 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
1663 bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
1665 // Decide which fold indicator should be displayed
1666 level = pdoc->GetLevel(lineDoc);
1667 int levelNext = pdoc->GetLevel(lineDoc + 1);
1668 int marks = pdoc->GetMark(lineDoc);
1669 if (!firstSubLine)
1670 marks = 0;
1671 int levelNum = level & SC_FOLDLEVELNUMBERMASK;
1672 int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
1673 if (level & SC_FOLDLEVELHEADERFLAG) {
1674 if (firstSubLine) {
1675 if (cs.GetExpanded(lineDoc)) {
1676 if (levelNum == SC_FOLDLEVELBASE)
1677 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
1678 else
1679 marks |= 1 << folderOpenMid;
1680 } else {
1681 if (levelNum == SC_FOLDLEVELBASE)
1682 marks |= 1 << SC_MARKNUM_FOLDER;
1683 else
1684 marks |= 1 << folderEnd;
1686 } else {
1687 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1689 needWhiteClosure = false;
1690 } else if (level & SC_FOLDLEVELWHITEFLAG) {
1691 if (needWhiteClosure) {
1692 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1693 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1694 } else if (levelNum > SC_FOLDLEVELBASE) {
1695 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1696 needWhiteClosure = false;
1697 } else {
1698 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1699 needWhiteClosure = false;
1701 } else if (levelNum > SC_FOLDLEVELBASE) {
1702 if (levelNextNum < levelNum) {
1703 if (levelNextNum > SC_FOLDLEVELBASE) {
1704 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1705 } else {
1706 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1708 } else {
1709 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1712 } else if (levelNum > SC_FOLDLEVELBASE) {
1713 if (levelNextNum < levelNum) {
1714 needWhiteClosure = false;
1715 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1716 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1717 needWhiteClosure = true;
1718 } else if (levelNextNum > SC_FOLDLEVELBASE) {
1719 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1720 } else {
1721 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1723 } else {
1724 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1728 marks &= vs.ms[margin].mask;
1729 PRectangle rcMarker = rcSelMargin;
1730 rcMarker.top = yposScreen;
1731 rcMarker.bottom = yposScreen + vs.lineHeight;
1732 if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
1733 char number[100];
1734 number[0] = '\0';
1735 if (firstSubLine)
1736 sprintf(number, "%d", lineDoc + 1);
1737 if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
1738 int lev = pdoc->GetLevel(lineDoc);
1739 sprintf(number, "%c%c %03X %03X",
1740 (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
1741 (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
1742 lev & SC_FOLDLEVELNUMBERMASK,
1743 lev >> 16
1746 PRectangle rcNumber = rcMarker;
1747 // Right justify
1748 int width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
1749 int xpos = rcNumber.right - width - 3;
1750 rcNumber.left = xpos;
1751 surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font,
1752 rcNumber.top + vs.maxAscent, number, istrlen(number),
1753 vs.styles[STYLE_LINENUMBER].fore.allocated,
1754 vs.styles[STYLE_LINENUMBER].back.allocated);
1755 } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
1756 if (firstSubLine) {
1757 const StyledText stMargin = pdoc->MarginStyledText(lineDoc);
1758 if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
1759 surface->FillRectangle(rcMarker,
1760 vs.styles[stMargin.StyleAt(0)+vs.marginStyleOffset].back.allocated);
1761 if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
1762 int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
1763 rcMarker.left = rcMarker.right - width - 3;
1765 DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, rcMarker.top + vs.maxAscent,
1766 stMargin, 0, stMargin.length);
1771 if (marks) {
1772 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1773 if (marks & 1) {
1774 vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font);
1776 marks >>= 1;
1780 visibleLine++;
1781 yposScreen += vs.lineHeight;
1786 PRectangle rcBlankMargin = rcMargin;
1787 rcBlankMargin.left = rcSelMargin.right;
1788 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back.allocated);
1790 if (bufferedDraw) {
1791 surfWindow->Copy(rcMargin, Point(), *pixmapSelMargin);
1795 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
1796 int ydiff = (rcTab.bottom - rcTab.top) / 2;
1797 int xhead = rcTab.right - 1 - ydiff;
1798 if (xhead <= rcTab.left) {
1799 ydiff -= rcTab.left - xhead - 1;
1800 xhead = rcTab.left - 1;
1802 if ((rcTab.left + 2) < (rcTab.right - 1))
1803 surface->MoveTo(rcTab.left + 2, ymid);
1804 else
1805 surface->MoveTo(rcTab.right - 1, ymid);
1806 surface->LineTo(rcTab.right - 1, ymid);
1807 surface->LineTo(xhead, ymid - ydiff);
1808 surface->MoveTo(rcTab.right - 1, ymid);
1809 surface->LineTo(xhead, ymid + ydiff);
1812 LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
1813 int posLineStart = pdoc->LineStart(lineNumber);
1814 int posLineEnd = pdoc->LineStart(lineNumber + 1);
1815 PLATFORM_ASSERT(posLineEnd >= posLineStart);
1816 int lineCaret = pdoc->LineFromPosition(sel.MainCaret());
1817 return llc.Retrieve(lineNumber, lineCaret,
1818 posLineEnd - posLineStart, pdoc->GetStyleClock(),
1819 LinesOnScreen() + 1, pdoc->LinesTotal());
1822 static bool GoodTrailByte(int v) {
1823 return (v >= 0x80) && (v < 0xc0);
1826 bool BadUTF(const char *s, int len, int &trailBytes) {
1827 if (trailBytes) {
1828 trailBytes--;
1829 return false;
1831 const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
1832 if (*us < 0x80) {
1833 // Single bytes easy
1834 return false;
1835 } else if (*us > 0xF4) {
1836 // Characters longer than 4 bytes not possible in current UTF-8
1837 return true;
1838 } else if (*us >= 0xF0) {
1839 // 4 bytes
1840 if (len < 4)
1841 return true;
1842 if (GoodTrailByte(us[1]) && GoodTrailByte(us[2]) && GoodTrailByte(us[3])) {
1843 trailBytes = 3;
1844 return false;
1845 } else {
1846 return true;
1848 } else if (*us >= 0xE0) {
1849 // 3 bytes
1850 if (len < 3)
1851 return true;
1852 if (GoodTrailByte(us[1]) && GoodTrailByte(us[2])) {
1853 trailBytes = 2;
1854 return false;
1855 } else {
1856 return true;
1858 } else if (*us >= 0xC2) {
1859 // 2 bytes
1860 if (len < 2)
1861 return true;
1862 if (GoodTrailByte(us[1])) {
1863 trailBytes = 1;
1864 return false;
1865 } else {
1866 return true;
1868 } else if (*us >= 0xC0) {
1869 // Overlong encoding
1870 return true;
1871 } else {
1872 // Trail byte
1873 return true;
1878 * Fill in the LineLayout data for the given line.
1879 * Copy the given @a line and its styles from the document into local arrays.
1880 * Also determine the x position at which each character starts.
1882 void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) {
1883 if (!ll)
1884 return;
1886 PLATFORM_ASSERT(line < pdoc->LinesTotal());
1887 PLATFORM_ASSERT(ll->chars != NULL);
1888 int posLineStart = pdoc->LineStart(line);
1889 int posLineEnd = pdoc->LineStart(line + 1);
1890 // If the line is very long, limit the treatment to a length that should fit in the viewport
1891 if (posLineEnd > (posLineStart + ll->maxLineLength)) {
1892 posLineEnd = posLineStart + ll->maxLineLength;
1894 if (ll->validity == LineLayout::llCheckTextAndStyle) {
1895 int lineLength = posLineEnd - posLineStart;
1896 if (!vstyle.viewEOL) {
1897 int cid = posLineEnd - 1;
1898 while ((cid > posLineStart) && IsEOLChar(pdoc->CharAt(cid))) {
1899 cid--;
1900 lineLength--;
1903 if (lineLength == ll->numCharsInLine) {
1904 // See if chars, styles, indicators, are all the same
1905 bool allSame = true;
1906 const int styleMask = pdoc->stylingBitsMask;
1907 // Check base line layout
1908 char styleByte = 0;
1909 int numCharsInLine = 0;
1910 while (numCharsInLine < lineLength) {
1911 int charInDoc = numCharsInLine + posLineStart;
1912 char chDoc = pdoc->CharAt(charInDoc);
1913 styleByte = pdoc->StyleAt(charInDoc);
1914 allSame = allSame &&
1915 (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte & styleMask));
1916 allSame = allSame &&
1917 (ll->indicators[numCharsInLine] == static_cast<char>(styleByte & ~styleMask));
1918 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
1919 allSame = allSame &&
1920 (ll->chars[numCharsInLine] == chDoc);
1921 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
1922 allSame = allSame &&
1923 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
1924 else // Style::caseUpper
1925 allSame = allSame &&
1926 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
1927 numCharsInLine++;
1929 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
1930 if (allSame) {
1931 ll->validity = LineLayout::llPositions;
1932 } else {
1933 ll->validity = LineLayout::llInvalid;
1935 } else {
1936 ll->validity = LineLayout::llInvalid;
1939 if (ll->validity == LineLayout::llInvalid) {
1940 ll->widthLine = LineLayout::wrapWidthInfinite;
1941 ll->lines = 1;
1942 int numCharsInLine = 0;
1943 int numCharsBeforeEOL = 0;
1944 if (vstyle.edgeState == EDGE_BACKGROUND) {
1945 ll->edgeColumn = pdoc->FindColumn(line, theEdge);
1946 if (ll->edgeColumn >= posLineStart) {
1947 ll->edgeColumn -= posLineStart;
1949 } else {
1950 ll->edgeColumn = -1;
1953 char styleByte = 0;
1954 int styleMask = pdoc->stylingBitsMask;
1955 ll->styleBitsSet = 0;
1956 // Fill base line layout
1957 for (int charInDoc = posLineStart; charInDoc < posLineEnd; charInDoc++) {
1958 char chDoc = pdoc->CharAt(charInDoc);
1959 styleByte = pdoc->StyleAt(charInDoc);
1960 ll->styleBitsSet |= styleByte;
1961 if (vstyle.viewEOL || (!IsEOLChar(chDoc))) {
1962 ll->chars[numCharsInLine] = chDoc;
1963 ll->styles[numCharsInLine] = static_cast<char>(styleByte & styleMask);
1964 ll->indicators[numCharsInLine] = static_cast<char>(styleByte & ~styleMask);
1965 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseUpper)
1966 ll->chars[numCharsInLine] = static_cast<char>(toupper(chDoc));
1967 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
1968 ll->chars[numCharsInLine] = static_cast<char>(tolower(chDoc));
1969 numCharsInLine++;
1970 if (!IsEOLChar(chDoc))
1971 numCharsBeforeEOL++;
1974 ll->xHighlightGuide = 0;
1975 // Extra element at the end of the line to hold end x position and act as
1976 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
1977 ll->styles[numCharsInLine] = styleByte; // For eolFilled
1978 ll->indicators[numCharsInLine] = 0;
1980 // Layout the line, determining the position of each character,
1981 // with an extra element at the end for the end of the line.
1982 int startseg = 0; // Start of the current segment, in char. number
1983 int startsegx = 0; // Start of the current segment, in pixels
1984 ll->positions[0] = 0;
1985 unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
1986 bool lastSegItalics = false;
1987 Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
1989 int ctrlCharWidth[32] = {0};
1990 bool isControlNext = IsControlCharacter(ll->chars[0]);
1991 int trailBytes = 0;
1992 bool isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars, numCharsInLine, trailBytes);
1993 for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
1994 bool isControl = isControlNext;
1995 isControlNext = IsControlCharacter(ll->chars[charInLine + 1]);
1996 bool isBadUTF = isBadUTFNext;
1997 isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars + charInLine + 1, numCharsInLine - charInLine - 1, trailBytes);
1998 if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) ||
1999 isControl || isControlNext || isBadUTF || isBadUTFNext) {
2000 ll->positions[startseg] = 0;
2001 if (vstyle.styles[ll->styles[charInLine]].visible) {
2002 if (isControl) {
2003 if (ll->chars[charInLine] == '\t') {
2004 ll->positions[charInLine + 1] = ((((startsegx + 2) /
2005 tabWidth) + 1) * tabWidth) - startsegx;
2006 } else if (controlCharSymbol < 32) {
2007 if (ctrlCharWidth[ll->chars[charInLine]] == 0) {
2008 const char *ctrlChar = ControlCharacterString(ll->chars[charInLine]);
2009 // +3 For a blank on front and rounded edge each side:
2010 ctrlCharWidth[ll->chars[charInLine]] =
2011 surface->WidthText(ctrlCharsFont, ctrlChar, istrlen(ctrlChar)) + 3;
2013 ll->positions[charInLine + 1] = ctrlCharWidth[ll->chars[charInLine]];
2014 } else {
2015 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
2016 surface->MeasureWidths(ctrlCharsFont, cc, 1,
2017 ll->positions + startseg + 1);
2019 lastSegItalics = false;
2020 } else if (isBadUTF) {
2021 char hexits[3];
2022 sprintf(hexits, "%2X", ll->chars[charInLine] & 0xff);
2023 ll->positions[charInLine + 1] =
2024 surface->WidthText(ctrlCharsFont, hexits, istrlen(hexits)) + 3;
2025 } else { // Regular character
2026 int lenSeg = charInLine - startseg + 1;
2027 if ((lenSeg == 1) && (' ' == ll->chars[startseg])) {
2028 lastSegItalics = false;
2029 // Over half the segments are single characters and of these about half are space characters.
2030 ll->positions[charInLine + 1] = vstyle.styles[ll->styles[charInLine]].spaceWidth;
2031 } else {
2032 lastSegItalics = vstyle.styles[ll->styles[charInLine]].italic;
2033 posCache.MeasureWidths(surface, vstyle, ll->styles[charInLine], ll->chars + startseg,
2034 lenSeg, ll->positions + startseg + 1);
2037 } else { // invisible
2038 for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
2039 ll->positions[posToZero] = 0;
2042 for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
2043 ll->positions[posToIncrease] += startsegx;
2045 startsegx = ll->positions[charInLine + 1];
2046 startseg = charInLine + 1;
2049 // Small hack to make lines that end with italics not cut off the edge of the last character
2050 if ((startseg > 0) && lastSegItalics) {
2051 ll->positions[startseg] += 2;
2053 ll->numCharsInLine = numCharsInLine;
2054 ll->numCharsBeforeEOL = numCharsBeforeEOL;
2055 ll->validity = LineLayout::llPositions;
2057 // Hard to cope when too narrow, so just assume there is space
2058 if (width < 20) {
2059 width = 20;
2061 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
2062 ll->widthLine = width;
2063 if (width == LineLayout::wrapWidthInfinite) {
2064 ll->lines = 1;
2065 } else if (width > ll->positions[ll->numCharsInLine]) {
2066 // Simple common case where line does not need wrapping.
2067 ll->lines = 1;
2068 } else {
2069 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2070 width -= vstyle.aveCharWidth; // take into account the space for end wrap mark
2072 ll->wrapIndent = wrapAddIndent;
2073 if (wrapIndentMode != SC_WRAPINDENT_FIXED)
2074 for (int i = 0; i < ll->numCharsInLine; i++) {
2075 if (!IsSpaceOrTab(ll->chars[i])) {
2076 ll->wrapIndent += ll->positions[i]; // Add line indent
2077 break;
2080 // Check for text width minimum
2081 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
2082 ll->wrapIndent = wrapAddIndent;
2083 // Check for wrapIndent minimum
2084 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < static_cast<int>(vstyle.aveCharWidth)))
2085 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
2086 ll->lines = 0;
2087 // Calculate line start positions based upon width.
2088 int lastGoodBreak = 0;
2089 int lastLineStart = 0;
2090 int startOffset = 0;
2091 int p = 0;
2092 while (p < ll->numCharsInLine) {
2093 if ((ll->positions[p + 1] - startOffset) >= width) {
2094 if (lastGoodBreak == lastLineStart) {
2095 // Try moving to start of last character
2096 if (p > 0) {
2097 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2098 - posLineStart;
2100 if (lastGoodBreak == lastLineStart) {
2101 // Ensure at least one character on line.
2102 lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
2103 - posLineStart;
2106 lastLineStart = lastGoodBreak;
2107 ll->lines++;
2108 ll->SetLineStart(ll->lines, lastGoodBreak);
2109 startOffset = ll->positions[lastGoodBreak];
2110 // take into account the space for start wrap mark and indent
2111 startOffset -= ll->wrapIndent;
2112 p = lastGoodBreak + 1;
2113 continue;
2115 if (p > 0) {
2116 if (wrapState == eWrapChar) {
2117 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2118 - posLineStart;
2119 p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
2120 continue;
2121 } else if (ll->styles[p] != ll->styles[p - 1]) {
2122 lastGoodBreak = p;
2123 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
2124 lastGoodBreak = p;
2127 p++;
2129 ll->lines++;
2131 ll->validity = LineLayout::llLines;
2135 ColourAllocated Editor::SelectionBackground(ViewStyle &vsDraw, bool main) {
2136 return main ?
2137 (primarySelection ? vsDraw.selbackground.allocated : vsDraw.selbackground2.allocated) :
2138 vsDraw.selAdditionalBackground.allocated;
2141 ColourAllocated Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
2142 ColourAllocated background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) {
2143 if (inSelection == 1) {
2144 if (vsDraw.selbackset && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
2145 return SelectionBackground(vsDraw, true);
2147 } else if (inSelection == 2) {
2148 if (vsDraw.selbackset && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
2149 return SelectionBackground(vsDraw, false);
2151 } else {
2152 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
2153 (i >= ll->edgeColumn) &&
2154 !IsEOLChar(ll->chars[i]))
2155 return vsDraw.edgecolour.allocated;
2156 if (inHotspot && vsDraw.hotspotBackgroundSet)
2157 return vsDraw.hotspotBackground.allocated;
2158 if (overrideBackground && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD))
2159 return background;
2161 return vsDraw.styles[styleMain].back.allocated;
2164 void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
2165 Point from(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
2166 PRectangle rcCopyArea(start + 1, rcSegment.top, start + 2, rcSegment.bottom);
2167 surface->Copy(rcCopyArea, from,
2168 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
2171 void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
2172 bool isEndMarker, ColourAllocated wrapColour) {
2173 surface->PenColour(wrapColour);
2175 enum { xa = 1 }; // gap before start
2176 int w = rcPlace.right - rcPlace.left - xa - 1;
2178 bool xStraight = isEndMarker; // x-mirrored symbol for start marker
2179 bool yStraight = true;
2180 //bool yStraight= isEndMarker; // comment in for start marker y-mirrowed
2182 int x0 = xStraight ? rcPlace.left : rcPlace.right - 1;
2183 int y0 = yStraight ? rcPlace.top : rcPlace.bottom - 1;
2185 int dy = (rcPlace.bottom - rcPlace.top) / 5;
2186 int y = (rcPlace.bottom - rcPlace.top) / 2 + dy;
2188 struct Relative {
2189 Surface *surface;
2190 int xBase;
2191 int xDir;
2192 int yBase;
2193 int yDir;
2194 void MoveTo(int xRelative, int yRelative) {
2195 surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2197 void LineTo(int xRelative, int yRelative) {
2198 surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2201 Relative rel = {surface, x0, xStraight ? 1 : -1, y0, yStraight ? 1 : -1};
2203 // arrow head
2204 rel.MoveTo(xa, y);
2205 rel.LineTo(xa + 2*w / 3, y - dy);
2206 rel.MoveTo(xa, y);
2207 rel.LineTo(xa + 2*w / 3, y + dy);
2209 // arrow body
2210 rel.MoveTo(xa, y);
2211 rel.LineTo(xa + w, y);
2212 rel.LineTo(xa + w, y - 2 * dy);
2213 rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
2214 y - 2 * dy);
2217 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourAllocated fill, int alpha) {
2218 if (alpha != SC_ALPHA_NOALPHA) {
2219 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
2223 void DrawTextBlob(Surface *surface, ViewStyle &vsDraw, PRectangle rcSegment,
2224 const char *s, ColourAllocated textBack, ColourAllocated textFore, bool twoPhaseDraw) {
2225 if (!twoPhaseDraw) {
2226 surface->FillRectangle(rcSegment, textBack);
2228 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2229 int normalCharHeight = surface->Ascent(ctrlCharsFont) -
2230 surface->InternalLeading(ctrlCharsFont);
2231 PRectangle rcCChar = rcSegment;
2232 rcCChar.left = rcCChar.left + 1;
2233 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
2234 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
2235 PRectangle rcCentral = rcCChar;
2236 rcCentral.top++;
2237 rcCentral.bottom--;
2238 surface->FillRectangle(rcCentral, textFore);
2239 PRectangle rcChar = rcCChar;
2240 rcChar.left++;
2241 rcChar.right--;
2242 surface->DrawTextClipped(rcChar, ctrlCharsFont,
2243 rcSegment.top + vsDraw.maxAscent, s, istrlen(s),
2244 textBack, textFore);
2247 void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
2248 int line, int lineEnd, int xStart, int subLine, int subLineStart,
2249 bool overrideBackground, ColourAllocated background,
2250 bool drawWrapMarkEnd, ColourAllocated wrapColour) {
2252 const int posLineStart = pdoc->LineStart(line);
2253 const int styleMask = pdoc->stylingBitsMask;
2254 PRectangle rcSegment = rcLine;
2256 const bool lastSubLine = subLine == (ll->lines - 1);
2257 int virtualSpace = 0;
2258 if (lastSubLine) {
2259 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
2260 virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * spaceWidth;
2263 // Fill in a PRectangle representing the end of line characters
2265 int xEol = ll->positions[lineEnd] - subLineStart;
2267 // Fill the virtual space and show selections within it
2268 if (virtualSpace) {
2269 rcSegment.left = xEol + xStart;
2270 rcSegment.right = xEol + xStart + virtualSpace;
2271 surface->FillRectangle(rcSegment, overrideBackground ? background : vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2272 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
2273 SelectionSegment virtualSpaceRange(SelectionPosition(pdoc->LineEnd(line)), SelectionPosition(pdoc->LineEnd(line), sel.VirtualSpaceFor(pdoc->LineEnd(line))));
2274 for (size_t r=0; r<sel.Count(); r++) {
2275 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2276 if (alpha == SC_ALPHA_NOALPHA) {
2277 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
2278 if (!portion.Empty()) {
2279 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
2280 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
2281 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
2282 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2283 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2284 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == sel.Main()));
2291 int posAfterLineEnd = pdoc->LineStart(line + 1);
2292 int eolInSelection = (subLine == (ll->lines - 1)) ? sel.InSelectionForEOL(posAfterLineEnd) : 0;
2293 int alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2295 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
2296 int blobsWidth = 0;
2297 if (lastSubLine) {
2298 for (int eolPos=ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
2299 rcSegment.left = xStart + ll->positions[eolPos] - subLineStart + virtualSpace;
2300 rcSegment.right = xStart + ll->positions[eolPos+1] - subLineStart + virtualSpace;
2301 blobsWidth += rcSegment.Width();
2302 const char *ctrlChar = ControlCharacterString(ll->chars[eolPos]);
2303 int inSelection = 0;
2304 bool inHotspot = false;
2305 int styleMain = ll->styles[eolPos];
2306 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, eolPos, ll);
2307 ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
2308 if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1)) {
2309 if (alpha == SC_ALPHA_NOALPHA) {
2310 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2311 } else {
2312 surface->FillRectangle(rcSegment, textBack);
2313 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2315 } else {
2316 surface->FillRectangle(rcSegment, textBack);
2318 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
2322 // Draw the eol-is-selected rectangle
2323 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
2324 rcSegment.right = xEol + xStart + virtualSpace + blobsWidth + vsDraw.aveCharWidth;
2326 if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2327 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2328 } else {
2329 if (overrideBackground) {
2330 surface->FillRectangle(rcSegment, background);
2331 } else if (line < pdoc->LinesTotal() - 1) {
2332 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2333 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2334 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2335 } else {
2336 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
2338 if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2339 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2343 // Fill the remainder of the line
2344 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth + vsDraw.aveCharWidth;
2345 rcSegment.right = rcLine.right;
2347 if (!hideSelection && vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2348 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2349 } else {
2350 if (overrideBackground) {
2351 surface->FillRectangle(rcSegment, background);
2352 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2353 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2354 } else {
2355 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
2357 if (!hideSelection && vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2358 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2362 if (drawWrapMarkEnd) {
2363 PRectangle rcPlace = rcSegment;
2365 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
2366 rcPlace.left = xEol + xStart + virtualSpace;
2367 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2368 } else {
2369 // draw left of the right text margin, to avoid clipping by the current clip rect
2370 rcPlace.right = rcLine.right - vs.rightMarginWidth;
2371 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2373 DrawWrapMarker(surface, rcPlace, true, wrapColour);
2377 void Editor::DrawIndicators(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2378 PRectangle rcLine, LineLayout *ll, int subLine, int lineEnd, bool under) {
2379 // Draw decorators
2380 const int posLineStart = pdoc->LineStart(line);
2381 const int lineStart = ll->LineStart(subLine);
2382 const int subLineStart = ll->positions[lineStart];
2383 const int posLineEnd = posLineStart + lineEnd;
2385 if (!under) {
2386 // Draw indicators
2387 // foreach indicator...
2388 for (int indicnum = 0, mask = 1 << pdoc->stylingBits; mask < 0x100; indicnum++) {
2389 if (!(mask & ll->styleBitsSet)) {
2390 mask <<= 1;
2391 continue;
2393 int startPos = -1;
2394 // foreach style pos in line...
2395 for (int indicPos = lineStart; indicPos <= lineEnd; indicPos++) {
2396 // look for starts...
2397 if (startPos < 0) {
2398 // NOT in indicator run, looking for START
2399 if (indicPos < lineEnd && (ll->indicators[indicPos] & mask))
2400 startPos = indicPos;
2402 // ... or ends
2403 if (startPos >= 0) {
2404 // IN indicator run, looking for END
2405 if (indicPos >= lineEnd || !(ll->indicators[indicPos] & mask)) {
2406 // AT end of indicator run, DRAW it!
2407 PRectangle rcIndic(
2408 ll->positions[startPos] + xStart - subLineStart,
2409 rcLine.top + vsDraw.maxAscent,
2410 ll->positions[indicPos] + xStart - subLineStart,
2411 rcLine.top + vsDraw.maxAscent + 3);
2412 vsDraw.indicators[indicnum].Draw(surface, rcIndic, rcLine);
2413 // RESET control var
2414 startPos = -1;
2418 mask <<= 1;
2422 for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
2423 if (under == vsDraw.indicators[deco->indicator].under) {
2424 int startPos = posLineStart + lineStart;
2425 if (!deco->rs.ValueAt(startPos)) {
2426 startPos = deco->rs.EndRun(startPos);
2428 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
2429 int endPos = deco->rs.EndRun(startPos);
2430 if (endPos > posLineEnd)
2431 endPos = posLineEnd;
2432 PRectangle rcIndic(
2433 ll->positions[startPos - posLineStart] + xStart - subLineStart,
2434 rcLine.top + vsDraw.maxAscent,
2435 ll->positions[endPos - posLineStart] + xStart - subLineStart,
2436 rcLine.top + vsDraw.maxAscent + 3);
2437 vsDraw.indicators[deco->indicator].Draw(surface, rcIndic, rcLine);
2438 startPos = deco->rs.EndRun(endPos);
2444 void Editor::DrawAnnotation(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2445 PRectangle rcLine, LineLayout *ll, int subLine) {
2446 int indent = pdoc->GetLineIndentation(line) * vsDraw.spaceWidth;
2447 PRectangle rcSegment = rcLine;
2448 int annotationLine = subLine - ll->lines;
2449 const StyledText stAnnotation = pdoc->AnnotationStyledText(line);
2450 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
2451 surface->FillRectangle(rcSegment, vsDraw.styles[0].back.allocated);
2452 if (vs.annotationVisible == ANNOTATION_BOXED) {
2453 // Only care about calculating width if need to draw box
2454 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
2455 widthAnnotation += vsDraw.spaceWidth * 2; // Margins
2456 rcSegment.left = xStart + indent;
2457 rcSegment.right = rcSegment.left + widthAnnotation;
2458 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore.allocated);
2459 } else {
2460 rcSegment.left = xStart;
2462 const int annotationLines = pdoc->AnnotationLines(line);
2463 size_t start = 0;
2464 size_t lengthAnnotation = stAnnotation.LineLength(start);
2465 int lineInAnnotation = 0;
2466 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
2467 start += lengthAnnotation + 1;
2468 lengthAnnotation = stAnnotation.LineLength(start);
2469 lineInAnnotation++;
2471 PRectangle rcText = rcSegment;
2472 if (vs.annotationVisible == ANNOTATION_BOXED) {
2473 surface->FillRectangle(rcText,
2474 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back.allocated);
2475 rcText.left += vsDraw.spaceWidth;
2477 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText, rcText.top + vsDraw.maxAscent,
2478 stAnnotation, start, lengthAnnotation);
2479 if (vs.annotationVisible == ANNOTATION_BOXED) {
2480 surface->MoveTo(rcSegment.left, rcSegment.top);
2481 surface->LineTo(rcSegment.left, rcSegment.bottom);
2482 surface->MoveTo(rcSegment.right, rcSegment.top);
2483 surface->LineTo(rcSegment.right, rcSegment.bottom);
2484 if (subLine == ll->lines){
2485 surface->MoveTo(rcSegment.left, rcSegment.top);
2486 surface->LineTo(rcSegment.right, rcSegment.top);
2488 if (subLine == ll->lines+annotationLines-1) {
2489 surface->MoveTo(rcSegment.left, rcSegment.bottom - 1);
2490 surface->LineTo(rcSegment.right, rcSegment.bottom - 1);
2496 void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
2497 PRectangle rcLine, LineLayout *ll, int subLine) {
2499 PRectangle rcSegment = rcLine;
2501 // Using one font for all control characters so it can be controlled independently to ensure
2502 // the box goes around the characters tightly. Seems to be no way to work out what height
2503 // is taken by an individual character - internal leading gives varying results.
2504 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2506 // See if something overrides the line background color: Either if caret is on the line
2507 // and background color is set for that, or if a marker is defined that forces its background
2508 // color onto the line, or if a marker is defined but has no selection margin in which to
2509 // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
2510 // with the earlier taking precedence. When multiple markers cause background override,
2511 // the color for the highest numbered one is used.
2512 bool overrideBackground = false;
2513 ColourAllocated background;
2514 if (caret.active && vsDraw.showCaretLineBackground && (vsDraw.caretLineAlpha == SC_ALPHA_NOALPHA) && ll->containsCaret) {
2515 overrideBackground = true;
2516 background = vsDraw.caretLineBackground.allocated;
2518 if (!overrideBackground) {
2519 int marks = pdoc->GetMark(line);
2520 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
2521 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) &&
2522 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2523 background = vsDraw.markers[markBit].back.allocated;
2524 overrideBackground = true;
2526 marks >>= 1;
2529 if (!overrideBackground) {
2530 if (vsDraw.maskInLine) {
2531 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
2532 if (marksMasked) {
2533 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
2534 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY) &&
2535 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2536 overrideBackground = true;
2537 background = vsDraw.markers[markBit].back.allocated;
2539 marksMasked >>= 1;
2545 bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
2546 (!overrideBackground) && (vsDraw.whitespaceBackgroundSet);
2548 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2549 int indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
2551 int posLineStart = pdoc->LineStart(line);
2553 int startseg = ll->LineStart(subLine);
2554 int subLineStart = ll->positions[startseg];
2555 if (subLine >= ll->lines) {
2556 DrawAnnotation(surface, vsDraw, line, xStart, rcLine, ll, subLine);
2557 return; // No further drawing
2559 int lineStart = 0;
2560 int lineEnd = 0;
2561 if (subLine < ll->lines) {
2562 lineStart = ll->LineStart(subLine);
2563 lineEnd = ll->LineStart(subLine + 1);
2564 if (subLine == ll->lines - 1) {
2565 lineEnd = ll->numCharsBeforeEOL;
2569 ColourAllocated wrapColour = vsDraw.styles[STYLE_DEFAULT].fore.allocated;
2570 if (vsDraw.whitespaceForegroundSet)
2571 wrapColour = vsDraw.whitespaceForeground.allocated;
2573 bool drawWrapMarkEnd = false;
2575 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2576 if (subLine + 1 < ll->lines) {
2577 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
2581 if (ll->wrapIndent != 0) {
2583 bool continuedWrapLine = false;
2584 if (subLine < ll->lines) {
2585 continuedWrapLine = ll->LineStart(subLine) != 0;
2588 if (continuedWrapLine) {
2589 // draw continuation rect
2590 PRectangle rcPlace = rcSegment;
2592 rcPlace.left = ll->positions[startseg] + xStart - subLineStart;
2593 rcPlace.right = rcPlace.left + ll->wrapIndent;
2595 // default bgnd here..
2596 surface->FillRectangle(rcSegment, overrideBackground ? background :
2597 vsDraw.styles[STYLE_DEFAULT].back.allocated);
2599 // main line style would be below but this would be inconsistent with end markers
2600 // also would possibly not be the style at wrap point
2601 //int styleMain = ll->styles[lineStart];
2602 //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back.allocated);
2604 if (wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
2606 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
2607 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2608 else
2609 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2611 DrawWrapMarker(surface, rcPlace, false, wrapColour);
2614 xStart += ll->wrapIndent;
2618 bool selBackDrawn = vsDraw.selbackset &&
2619 ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA));
2621 // Does not take margin into account but not significant
2622 int xStartVisible = subLineStart - xStart;
2624 ll->psel = &sel;
2626 BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, IsUnicodeMode(), xStartVisible, selBackDrawn);
2627 int next = bfBack.First();
2629 // Background drawing loop
2630 while (twoPhaseDraw && (next < lineEnd)) {
2632 startseg = next;
2633 next = bfBack.Next();
2634 int i = next - 1;
2635 int iDoc = i + posLineStart;
2637 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2638 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2639 // Only try to draw if really visible - enhances performance by not calling environment to
2640 // draw strings that are completely past the right side of the window.
2641 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2642 // Clip to line rectangle, since may have a huge position which will not work with some platforms
2643 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2644 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2646 int styleMain = ll->styles[i];
2647 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2648 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2649 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2650 if (ll->chars[i] == '\t') {
2651 // Tab display
2652 if (drawWhitespaceBackground &&
2653 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2654 textBack = vsDraw.whitespaceBackground.allocated;
2655 surface->FillRectangle(rcSegment, textBack);
2656 } else if (IsControlCharacter(ll->chars[i])) {
2657 // Control character display
2658 inIndentation = false;
2659 surface->FillRectangle(rcSegment, textBack);
2660 } else {
2661 // Normal text display
2662 surface->FillRectangle(rcSegment, textBack);
2663 if (vsDraw.viewWhitespace != wsInvisible ||
2664 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
2665 for (int cpos = 0; cpos <= i - startseg; cpos++) {
2666 if (ll->chars[cpos + startseg] == ' ') {
2667 if (drawWhitespaceBackground &&
2668 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
2669 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
2670 rcSegment.top,
2671 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
2672 rcSegment.bottom);
2673 surface->FillRectangle(rcSpace, vsDraw.whitespaceBackground.allocated);
2675 } else {
2676 inIndentation = false;
2681 } else if (rcSegment.left > rcLine.right) {
2682 break;
2686 if (twoPhaseDraw) {
2687 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
2688 xStart, subLine, subLineStart, overrideBackground, background,
2689 drawWrapMarkEnd, wrapColour);
2692 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, true);
2694 if (vsDraw.edgeState == EDGE_LINE) {
2695 int edgeX = theEdge * vsDraw.spaceWidth;
2696 rcSegment.left = edgeX + xStart;
2697 rcSegment.right = rcSegment.left + 1;
2698 surface->FillRectangle(rcSegment, vsDraw.edgecolour.allocated);
2701 // Draw underline mark as part of background if not transparent
2702 int marks = pdoc->GetMark(line);
2703 int markBit;
2704 for (markBit = 0; (markBit < 32) && marks; markBit++) {
2705 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
2706 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2707 PRectangle rcUnderline = rcLine;
2708 rcUnderline.top = rcUnderline.bottom - 2;
2709 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back.allocated);
2711 marks >>= 1;
2714 inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2715 // Foreground drawing loop
2716 BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, IsUnicodeMode(), xStartVisible,
2717 ((!twoPhaseDraw && selBackDrawn) || vsDraw.selforeset));
2718 next = bfFore.First();
2720 while (next < lineEnd) {
2722 startseg = next;
2723 next = bfFore.Next();
2724 int i = next - 1;
2726 int iDoc = i + posLineStart;
2728 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2729 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2730 // Only try to draw if really visible - enhances performance by not calling environment to
2731 // draw strings that are completely past the right side of the window.
2732 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2733 int styleMain = ll->styles[i];
2734 ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
2735 Font &textFont = vsDraw.styles[styleMain].font;
2736 //hotspot foreground
2737 if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) {
2738 if (vsDraw.hotspotForegroundSet)
2739 textFore = vsDraw.hotspotForeground.allocated;
2741 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2742 if (inSelection && (vsDraw.selforeset)) {
2743 textFore = (inSelection == 1) ? vsDraw.selforeground.allocated : vsDraw.selAdditionalForeground.allocated;
2745 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2746 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2747 if (ll->chars[i] == '\t') {
2748 // Tab display
2749 if (!twoPhaseDraw) {
2750 if (drawWhitespaceBackground &&
2751 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2752 textBack = vsDraw.whitespaceBackground.allocated;
2753 surface->FillRectangle(rcSegment, textBack);
2755 if ((vsDraw.viewWhitespace != wsInvisible) ||
2756 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
2757 if (vsDraw.whitespaceForegroundSet)
2758 textFore = vsDraw.whitespaceForeground.allocated;
2759 surface->PenColour(textFore);
2761 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
2762 for (int xIG = ll->positions[i] / indentWidth * indentWidth; xIG < ll->positions[i + 1]; xIG += indentWidth) {
2763 if (xIG >= ll->positions[i] && xIG > 0) {
2764 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIG + xStart, rcSegment,
2765 (ll->xHighlightGuide == xIG));
2769 if (vsDraw.viewWhitespace != wsInvisible) {
2770 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
2771 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
2772 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
2773 DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
2776 } else if (IsControlCharacter(ll->chars[i])) {
2777 // Control character display
2778 inIndentation = false;
2779 if (controlCharSymbol < 32) {
2780 // Draw the character
2781 const char *ctrlChar = ControlCharacterString(ll->chars[i]);
2782 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
2783 } else {
2784 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
2785 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
2786 rcSegment.top + vsDraw.maxAscent,
2787 cc, 1, textBack, textFore);
2789 } else if ((i == startseg) && (static_cast<unsigned char>(ll->chars[i]) >= 0x80) && IsUnicodeMode()) {
2790 char hexits[3];
2791 sprintf(hexits, "%2X", ll->chars[i] & 0xff);
2792 DrawTextBlob(surface, vsDraw, rcSegment, hexits, textBack, textFore, twoPhaseDraw);
2793 } else {
2794 // Normal text display
2795 if (vsDraw.styles[styleMain].visible) {
2796 if (twoPhaseDraw) {
2797 surface->DrawTextTransparent(rcSegment, textFont,
2798 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
2799 i - startseg + 1, textFore);
2800 } else {
2801 surface->DrawTextNoClip(rcSegment, textFont,
2802 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
2803 i - startseg + 1, textFore, textBack);
2806 if (vsDraw.viewWhitespace != wsInvisible ||
2807 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
2808 for (int cpos = 0; cpos <= i - startseg; cpos++) {
2809 if (ll->chars[cpos + startseg] == ' ') {
2810 if (vsDraw.viewWhitespace != wsInvisible) {
2811 if (vsDraw.whitespaceForegroundSet)
2812 textFore = vsDraw.whitespaceForeground.allocated;
2813 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
2814 int xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2;
2815 if (!twoPhaseDraw && drawWhitespaceBackground &&
2816 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
2817 textBack = vsDraw.whitespaceBackground.allocated;
2818 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
2819 rcSegment.top,
2820 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
2821 rcSegment.bottom);
2822 surface->FillRectangle(rcSpace, textBack);
2824 PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
2825 rcDot.right = rcDot.left + vs.whitespaceSize;
2826 rcDot.bottom = rcDot.top + vs.whitespaceSize;
2827 surface->FillRectangle(rcDot, textFore);
2830 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
2831 int startSpace = ll->positions[cpos + startseg];
2832 if (startSpace > 0 && (startSpace % indentWidth == 0)) {
2833 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, startSpace + xStart, rcSegment,
2834 (ll->xHighlightGuide == ll->positions[cpos + startseg]));
2837 } else {
2838 inIndentation = false;
2843 if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd ) {
2844 PRectangle rcUL = rcSegment;
2845 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
2846 rcUL.bottom = rcUL.top + 1;
2847 if (vsDraw.hotspotForegroundSet)
2848 surface->FillRectangle(rcUL, vsDraw.hotspotForeground.allocated);
2849 else
2850 surface->FillRectangle(rcUL, textFore);
2851 } else if (vsDraw.styles[styleMain].underline) {
2852 PRectangle rcUL = rcSegment;
2853 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
2854 rcUL.bottom = rcUL.top + 1;
2855 surface->FillRectangle(rcUL, textFore);
2857 } else if (rcSegment.left > rcLine.right) {
2858 break;
2861 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
2862 && (subLine == 0)) {
2863 int indentSpace = pdoc->GetLineIndentation(line);
2864 int xStartText = ll->positions[pdoc->GetLineIndentPosition(line) - posLineStart];
2866 // Find the most recent line with some text
2868 int lineLastWithText = line;
2869 while (lineLastWithText > Platform::Maximum(line-20, 0) && pdoc->IsWhiteLine(lineLastWithText)) {
2870 lineLastWithText--;
2872 if (lineLastWithText < line) {
2873 xStartText = 100000; // Don't limit to visible indentation on empty line
2874 // This line is empty, so use indentation of last line with text
2875 int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText);
2876 int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
2877 if (isFoldHeader) {
2878 // Level is one more level than parent
2879 indentLastWithText += pdoc->IndentSize();
2881 if (vsDraw.viewIndentationGuides == ivLookForward) {
2882 // In viLookForward mode, previous line only used if it is a fold header
2883 if (isFoldHeader) {
2884 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
2886 } else { // viLookBoth
2887 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
2891 int lineNextWithText = line;
2892 while (lineNextWithText < Platform::Minimum(line+20, pdoc->LinesTotal()) && pdoc->IsWhiteLine(lineNextWithText)) {
2893 lineNextWithText++;
2895 if (lineNextWithText > line) {
2896 // This line is empty, so use indentation of last line with text
2897 indentSpace = Platform::Maximum(indentSpace,
2898 pdoc->GetLineIndentation(lineNextWithText));
2901 for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) {
2902 int xIndent = indentPos * vsDraw.spaceWidth;
2903 if (xIndent < xStartText) {
2904 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
2905 (ll->xHighlightGuide == xIndent));
2910 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, false);
2912 // End of the drawing of the current line
2913 if (!twoPhaseDraw) {
2914 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
2915 xStart, subLine, subLineStart, overrideBackground, background,
2916 drawWrapMarkEnd, wrapColour);
2918 if (!hideSelection && ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA))) {
2919 // For each selection draw
2920 int virtualSpaces = 0;
2921 if (subLine == (ll->lines - 1)) {
2922 virtualSpaces = sel.VirtualSpaceFor(pdoc->LineEnd(line));
2924 SelectionPosition posStart(posLineStart);
2925 SelectionPosition posEnd(posLineStart + lineEnd, virtualSpaces);
2926 SelectionSegment virtualSpaceRange(posStart, posEnd);
2927 for (size_t r=0; r<sel.Count(); r++) {
2928 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2929 if (alpha != SC_ALPHA_NOALPHA) {
2930 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
2931 if (!portion.Empty()) {
2932 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
2933 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
2934 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
2935 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2936 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2937 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == sel.Main()), alpha);
2943 // Draw any translucent whole line states
2944 rcSegment.left = xStart;
2945 rcSegment.right = rcLine.right - 1;
2946 if (caret.active && vsDraw.showCaretLineBackground && ll->containsCaret) {
2947 SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground.allocated, vsDraw.caretLineAlpha);
2949 marks = pdoc->GetMark(line);
2950 for (markBit = 0; (markBit < 32) && marks; markBit++) {
2951 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
2952 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
2953 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
2954 PRectangle rcUnderline = rcSegment;
2955 rcUnderline.top = rcUnderline.bottom - 2;
2956 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
2958 marks >>= 1;
2960 if (vsDraw.maskInLine) {
2961 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
2962 if (marksMasked) {
2963 for (markBit = 0; (markBit < 32) && marksMasked; markBit++) {
2964 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
2965 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
2967 marksMasked >>= 1;
2973 void Editor::DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine,
2974 int xStart, int offset, int posCaret, PRectangle rcCaret, ColourAllocated caretColour) {
2976 int lineStart = ll->LineStart(subLine);
2977 int posBefore = posCaret;
2978 int posAfter = MovePositionOutsideChar(posCaret + 1, 1);
2979 int numCharsToDraw = posAfter - posCaret;
2981 // Work out where the starting and ending offsets are. We need to
2982 // see if the previous character shares horizontal space, such as a
2983 // glyph / combining character. If so we'll need to draw that too.
2984 int offsetFirstChar = offset;
2985 int offsetLastChar = offset + (posAfter - posCaret);
2986 while ((offsetLastChar - numCharsToDraw) >= lineStart) {
2987 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
2988 // The char does not share horizontal space
2989 break;
2991 // Char shares horizontal space, update the numChars to draw
2992 // Update posBefore to point to the prev char
2993 posBefore = MovePositionOutsideChar(posBefore - 1, -1);
2994 numCharsToDraw = posAfter - posBefore;
2995 offsetFirstChar = offset - (posCaret - posBefore);
2998 // See if the next character shares horizontal space, if so we'll
2999 // need to draw that too.
3000 numCharsToDraw = offsetLastChar - offsetFirstChar;
3001 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
3002 // Update posAfter to point to the 2nd next char, this is where
3003 // the next character ends, and 2nd next begins. We'll need
3004 // to compare these two
3005 posBefore = posAfter;
3006 posAfter = MovePositionOutsideChar(posAfter + 1, 1);
3007 offsetLastChar = offset + (posAfter - posCaret);
3008 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
3009 // The char does not share horizontal space
3010 break;
3012 // Char shares horizontal space, update the numChars to draw
3013 numCharsToDraw = offsetLastChar - offsetFirstChar;
3016 // We now know what to draw, update the caret drawing rectangle
3017 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
3018 rcCaret.right = ll->positions[offsetFirstChar+numCharsToDraw] - ll->positions[lineStart] + xStart;
3020 // Adjust caret position to take into account any word wrapping symbols.
3021 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3022 int wordWrapCharWidth = ll->wrapIndent;
3023 rcCaret.left += wordWrapCharWidth;
3024 rcCaret.right += wordWrapCharWidth;
3027 // This character is where the caret block is, we override the colours
3028 // (inversed) for drawing the caret here.
3029 int styleMain = ll->styles[offsetFirstChar];
3030 surface->DrawTextClipped(rcCaret, vsDraw.styles[styleMain].font,
3031 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
3032 numCharsToDraw, vsDraw.styles[styleMain].back.allocated,
3033 caretColour);
3036 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
3037 if (!pixmapSelPattern->Initialised()) {
3038 const int patternSize = 8;
3039 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3040 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
3041 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
3042 // way between the chrome colour and the chrome highlight colour making a nice transition
3043 // between the window chrome and the content area. And it works in low colour depths.
3044 PRectangle rcPattern(0, 0, patternSize, patternSize);
3046 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
3047 ColourAllocated colourFMFill = vs.selbar.allocated;
3048 ColourAllocated colourFMStripes = vs.selbarlight.allocated;
3050 if (!(vs.selbarlight.desired == ColourDesired(0xff, 0xff, 0xff))) {
3051 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
3052 // (Typically, the highlight colour is white.)
3053 colourFMFill = vs.selbarlight.allocated;
3056 if (vs.foldmarginColourSet) {
3057 // override default fold margin colour
3058 colourFMFill = vs.foldmarginColour.allocated;
3060 if (vs.foldmarginHighlightColourSet) {
3061 // override default fold margin highlight colour
3062 colourFMStripes = vs.foldmarginHighlightColour.allocated;
3065 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
3066 pixmapSelPattern->PenColour(colourFMStripes);
3067 for (int stripe = 0; stripe < patternSize; stripe++) {
3068 // Alternating 1 pixel stripes is same as checkerboard.
3069 pixmapSelPattern->MoveTo(0, stripe * 2);
3070 pixmapSelPattern->LineTo(patternSize, stripe * 2 - patternSize);
3074 if (!pixmapIndentGuide->Initialised()) {
3075 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
3076 pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3077 pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3078 PRectangle rcIG(0, 0, 1, vs.lineHeight);
3079 pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back.allocated);
3080 pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore.allocated);
3081 pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back.allocated);
3082 pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore.allocated);
3083 for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
3084 pixmapIndentGuide->MoveTo(0, stripe);
3085 pixmapIndentGuide->LineTo(2, stripe);
3086 pixmapIndentGuideHighlight->MoveTo(0, stripe);
3087 pixmapIndentGuideHighlight->LineTo(2, stripe);
3091 if (bufferedDraw) {
3092 if (!pixmapLine->Initialised()) {
3093 PRectangle rcClient = GetClientRectangle();
3094 pixmapLine->InitPixMap(rcClient.Width(), vs.lineHeight,
3095 surfaceWindow, wMain.GetID());
3096 pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
3097 rcClient.Height(), surfaceWindow, wMain.GetID());
3102 void Editor::DrawCarets(Surface *surface, ViewStyle &vsDraw, int lineDoc, int xStart,
3103 PRectangle rcLine, LineLayout *ll, int subLine) {
3104 // When drag is active it is the only caret drawn
3105 bool drawDrag = posDrag.IsValid();
3106 if (hideSelection && !drawDrag)
3107 return;
3108 const int posLineStart = pdoc->LineStart(lineDoc);
3109 // For each selection draw
3110 for (size_t r=0; (r<sel.Count()) || drawDrag; r++) {
3111 const bool mainCaret = r == sel.Main();
3112 const SelectionPosition posCaret = (drawDrag ? posDrag : sel.Range(r).caret);
3113 const int offset = posCaret.Position() - posLineStart;
3114 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
3115 const int virtualOffset = posCaret.VirtualSpace() * spaceWidth;
3116 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
3117 int xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
3118 if (ll->wrapIndent != 0) {
3119 int lineStart = ll->LineStart(subLine);
3120 if (lineStart != 0) // Wrapped
3121 xposCaret += ll->wrapIndent;
3123 bool caretBlinkState = (caret.active && caret.on) || (!additionalCaretsBlink && !mainCaret);
3124 bool caretVisibleState = additionalCaretsVisible || mainCaret;
3125 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
3126 ((posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
3127 bool caretAtEOF = false;
3128 bool caretAtEOL = false;
3129 bool drawBlockCaret = false;
3130 int widthOverstrikeCaret;
3131 int caretWidthOffset = 0;
3132 PRectangle rcCaret = rcLine;
3134 if (posCaret.Position() == pdoc->Length()) { // At end of document
3135 caretAtEOF = true;
3136 widthOverstrikeCaret = vsDraw.aveCharWidth;
3137 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
3138 caretAtEOL = true;
3139 widthOverstrikeCaret = vsDraw.aveCharWidth;
3140 } else {
3141 widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
3143 if (widthOverstrikeCaret < 3) // Make sure its visible
3144 widthOverstrikeCaret = 3;
3146 if (xposCaret > 0)
3147 caretWidthOffset = 1; // Move back so overlaps both character cells.
3148 xposCaret += xStart;
3149 if (posDrag.IsValid()) {
3150 /* Dragging text, use a line caret */
3151 rcCaret.left = xposCaret - caretWidthOffset;
3152 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3153 } else if (inOverstrike) {
3154 /* Overstrike (insert mode), use a modified bar caret */
3155 rcCaret.top = rcCaret.bottom - 2;
3156 rcCaret.left = xposCaret + 1;
3157 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
3158 } else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) {
3159 /* Block caret */
3160 rcCaret.left = xposCaret;
3161 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
3162 drawBlockCaret = true;
3163 rcCaret.right = xposCaret + widthOverstrikeCaret;
3164 } else {
3165 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
3167 } else {
3168 /* Line caret */
3169 rcCaret.left = xposCaret - caretWidthOffset;
3170 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3172 ColourAllocated caretColour = mainCaret ? vsDraw.caretcolour.allocated : vsDraw.additionalCaretColour.allocated;
3173 if (drawBlockCaret) {
3174 DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
3175 } else {
3176 surface->FillRectangle(rcCaret, caretColour);
3180 if (drawDrag)
3181 break;
3185 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
3186 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
3187 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
3189 pixmapLine->Release();
3190 RefreshStyleData();
3191 RefreshPixMaps(surfaceWindow);
3193 PRectangle rcClient = GetClientRectangle();
3194 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
3195 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3197 surfaceWindow->SetPalette(&palette, true);
3198 pixmapLine->SetPalette(&palette, !hasFocus);
3200 int screenLinePaintFirst = rcArea.top / vs.lineHeight;
3201 // The area to be painted plus one extra line is styled.
3202 // The extra line is to determine when a style change, such as starting a comment flows on to other lines.
3203 int lineStyleLast = topLine + (rcArea.bottom - 1) / vs.lineHeight + 1;
3204 //Platform::DebugPrintf("Paint lines = %d .. %d\n", topLine + screenLinePaintFirst, lineStyleLast);
3205 int endPosPaint = pdoc->Length();
3206 if (lineStyleLast < cs.LinesDisplayed())
3207 endPosPaint = pdoc->LineStart(cs.DocFromDisplay(lineStyleLast) + 1);
3209 int xStart = vs.fixedColumnWidth - xOffset;
3210 int ypos = 0;
3211 if (!bufferedDraw)
3212 ypos += screenLinePaintFirst * vs.lineHeight;
3213 int yposScreen = screenLinePaintFirst * vs.lineHeight;
3215 // Ensure we are styled as far as we are painting.
3216 pdoc->EnsureStyledTo(endPosPaint);
3217 bool paintAbandonedByStyling = paintState == paintAbandoned;
3218 if (needUpdateUI) {
3219 // Deselect palette by selecting a temporary palette
3220 Palette palTemp;
3221 surfaceWindow->SetPalette(&palTemp, true);
3223 NotifyUpdateUI();
3224 needUpdateUI = false;
3226 RefreshStyleData();
3227 RefreshPixMaps(surfaceWindow);
3228 surfaceWindow->SetPalette(&palette, true);
3229 pixmapLine->SetPalette(&palette, !hasFocus);
3232 // Call priority lines wrap on a window of lines which are likely
3233 // to rendered with the following paint (that is wrap the visible
3234 // lines first).
3235 int startLineToWrap = cs.DocFromDisplay(topLine) - 5;
3236 if (startLineToWrap < 0)
3237 startLineToWrap = 0;
3238 if (WrapLines(false, startLineToWrap)) {
3239 // The wrapping process has changed the height of some lines so
3240 // abandon this paint for a complete repaint.
3241 if (AbandonPaint()) {
3242 return;
3244 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
3246 PLATFORM_ASSERT(pixmapSelPattern->Initialised());
3248 PaintSelMargin(surfaceWindow, rcArea);
3250 PRectangle rcRightMargin = rcClient;
3251 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
3252 if (rcArea.Intersects(rcRightMargin)) {
3253 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back.allocated);
3256 if (paintState == paintAbandoned) {
3257 // Either styling or NotifyUpdateUI noticed that painting is needed
3258 // outside the current painting rectangle
3259 //Platform::DebugPrintf("Abandoning paint\n");
3260 if (wrapState != eWrapNone) {
3261 if (paintAbandonedByStyling) {
3262 // Styling has spilled over a line end, such as occurs by starting a multiline
3263 // comment. The width of subsequent text may have changed, so rewrap.
3264 NeedWrapping(cs.DocFromDisplay(topLine));
3267 return;
3269 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
3271 // Do the painting
3272 if (rcArea.right > vs.fixedColumnWidth) {
3274 Surface *surface = surfaceWindow;
3275 if (bufferedDraw) {
3276 surface = pixmapLine;
3277 PLATFORM_ASSERT(pixmapLine->Initialised());
3279 surface->SetUnicodeMode(IsUnicodeMode());
3280 surface->SetDBCSMode(CodePage());
3282 int visibleLine = topLine + screenLinePaintFirst;
3284 SelectionPosition posCaret = sel.RangeMain().caret;
3285 if (posDrag.IsValid())
3286 posCaret = posDrag;
3287 int lineCaret = pdoc->LineFromPosition(posCaret.Position());
3289 // Remove selection margin from drawing area so text will not be drawn
3290 // on it in unbuffered mode.
3291 PRectangle rcTextArea = rcClient;
3292 rcTextArea.left = vs.fixedColumnWidth;
3293 rcTextArea.right -= vs.rightMarginWidth;
3294 surfaceWindow->SetClip(rcTextArea);
3296 // Loop on visible lines
3297 //double durLayout = 0.0;
3298 //double durPaint = 0.0;
3299 //double durCopy = 0.0;
3300 //ElapsedTime etWhole;
3301 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
3302 AutoLineLayout ll(llc, 0);
3303 while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
3305 int lineDoc = cs.DocFromDisplay(visibleLine);
3306 // Only visible lines should be handled by the code within the loop
3307 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
3308 int lineStartSet = cs.DisplayFromDoc(lineDoc);
3309 int subLine = visibleLine - lineStartSet;
3311 // Copy this line and its styles from the document into local arrays
3312 // and determine the x position at which each character starts.
3313 //ElapsedTime et;
3314 if (lineDoc != lineDocPrevious) {
3315 ll.Set(0);
3316 ll.Set(RetrieveLineLayout(lineDoc));
3317 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
3318 lineDocPrevious = lineDoc;
3320 //durLayout += et.Duration(true);
3322 if (ll) {
3323 ll->containsCaret = lineDoc == lineCaret;
3324 if (hideSelection) {
3325 ll->containsCaret = false;
3328 GetHotSpotRange(ll->hsStart, ll->hsEnd);
3330 PRectangle rcLine = rcClient;
3331 rcLine.top = ypos;
3332 rcLine.bottom = ypos + vs.lineHeight;
3334 Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
3335 // Highlight the current braces if any
3336 ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
3337 highlightGuideColumn * vs.spaceWidth);
3339 // Draw the line
3340 DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
3341 //durPaint += et.Duration(true);
3343 // Restore the previous styles for the brace highlights in case layout is in cache.
3344 ll->RestoreBracesHighlight(rangeLine, braces);
3346 bool expanded = cs.GetExpanded(lineDoc);
3347 // Paint the line above the fold
3348 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
3350 (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
3351 if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
3352 PRectangle rcFoldLine = rcLine;
3353 rcFoldLine.bottom = rcFoldLine.top + 1;
3354 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
3357 // Paint the line below the fold
3358 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
3360 (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
3361 if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
3362 PRectangle rcFoldLine = rcLine;
3363 rcFoldLine.top = rcFoldLine.bottom - 1;
3364 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
3368 DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine);
3370 if (bufferedDraw) {
3371 Point from(vs.fixedColumnWidth, 0);
3372 PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen,
3373 rcClient.right, yposScreen + vs.lineHeight);
3374 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
3377 lineWidthMaxSeen = Platform::Maximum(
3378 lineWidthMaxSeen, ll->positions[ll->numCharsInLine]);
3379 //durCopy += et.Duration(true);
3382 if (!bufferedDraw) {
3383 ypos += vs.lineHeight;
3386 yposScreen += vs.lineHeight;
3387 visibleLine++;
3389 //gdk_flush();
3391 ll.Set(0);
3392 //if (durPaint < 0.00000001)
3393 // durPaint = 0.00000001;
3395 // Right column limit indicator
3396 PRectangle rcBeyondEOF = rcClient;
3397 rcBeyondEOF.left = vs.fixedColumnWidth;
3398 rcBeyondEOF.right = rcBeyondEOF.right;
3399 rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight;
3400 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
3401 surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back.allocated);
3402 if (vs.edgeState == EDGE_LINE) {
3403 int edgeX = theEdge * vs.spaceWidth;
3404 rcBeyondEOF.left = edgeX + xStart;
3405 rcBeyondEOF.right = rcBeyondEOF.left + 1;
3406 surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour.allocated);
3409 //Platform::DebugPrintf(
3410 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
3411 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
3412 NotifyPainted();
3416 // Space (3 space characters) between line numbers and text when printing.
3417 #define lineNumberPrintSpace " "
3419 ColourDesired InvertedLight(ColourDesired orig) {
3420 unsigned int r = orig.GetRed();
3421 unsigned int g = orig.GetGreen();
3422 unsigned int b = orig.GetBlue();
3423 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
3424 unsigned int il = 0xff - l;
3425 if (l == 0)
3426 return ColourDesired(0xff, 0xff, 0xff);
3427 r = r * il / l;
3428 g = g * il / l;
3429 b = b * il / l;
3430 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
3433 // This is mostly copied from the Paint method but with some things omitted
3434 // such as the margin markers, line numbers, selection and caret
3435 // Should be merged back into a combined Draw method.
3436 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
3437 if (!pfr)
3438 return 0;
3440 AutoSurface surface(pfr->hdc, this);
3441 if (!surface)
3442 return 0;
3443 AutoSurface surfaceMeasure(pfr->hdcTarget, this);
3444 if (!surfaceMeasure) {
3445 return 0;
3448 // Can't use measurements cached for screen
3449 posCache.Clear();
3451 ViewStyle vsPrint(vs);
3453 // Modify the view style for printing as do not normally want any of the transient features to be printed
3454 // Printing supports only the line number margin.
3455 int lineNumberIndex = -1;
3456 for (int margin = 0; margin < ViewStyle::margins; margin++) {
3457 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
3458 lineNumberIndex = margin;
3459 } else {
3460 vsPrint.ms[margin].width = 0;
3463 vsPrint.showMarkedLines = false;
3464 vsPrint.fixedColumnWidth = 0;
3465 vsPrint.zoomLevel = printMagnification;
3466 vsPrint.viewIndentationGuides = ivNone;
3467 // Don't show the selection when printing
3468 vsPrint.selbackset = false;
3469 vsPrint.selforeset = false;
3470 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
3471 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
3472 vsPrint.whitespaceBackgroundSet = false;
3473 vsPrint.whitespaceForegroundSet = false;
3474 vsPrint.showCaretLineBackground = false;
3476 // Set colours for printing according to users settings
3477 for (size_t sty = 0;sty < vsPrint.stylesSize;sty++) {
3478 if (printColourMode == SC_PRINT_INVERTLIGHT) {
3479 vsPrint.styles[sty].fore.desired = InvertedLight(vsPrint.styles[sty].fore.desired);
3480 vsPrint.styles[sty].back.desired = InvertedLight(vsPrint.styles[sty].back.desired);
3481 } else if (printColourMode == SC_PRINT_BLACKONWHITE) {
3482 vsPrint.styles[sty].fore.desired = ColourDesired(0, 0, 0);
3483 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3484 } else if (printColourMode == SC_PRINT_COLOURONWHITE) {
3485 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3486 } else if (printColourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
3487 if (sty <= STYLE_DEFAULT) {
3488 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3492 // White background for the line numbers
3493 vsPrint.styles[STYLE_LINENUMBER].back.desired = ColourDesired(0xff, 0xff, 0xff);
3495 vsPrint.Refresh(*surfaceMeasure);
3496 // Determining width must hapen after fonts have been realised in Refresh
3497 int lineNumberWidth = 0;
3498 if (lineNumberIndex >= 0) {
3499 lineNumberWidth = surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
3500 "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace));
3501 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
3502 vsPrint.Refresh(*surfaceMeasure); // Recalculate fixedColumnWidth
3504 // Ensure colours are set up
3505 vsPrint.RefreshColourPalette(palette, true);
3506 vsPrint.RefreshColourPalette(palette, false);
3508 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
3509 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
3510 if (linePrintLast < linePrintStart)
3511 linePrintLast = linePrintStart;
3512 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
3513 if (linePrintLast > linePrintMax)
3514 linePrintLast = linePrintMax;
3515 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
3516 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
3517 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
3518 int endPosPrint = pdoc->Length();
3519 if (linePrintLast < pdoc->LinesTotal())
3520 endPosPrint = pdoc->LineStart(linePrintLast + 1);
3522 // Ensure we are styled to where we are formatting.
3523 pdoc->EnsureStyledTo(endPosPrint);
3525 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
3526 int ypos = pfr->rc.top;
3528 int lineDoc = linePrintStart;
3530 int nPrintPos = pfr->chrg.cpMin;
3531 int visibleLine = 0;
3532 int widthPrint = pfr->rc.Width() - vsPrint.fixedColumnWidth;
3533 if (printWrapState == eWrapNone)
3534 widthPrint = LineLayout::wrapWidthInfinite;
3536 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
3538 // When printing, the hdc and hdcTarget may be the same, so
3539 // changing the state of surfaceMeasure may change the underlying
3540 // state of surface. Therefore, any cached state is discarded before
3541 // using each surface.
3542 surfaceMeasure->FlushCachedState();
3544 // Copy this line and its styles from the document into local arrays
3545 // and determine the x position at which each character starts.
3546 LineLayout ll(8000);
3547 LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
3549 ll.containsCaret = false;
3551 PRectangle rcLine;
3552 rcLine.left = pfr->rc.left;
3553 rcLine.top = ypos;
3554 rcLine.right = pfr->rc.right - 1;
3555 rcLine.bottom = ypos + vsPrint.lineHeight;
3557 // When document line is wrapped over multiple display lines, find where
3558 // to start printing from to ensure a particular position is on the first
3559 // line of the page.
3560 if (visibleLine == 0) {
3561 int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
3562 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
3563 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
3564 visibleLine = -iwl;
3568 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
3569 visibleLine = -(ll.lines - 1);
3573 if (draw && lineNumberWidth &&
3574 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
3575 (visibleLine >= 0)) {
3576 char number[100];
3577 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
3578 PRectangle rcNumber = rcLine;
3579 rcNumber.right = rcNumber.left + lineNumberWidth;
3580 // Right justify
3581 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
3582 vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
3583 surface->FlushCachedState();
3584 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
3585 ypos + vsPrint.maxAscent, number, istrlen(number),
3586 vsPrint.styles[STYLE_LINENUMBER].fore.allocated,
3587 vsPrint.styles[STYLE_LINENUMBER].back.allocated);
3590 // Draw the line
3591 surface->FlushCachedState();
3593 for (int iwl = 0; iwl < ll.lines; iwl++) {
3594 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
3595 if (visibleLine >= 0) {
3596 if (draw) {
3597 rcLine.top = ypos;
3598 rcLine.bottom = ypos + vsPrint.lineHeight;
3599 DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
3601 ypos += vsPrint.lineHeight;
3603 visibleLine++;
3604 if (iwl == ll.lines - 1)
3605 nPrintPos = pdoc->LineStart(lineDoc + 1);
3606 else
3607 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
3611 ++lineDoc;
3614 // Clear cache so measurements are not used for screen
3615 posCache.Clear();
3617 return nPrintPos;
3620 int Editor::TextWidth(int style, const char *text) {
3621 RefreshStyleData();
3622 AutoSurface surface(this);
3623 if (surface) {
3624 return surface->WidthText(vs.styles[style].font, text, istrlen(text));
3625 } else {
3626 return 1;
3630 // Empty method is overridden on GTK+ to show / hide scrollbars
3631 void Editor::ReconfigureScrollBars() {}
3633 void Editor::SetScrollBars() {
3634 RefreshStyleData();
3636 int nMax = MaxScrollPos();
3637 int nPage = LinesOnScreen();
3638 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
3639 if (modified) {
3640 DwellEnd(true);
3643 // TODO: ensure always showing as many lines as possible
3644 // May not be, if, for example, window made larger
3645 if (topLine > MaxScrollPos()) {
3646 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
3647 SetVerticalScrollPos();
3648 Redraw();
3650 if (modified) {
3651 if (!AbandonPaint())
3652 Redraw();
3654 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
3657 void Editor::ChangeSize() {
3658 DropGraphics();
3659 SetScrollBars();
3660 if (wrapState != eWrapNone) {
3661 PRectangle rcTextArea = GetClientRectangle();
3662 rcTextArea.left = vs.fixedColumnWidth;
3663 rcTextArea.right -= vs.rightMarginWidth;
3664 if (wrapWidth != rcTextArea.Width()) {
3665 NeedWrapping();
3666 Redraw();
3671 int Editor::InsertSpace(int position, unsigned int spaces) {
3672 if (spaces > 0) {
3673 std::string spaceText(spaces, ' ');
3674 pdoc->InsertString(position, spaceText.c_str(), spaces);
3675 position += spaces;
3677 return position;
3680 void Editor::AddChar(char ch) {
3681 char s[2];
3682 s[0] = ch;
3683 s[1] = '\0';
3684 AddCharUTF(s, 1);
3687 void Editor::FilterSelections() {
3688 if (!additionalSelectionTyping && (sel.Count() > 1)) {
3689 SelectionRange rangeOnly = sel.RangeMain();
3690 InvalidateSelection(rangeOnly, true);
3691 sel.SetSelection(rangeOnly);
3695 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
3696 void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
3697 FilterSelections();
3699 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
3700 for (size_t r=0; r<sel.Count(); r++) {
3701 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
3702 sel.Range(r).End().Position())) {
3703 int positionInsert = sel.Range(r).Start().Position();
3704 if (!sel.Range(r).Empty()) {
3705 if (sel.Range(r).Length()) {
3706 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
3707 sel.Range(r).ClearVirtualSpace();
3708 } else {
3709 // Range is all virtual so collapse to start of virtual space
3710 sel.Range(r).MinimizeVirtualSpace();
3712 } else if (inOverstrike) {
3713 if (positionInsert < pdoc->Length()) {
3714 if (!IsEOLChar(pdoc->CharAt(positionInsert))) {
3715 pdoc->DelChar(positionInsert);
3716 sel.Range(r).ClearVirtualSpace();
3720 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
3721 if (pdoc->InsertString(positionInsert, s, len)) {
3722 sel.Range(r).caret.SetPosition(positionInsert + len);
3723 sel.Range(r).anchor.SetPosition(positionInsert + len);
3725 sel.Range(r).ClearVirtualSpace();
3726 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
3727 if (wrapState != eWrapNone) {
3728 AutoSurface surface(this);
3729 if (surface) {
3730 WrapOneLine(surface, pdoc->LineFromPosition(positionInsert));
3736 if (wrapState != eWrapNone) {
3737 SetScrollBars();
3739 ThinRectangularRange();
3740 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
3741 EnsureCaretVisible();
3742 // Avoid blinking during rapid typing:
3743 ShowCaretAtCurrentPosition();
3744 if (!caretSticky) {
3745 SetLastXChosen();
3748 if (treatAsDBCS) {
3749 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
3750 static_cast<unsigned char>(s[1]));
3751 } else {
3752 int byte = static_cast<unsigned char>(s[0]);
3753 if ((byte < 0xC0) || (1 == len)) {
3754 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
3755 // characters when not in UTF-8 mode.
3756 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
3757 // characters representing themselves.
3758 } else {
3759 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
3760 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
3761 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
3762 if (byte < 0xE0) {
3763 int byte2 = static_cast<unsigned char>(s[1]);
3764 if ((byte2 & 0xC0) == 0x80) {
3765 // Two-byte-character lead-byte followed by a trail-byte.
3766 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
3768 // A two-byte-character lead-byte not followed by trail-byte
3769 // represents itself.
3770 } else if (byte < 0xF0) {
3771 int byte2 = static_cast<unsigned char>(s[1]);
3772 int byte3 = static_cast<unsigned char>(s[2]);
3773 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
3774 // Three-byte-character lead byte followed by two trail bytes.
3775 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
3776 (byte3 & 0x3F));
3778 // A three-byte-character lead-byte not followed by two trail-bytes
3779 // represents itself.
3782 NotifyChar(byte);
3785 if (recordingMacro) {
3786 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
3790 void Editor::ClearSelection() {
3791 if (!sel.IsRectangular())
3792 FilterSelections();
3793 UndoGroup ug(pdoc);
3794 for (size_t r=0; r<sel.Count(); r++) {
3795 if (!sel.Range(r).Empty()) {
3796 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
3797 sel.Range(r).End().Position())) {
3798 pdoc->DeleteChars(sel.Range(r).Start().Position(),
3799 sel.Range(r).Length());
3800 sel.Range(r) = sel.Range(r).Start();
3804 ThinRectangularRange();
3805 sel.RemoveDuplicates();
3806 ClaimSelection();
3809 void Editor::ClearAll() {
3811 UndoGroup ug(pdoc);
3812 if (0 != pdoc->Length()) {
3813 pdoc->DeleteChars(0, pdoc->Length());
3815 if (!pdoc->IsReadOnly()) {
3816 cs.Clear();
3817 pdoc->AnnotationClearAll();
3818 pdoc->MarginClearAll();
3821 sel.Clear();
3822 SetTopLine(0);
3823 SetVerticalScrollPos();
3824 InvalidateStyleRedraw();
3827 void Editor::ClearDocumentStyle() {
3828 Decoration *deco = pdoc->decorations.root;
3829 while (deco) {
3830 // Save next in case deco deleted
3831 Decoration *decoNext = deco->next;
3832 if (deco->indicator < INDIC_CONTAINER) {
3833 pdoc->decorations.SetCurrentIndicator(deco->indicator);
3834 pdoc->DecorationFillRange(0, 0, pdoc->Length());
3836 deco = decoNext;
3838 pdoc->StartStyling(0, '\377');
3839 pdoc->SetStyleFor(pdoc->Length(), 0);
3840 cs.ShowAll();
3841 pdoc->ClearLevels();
3844 void Editor::CopyAllowLine() {
3845 SelectionText selectedText;
3846 CopySelectionRange(&selectedText, true);
3847 CopyToClipboard(selectedText);
3850 void Editor::Cut() {
3851 pdoc->CheckReadOnly();
3852 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
3853 Copy();
3854 ClearSelection();
3858 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
3859 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
3860 return;
3862 sel.Clear();
3863 sel.RangeMain() = SelectionRange(pos);
3864 int line = pdoc->LineFromPosition(sel.MainCaret());
3865 UndoGroup ug(pdoc);
3866 sel.RangeMain().caret = SelectionPosition(
3867 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
3868 int xInsert = XFromPosition(sel.RangeMain().caret);
3869 bool prevCr = false;
3870 while ((len > 0) && IsEOLChar(ptr[len-1]))
3871 len--;
3872 for (int i = 0; i < len; i++) {
3873 if (IsEOLChar(ptr[i])) {
3874 if ((ptr[i] == '\r') || (!prevCr))
3875 line++;
3876 if (line >= pdoc->LinesTotal()) {
3877 if (pdoc->eolMode != SC_EOL_LF)
3878 pdoc->InsertChar(pdoc->Length(), '\r');
3879 if (pdoc->eolMode != SC_EOL_CR)
3880 pdoc->InsertChar(pdoc->Length(), '\n');
3882 // Pad the end of lines with spaces if required
3883 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
3884 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
3885 while (XFromPosition(sel.MainCaret()) < xInsert) {
3886 pdoc->InsertChar(sel.MainCaret(), ' ');
3887 sel.RangeMain().caret.Add(1);
3890 prevCr = ptr[i] == '\r';
3891 } else {
3892 pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
3893 sel.RangeMain().caret.Add(1);
3894 prevCr = false;
3897 SetEmptySelection(pos);
3900 bool Editor::CanPaste() {
3901 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
3904 void Editor::Clear() {
3905 UndoGroup ug(pdoc);
3906 // If multiple selections, don't delete EOLS
3907 if (sel.Empty()) {
3908 for (size_t r=0; r<sel.Count(); r++) {
3909 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
3910 if (sel.Range(r).Start().VirtualSpace()) {
3911 if (sel.Range(r).anchor < sel.Range(r).caret)
3912 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
3913 else
3914 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
3916 if ((sel.Count() == 1) || !IsEOLChar(pdoc->CharAt(sel.Range(r).caret.Position()))) {
3917 pdoc->DelChar(sel.Range(r).caret.Position());
3918 sel.Range(r).ClearVirtualSpace();
3919 } // else multiple selection so don't eat line ends
3920 } else {
3921 sel.Range(r).ClearVirtualSpace();
3924 } else {
3925 ClearSelection();
3927 sel.RemoveDuplicates();
3930 void Editor::SelectAll() {
3931 sel.Clear();
3932 SetSelection(0, pdoc->Length());
3933 Redraw();
3936 void Editor::Undo() {
3937 if (pdoc->CanUndo()) {
3938 InvalidateCaret();
3939 int newPos = pdoc->Undo();
3940 if (newPos >= 0)
3941 SetEmptySelection(newPos);
3942 EnsureCaretVisible();
3946 void Editor::Redo() {
3947 if (pdoc->CanRedo()) {
3948 int newPos = pdoc->Redo();
3949 if (newPos >= 0)
3950 SetEmptySelection(newPos);
3951 EnsureCaretVisible();
3955 void Editor::DelChar() {
3956 if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) {
3957 pdoc->DelChar(sel.MainCaret());
3959 // Avoid blinking during rapid typing:
3960 ShowCaretAtCurrentPosition();
3963 void Editor::DelCharBack(bool allowLineStartDeletion) {
3964 if (!sel.IsRectangular())
3965 FilterSelections();
3966 if (sel.IsRectangular())
3967 allowLineStartDeletion = false;
3968 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
3969 if (sel.Empty()) {
3970 for (size_t r=0; r<sel.Count(); r++) {
3971 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
3972 if (sel.Range(r).caret.VirtualSpace()) {
3973 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
3974 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
3975 } else {
3976 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
3977 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
3978 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
3979 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
3980 UndoGroup ugInner(pdoc, !ug.Needed());
3981 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3982 int indentationStep = pdoc->IndentSize();
3983 if (indentation % indentationStep == 0) {
3984 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
3985 } else {
3986 pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
3988 // SetEmptySelection
3989 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos),
3990 pdoc->GetLineIndentPosition(lineCurrentPos));
3991 } else {
3992 pdoc->DelCharBack(sel.Range(r).caret.Position());
3996 } else {
3997 sel.Range(r).ClearVirtualSpace();
4000 } else {
4001 ClearSelection();
4003 sel.RemoveDuplicates();
4004 // Avoid blinking during rapid typing:
4005 ShowCaretAtCurrentPosition();
4008 void Editor::NotifyFocus(bool) {}
4010 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
4011 SCNotification scn = {0};
4012 scn.nmhdr.code = SCN_STYLENEEDED;
4013 scn.position = endStyleNeeded;
4014 NotifyParent(scn);
4017 void Editor::NotifyStyleNeeded(Document*, void *, int endStyleNeeded) {
4018 NotifyStyleToNeeded(endStyleNeeded);
4021 void Editor::NotifyChar(int ch) {
4022 SCNotification scn = {0};
4023 scn.nmhdr.code = SCN_CHARADDED;
4024 scn.ch = ch;
4025 NotifyParent(scn);
4028 void Editor::NotifySavePoint(bool isSavePoint) {
4029 SCNotification scn = {0};
4030 if (isSavePoint) {
4031 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
4032 } else {
4033 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
4035 NotifyParent(scn);
4038 void Editor::NotifyModifyAttempt() {
4039 SCNotification scn = {0};
4040 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
4041 NotifyParent(scn);
4044 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
4045 SCNotification scn = {0};
4046 scn.nmhdr.code = SCN_DOUBLECLICK;
4047 scn.line = LineFromLocation(pt);
4048 scn.position = PositionFromLocation(pt, true);
4049 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4050 (alt ? SCI_ALT : 0);
4051 NotifyParent(scn);
4054 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
4055 SCNotification scn = {0};
4056 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
4057 scn.position = position;
4058 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4059 (alt ? SCI_ALT : 0);
4060 NotifyParent(scn);
4063 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
4064 SCNotification scn = {0};
4065 scn.nmhdr.code = SCN_HOTSPOTCLICK;
4066 scn.position = position;
4067 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4068 (alt ? SCI_ALT : 0);
4069 NotifyParent(scn);
4072 void Editor::NotifyUpdateUI() {
4073 SCNotification scn = {0};
4074 scn.nmhdr.code = SCN_UPDATEUI;
4075 NotifyParent(scn);
4078 void Editor::NotifyPainted() {
4079 SCNotification scn = {0};
4080 scn.nmhdr.code = SCN_PAINTED;
4081 NotifyParent(scn);
4084 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
4085 int mask = pdoc->decorations.AllOnFor(position);
4086 if ((click && mask) || pdoc->decorations.clickNotified) {
4087 SCNotification scn = {0};
4088 pdoc->decorations.clickNotified = click;
4089 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
4090 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | (alt ? SCI_ALT : 0);
4091 scn.position = position;
4092 NotifyParent(scn);
4096 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
4097 int marginClicked = -1;
4098 int x = 0;
4099 for (int margin = 0; margin < ViewStyle::margins; margin++) {
4100 if ((pt.x > x) && (pt.x < x + vs.ms[margin].width))
4101 marginClicked = margin;
4102 x += vs.ms[margin].width;
4104 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
4105 SCNotification scn = {0};
4106 scn.nmhdr.code = SCN_MARGINCLICK;
4107 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4108 (alt ? SCI_ALT : 0);
4109 scn.position = pdoc->LineStart(LineFromLocation(pt));
4110 scn.margin = marginClicked;
4111 NotifyParent(scn);
4112 return true;
4113 } else {
4114 return false;
4118 void Editor::NotifyNeedShown(int pos, int len) {
4119 SCNotification scn = {0};
4120 scn.nmhdr.code = SCN_NEEDSHOWN;
4121 scn.position = pos;
4122 scn.length = len;
4123 NotifyParent(scn);
4126 void Editor::NotifyDwelling(Point pt, bool state) {
4127 SCNotification scn = {0};
4128 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
4129 scn.position = PositionFromLocation(pt, true);
4130 scn.x = pt.x;
4131 scn.y = pt.y;
4132 NotifyParent(scn);
4135 void Editor::NotifyZoom() {
4136 SCNotification scn = {0};
4137 scn.nmhdr.code = SCN_ZOOM;
4138 NotifyParent(scn);
4141 // Notifications from document
4142 void Editor::NotifyModifyAttempt(Document*, void *) {
4143 //Platform::DebugPrintf("** Modify Attempt\n");
4144 NotifyModifyAttempt();
4147 void Editor::NotifySavePoint(Document*, void *, bool atSavePoint) {
4148 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
4149 NotifySavePoint(atSavePoint);
4152 void Editor::CheckModificationForWrap(DocModification mh) {
4153 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
4154 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4155 if (wrapState != eWrapNone) {
4156 int lineDoc = pdoc->LineFromPosition(mh.position);
4157 int lines = Platform::Maximum(0, mh.linesAdded);
4158 NeedWrapping(lineDoc, lineDoc + lines + 1);
4160 // Fix up annotation heights
4161 int lineDoc = pdoc->LineFromPosition(mh.position);
4162 int lines = Platform::Maximum(0, mh.linesAdded);
4163 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
4167 // Move a position so it is still after the same character as before the insertion.
4168 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
4169 if (position > startInsertion) {
4170 return position + length;
4172 return position;
4175 // Move a position so it is still after the same character as before the deletion if that
4176 // character is still present else after the previous surviving character.
4177 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
4178 if (position > startDeletion) {
4179 int endDeletion = startDeletion + length;
4180 if (position > endDeletion) {
4181 return position - length;
4182 } else {
4183 return startDeletion;
4185 } else {
4186 return position;
4190 void Editor::NotifyModified(Document*, DocModification mh, void *) {
4191 needUpdateUI = true;
4192 if (paintState == painting) {
4193 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
4195 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
4196 if (paintState == painting) {
4197 CheckForChangeOutsidePaint(
4198 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
4199 } else {
4200 // Could check that change is before last visible line.
4201 Redraw();
4204 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
4205 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4206 pdoc->IncrementStyleClock();
4208 if (paintState == notPainting) {
4209 if (mh.position < pdoc->LineStart(topLine)) {
4210 // Styling performed before this view
4211 Redraw();
4212 } else {
4213 InvalidateRange(mh.position, mh.position + mh.length);
4216 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4217 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4219 } else {
4220 // Move selection and brace highlights
4221 if (mh.modificationType & SC_MOD_INSERTTEXT) {
4222 sel.MovePositions(true, mh.position, mh.length);
4223 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
4224 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
4225 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
4226 sel.MovePositions(false, mh.position, mh.length);
4227 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
4228 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
4230 if (cs.LinesDisplayed() < cs.LinesInDoc()) {
4231 // Some lines are hidden so may need shown.
4232 // TODO: check if the modified area is hidden.
4233 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
4234 NotifyNeedShown(mh.position, 0);
4235 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
4236 NotifyNeedShown(mh.position, mh.length);
4239 if (mh.linesAdded != 0) {
4240 // Update contraction state for inserted and removed lines
4241 // lineOfPos should be calculated in context of state before modification, shouldn't it
4242 int lineOfPos = pdoc->LineFromPosition(mh.position);
4243 if (mh.linesAdded > 0) {
4244 cs.InsertLines(lineOfPos, mh.linesAdded);
4245 } else {
4246 cs.DeleteLines(lineOfPos, -mh.linesAdded);
4249 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
4250 int lineDoc = pdoc->LineFromPosition(mh.position);
4251 if (vs.annotationVisible) {
4252 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
4255 CheckModificationForWrap(mh);
4256 if (mh.linesAdded != 0) {
4257 // Avoid scrolling of display if change before current display
4258 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
4259 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
4260 if (newTop != topLine) {
4261 SetTopLine(newTop);
4262 SetVerticalScrollPos();
4266 //Platform::DebugPrintf("** %x Doc Changed\n", this);
4267 // TODO: could invalidate from mh.startModification to end of screen
4268 //InvalidateRange(mh.position, mh.position + mh.length);
4269 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
4270 Redraw();
4272 } else {
4273 //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
4274 // mh.position, mh.position + mh.length);
4275 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
4276 InvalidateRange(mh.position, mh.position + mh.length);
4281 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
4282 SetScrollBars();
4285 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
4286 if ((paintState == notPainting) || !PaintContainsMargin()) {
4287 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
4288 // Fold changes can affect the drawing of following lines so redraw whole margin
4289 RedrawSelMargin();
4290 } else {
4291 RedrawSelMargin(mh.line);
4296 // NOW pay the piper WRT "deferred" visual updates
4297 if (IsLastStep(mh)) {
4298 SetScrollBars();
4299 Redraw();
4302 // If client wants to see this modification
4303 if (mh.modificationType & modEventMask) {
4304 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
4305 // Real modification made to text of document.
4306 NotifyChange(); // Send EN_CHANGE
4309 SCNotification scn = {0};
4310 scn.nmhdr.code = SCN_MODIFIED;
4311 scn.position = mh.position;
4312 scn.modificationType = mh.modificationType;
4313 scn.text = mh.text;
4314 scn.length = mh.length;
4315 scn.linesAdded = mh.linesAdded;
4316 scn.line = mh.line;
4317 scn.foldLevelNow = mh.foldLevelNow;
4318 scn.foldLevelPrev = mh.foldLevelPrev;
4319 scn.token = mh.token;
4320 scn.annotationLinesAdded = mh.annotationLinesAdded;
4321 NotifyParent(scn);
4325 void Editor::NotifyDeleted(Document *, void *) {
4326 /* Do nothing */
4329 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
4331 // Enumerates all macroable messages
4332 switch (iMessage) {
4333 case SCI_CUT:
4334 case SCI_COPY:
4335 case SCI_PASTE:
4336 case SCI_CLEAR:
4337 case SCI_REPLACESEL:
4338 case SCI_ADDTEXT:
4339 case SCI_INSERTTEXT:
4340 case SCI_APPENDTEXT:
4341 case SCI_CLEARALL:
4342 case SCI_SELECTALL:
4343 case SCI_GOTOLINE:
4344 case SCI_GOTOPOS:
4345 case SCI_SEARCHANCHOR:
4346 case SCI_SEARCHNEXT:
4347 case SCI_SEARCHPREV:
4348 case SCI_LINEDOWN:
4349 case SCI_LINEDOWNEXTEND:
4350 case SCI_PARADOWN:
4351 case SCI_PARADOWNEXTEND:
4352 case SCI_LINEUP:
4353 case SCI_LINEUPEXTEND:
4354 case SCI_PARAUP:
4355 case SCI_PARAUPEXTEND:
4356 case SCI_CHARLEFT:
4357 case SCI_CHARLEFTEXTEND:
4358 case SCI_CHARRIGHT:
4359 case SCI_CHARRIGHTEXTEND:
4360 case SCI_WORDLEFT:
4361 case SCI_WORDLEFTEXTEND:
4362 case SCI_WORDRIGHT:
4363 case SCI_WORDRIGHTEXTEND:
4364 case SCI_WORDPARTLEFT:
4365 case SCI_WORDPARTLEFTEXTEND:
4366 case SCI_WORDPARTRIGHT:
4367 case SCI_WORDPARTRIGHTEXTEND:
4368 case SCI_WORDLEFTEND:
4369 case SCI_WORDLEFTENDEXTEND:
4370 case SCI_WORDRIGHTEND:
4371 case SCI_WORDRIGHTENDEXTEND:
4372 case SCI_HOME:
4373 case SCI_HOMEEXTEND:
4374 case SCI_LINEEND:
4375 case SCI_LINEENDEXTEND:
4376 case SCI_HOMEWRAP:
4377 case SCI_HOMEWRAPEXTEND:
4378 case SCI_LINEENDWRAP:
4379 case SCI_LINEENDWRAPEXTEND:
4380 case SCI_DOCUMENTSTART:
4381 case SCI_DOCUMENTSTARTEXTEND:
4382 case SCI_DOCUMENTEND:
4383 case SCI_DOCUMENTENDEXTEND:
4384 case SCI_STUTTEREDPAGEUP:
4385 case SCI_STUTTEREDPAGEUPEXTEND:
4386 case SCI_STUTTEREDPAGEDOWN:
4387 case SCI_STUTTEREDPAGEDOWNEXTEND:
4388 case SCI_PAGEUP:
4389 case SCI_PAGEUPEXTEND:
4390 case SCI_PAGEDOWN:
4391 case SCI_PAGEDOWNEXTEND:
4392 case SCI_EDITTOGGLEOVERTYPE:
4393 case SCI_CANCEL:
4394 case SCI_DELETEBACK:
4395 case SCI_TAB:
4396 case SCI_BACKTAB:
4397 case SCI_FORMFEED:
4398 case SCI_VCHOME:
4399 case SCI_VCHOMEEXTEND:
4400 case SCI_VCHOMEWRAP:
4401 case SCI_VCHOMEWRAPEXTEND:
4402 case SCI_DELWORDLEFT:
4403 case SCI_DELWORDRIGHT:
4404 case SCI_DELWORDRIGHTEND:
4405 case SCI_DELLINELEFT:
4406 case SCI_DELLINERIGHT:
4407 case SCI_LINECOPY:
4408 case SCI_LINECUT:
4409 case SCI_LINEDELETE:
4410 case SCI_LINETRANSPOSE:
4411 case SCI_LINEDUPLICATE:
4412 case SCI_LOWERCASE:
4413 case SCI_UPPERCASE:
4414 case SCI_LINESCROLLDOWN:
4415 case SCI_LINESCROLLUP:
4416 case SCI_DELETEBACKNOTLINE:
4417 case SCI_HOMEDISPLAY:
4418 case SCI_HOMEDISPLAYEXTEND:
4419 case SCI_LINEENDDISPLAY:
4420 case SCI_LINEENDDISPLAYEXTEND:
4421 case SCI_SETSELECTIONMODE:
4422 case SCI_LINEDOWNRECTEXTEND:
4423 case SCI_LINEUPRECTEXTEND:
4424 case SCI_CHARLEFTRECTEXTEND:
4425 case SCI_CHARRIGHTRECTEXTEND:
4426 case SCI_HOMERECTEXTEND:
4427 case SCI_VCHOMERECTEXTEND:
4428 case SCI_LINEENDRECTEXTEND:
4429 case SCI_PAGEUPRECTEXTEND:
4430 case SCI_PAGEDOWNRECTEXTEND:
4431 case SCI_SELECTIONDUPLICATE:
4432 case SCI_COPYALLOWLINE:
4433 break;
4435 // Filter out all others like display changes. Also, newlines are redundant
4436 // with char insert messages.
4437 case SCI_NEWLINE:
4438 default:
4439 // printf("Filtered out %ld of macro recording\n", iMessage);
4440 return ;
4443 // Send notification
4444 SCNotification scn = {0};
4445 scn.nmhdr.code = SCN_MACRORECORD;
4446 scn.message = iMessage;
4447 scn.wParam = wParam;
4448 scn.lParam = lParam;
4449 NotifyParent(scn);
4453 * Force scroll and keep position relative to top of window.
4455 * If stuttered = true and not already at first/last row, move to first/last row of window.
4456 * If stuttered = true and already at first/last row, scroll as normal.
4458 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
4459 int topLineNew, newPos;
4461 // I consider only the caretYSlop, and ignore the caretYPolicy-- is that a problem?
4462 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
4463 int topStutterLine = topLine + caretYSlop;
4464 int bottomStutterLine =
4465 pdoc->LineFromPosition(PositionFromLocation(
4466 Point(lastXChosen, direction * vs.lineHeight * LinesToScroll())))
4467 - caretYSlop - 1;
4469 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
4470 topLineNew = topLine;
4471 newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * caretYSlop));
4473 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
4474 topLineNew = topLine;
4475 newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * (LinesToScroll() - caretYSlop)));
4477 } else {
4478 Point pt = LocationFromPosition(sel.MainCaret());
4480 topLineNew = Platform::Clamp(
4481 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
4482 newPos = PositionFromLocation(
4483 Point(lastXChosen, pt.y + direction * (vs.lineHeight * LinesToScroll())));
4486 if (topLineNew != topLine) {
4487 SetTopLine(topLineNew);
4488 MovePositionTo(SelectionPosition(newPos), selt);
4489 Redraw();
4490 SetVerticalScrollPos();
4491 } else {
4492 MovePositionTo(SelectionPosition(newPos), selt);
4496 void Editor::ChangeCaseOfSelection(bool makeUpperCase) {
4497 UndoGroup ug(pdoc);
4498 for (size_t r=0; r<sel.Count(); r++) {
4499 SelectionRange current = sel.Range(r);
4500 pdoc->ChangeCase(Range(current.Start().Position(), current.End().Position()),
4501 makeUpperCase);
4502 // Automatic movement cuts off last character so reset to exactly the same as it was.
4503 sel.Range(r) = current;
4507 void Editor::LineTranspose() {
4508 int line = pdoc->LineFromPosition(sel.MainCaret());
4509 if (line > 0) {
4510 UndoGroup ug(pdoc);
4511 int startPrev = pdoc->LineStart(line - 1);
4512 int endPrev = pdoc->LineEnd(line - 1);
4513 int start = pdoc->LineStart(line);
4514 int end = pdoc->LineEnd(line);
4515 char *line1 = CopyRange(startPrev, endPrev);
4516 int len1 = endPrev - startPrev;
4517 char *line2 = CopyRange(start, end);
4518 int len2 = end - start;
4519 pdoc->DeleteChars(start, len2);
4520 pdoc->DeleteChars(startPrev, len1);
4521 pdoc->InsertString(startPrev, line2, len2);
4522 pdoc->InsertString(start - len1 + len2, line1, len1);
4523 MovePositionTo(SelectionPosition(start - len1 + len2));
4524 delete []line1;
4525 delete []line2;
4529 void Editor::Duplicate(bool forLine) {
4530 if (sel.Empty()) {
4531 forLine = true;
4533 UndoGroup ug(pdoc, sel.Count() > 1);
4534 SelectionPosition last;
4535 const char *eol = "";
4536 int eolLen = 0;
4537 if (forLine) {
4538 eol = StringFromEOLMode(pdoc->eolMode);
4539 eolLen = istrlen(eol);
4541 for (size_t r=0; r<sel.Count(); r++) {
4542 SelectionPosition start = sel.Range(r).Start();
4543 SelectionPosition end = sel.Range(r).End();
4544 if (forLine) {
4545 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4546 start = SelectionPosition(pdoc->LineStart(line));
4547 end = SelectionPosition(pdoc->LineEnd(line));
4549 char *text = CopyRange(start.Position(), end.Position());
4550 if (forLine)
4551 pdoc->InsertString(end.Position(), eol, eolLen);
4552 pdoc->InsertString(end.Position() + eolLen, text, SelectionRange(end, start).Length());
4553 delete []text;
4555 if (sel.Count() && sel.IsRectangular()) {
4556 SelectionPosition last = sel.Last();
4557 if (forLine) {
4558 int line = pdoc->LineFromPosition(last.Position());
4559 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
4561 if (sel.Rectangular().anchor > sel.Rectangular().caret)
4562 sel.Rectangular().anchor = last;
4563 else
4564 sel.Rectangular().caret = last;
4565 SetRectangularRange();
4569 void Editor::CancelModes() {
4570 sel.SetMoveExtends(false);
4573 void Editor::NewLine() {
4574 ClearSelection();
4575 const char *eol = "\n";
4576 if (pdoc->eolMode == SC_EOL_CRLF) {
4577 eol = "\r\n";
4578 } else if (pdoc->eolMode == SC_EOL_CR) {
4579 eol = "\r";
4580 } // else SC_EOL_LF -> "\n" already set
4581 if (pdoc->InsertCString(sel.MainCaret(), eol)) {
4582 SetEmptySelection(sel.MainCaret() + istrlen(eol));
4583 while (*eol) {
4584 NotifyChar(*eol);
4585 if (recordingMacro) {
4586 char txt[2];
4587 txt[0] = *eol;
4588 txt[1] = '\0';
4589 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
4591 eol++;
4594 SetLastXChosen();
4595 SetScrollBars();
4596 EnsureCaretVisible();
4597 // Avoid blinking during rapid typing:
4598 ShowCaretAtCurrentPosition();
4601 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
4602 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
4603 if (sel.IsRectangular()) {
4604 if (selt == Selection::noSel) {
4605 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
4606 } else {
4607 caretToUse = sel.Rectangular().caret;
4610 Point pt = LocationFromPosition(caretToUse);
4611 int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
4612 Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
4613 int subLine = (pt.y - ptStartLine.y) / vs.lineHeight;
4614 int commentLines = vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0;
4615 SelectionPosition posNew = SPositionFromLocation(
4616 Point(lastXChosen, pt.y + direction * vs.lineHeight), false, false, UserVirtualSpace());
4617 if ((direction > 0) && (subLine >= (cs.GetHeight(lineDoc) - 1 - commentLines))) {
4618 posNew = SPositionFromLocation(
4619 Point(lastXChosen, pt.y + (commentLines + 1) * vs.lineHeight), false, false, UserVirtualSpace());
4621 if (direction < 0) {
4622 // Line wrapping may lead to a location on the same line, so
4623 // seek back if that is the case.
4624 // There is an equivalent case when moving down which skips
4625 // over a line but as that does not trap the user it is fine.
4626 Point ptNew = LocationFromPosition(posNew.Position());
4627 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
4628 posNew.Add(- 1);
4629 posNew.SetVirtualSpace(0);
4630 ptNew = LocationFromPosition(posNew.Position());
4633 MovePositionTo(posNew, selt);
4636 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
4637 int lineDoc, savedPos = sel.MainCaret();
4638 do {
4639 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
4640 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
4641 if (direction > 0) {
4642 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
4643 if (selt == Selection::noSel) {
4644 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
4646 break;
4649 } while (!cs.GetVisible(lineDoc));
4652 int Editor::StartEndDisplayLine(int pos, bool start) {
4653 RefreshStyleData();
4654 int line = pdoc->LineFromPosition(pos);
4655 AutoSurface surface(this);
4656 AutoLineLayout ll(llc, RetrieveLineLayout(line));
4657 int posRet = INVALID_POSITION;
4658 if (surface && ll) {
4659 unsigned int posLineStart = pdoc->LineStart(line);
4660 LayoutLine(line, surface, vs, ll, wrapWidth);
4661 int posInLine = pos - posLineStart;
4662 if (posInLine <= ll->maxLineLength) {
4663 for (int subLine = 0; subLine < ll->lines; subLine++) {
4664 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
4665 if (start) {
4666 posRet = ll->LineStart(subLine) + posLineStart;
4667 } else {
4668 if (subLine == ll->lines - 1)
4669 posRet = ll->LineStart(subLine + 1) + posLineStart;
4670 else
4671 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
4677 if (posRet == INVALID_POSITION) {
4678 return pos;
4679 } else {
4680 return posRet;
4684 int Editor::KeyCommand(unsigned int iMessage) {
4685 switch (iMessage) {
4686 case SCI_LINEDOWN:
4687 CursorUpOrDown(1);
4688 break;
4689 case SCI_LINEDOWNEXTEND:
4690 CursorUpOrDown(1, Selection::selStream);
4691 break;
4692 case SCI_LINEDOWNRECTEXTEND:
4693 CursorUpOrDown(1, Selection::selRectangle);
4694 break;
4695 case SCI_PARADOWN:
4696 ParaUpOrDown(1);
4697 break;
4698 case SCI_PARADOWNEXTEND:
4699 ParaUpOrDown(1, Selection::selStream);
4700 break;
4701 case SCI_LINESCROLLDOWN:
4702 ScrollTo(topLine + 1);
4703 MoveCaretInsideView(false);
4704 break;
4705 case SCI_LINEUP:
4706 CursorUpOrDown(-1);
4707 break;
4708 case SCI_LINEUPEXTEND:
4709 CursorUpOrDown(-1, Selection::selStream);
4710 break;
4711 case SCI_LINEUPRECTEXTEND:
4712 CursorUpOrDown(-1, Selection::selRectangle);
4713 break;
4714 case SCI_PARAUP:
4715 ParaUpOrDown(-1);
4716 break;
4717 case SCI_PARAUPEXTEND:
4718 ParaUpOrDown(-1, Selection::selStream);
4719 break;
4720 case SCI_LINESCROLLUP:
4721 ScrollTo(topLine - 1);
4722 MoveCaretInsideView(false);
4723 break;
4724 case SCI_CHARLEFT:
4725 if (SelectionEmpty() || sel.MoveExtends()) {
4726 if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
4727 SelectionPosition spCaret = sel.RangeMain().caret;
4728 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
4729 MovePositionTo(spCaret);
4730 } else {
4731 MovePositionTo(MovePositionSoVisible(
4732 SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
4734 } else {
4735 MovePositionTo(sel.LimitsForRectangularElseMain().start);
4737 SetLastXChosen();
4738 break;
4739 case SCI_CHARLEFTEXTEND:
4740 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
4741 SelectionPosition spCaret = sel.RangeMain().caret;
4742 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
4743 MovePositionTo(spCaret, Selection::selStream);
4744 } else {
4745 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
4747 SetLastXChosen();
4748 break;
4749 case SCI_CHARLEFTRECTEXTEND:
4750 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
4751 SelectionPosition spCaret = sel.RangeMain().caret;
4752 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
4753 MovePositionTo(spCaret, Selection::selRectangle);
4754 } else {
4755 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
4757 SetLastXChosen();
4758 break;
4759 case SCI_CHARRIGHT:
4760 if (SelectionEmpty() || sel.MoveExtends()) {
4761 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
4762 SelectionPosition spCaret = sel.RangeMain().caret;
4763 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
4764 MovePositionTo(spCaret);
4765 } else {
4766 MovePositionTo(MovePositionSoVisible(
4767 SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
4769 } else {
4770 MovePositionTo(sel.LimitsForRectangularElseMain().end);
4772 SetLastXChosen();
4773 break;
4774 case SCI_CHARRIGHTEXTEND:
4775 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
4776 SelectionPosition spCaret = sel.RangeMain().caret;
4777 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
4778 MovePositionTo(spCaret, Selection::selStream);
4779 } else {
4780 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
4782 SetLastXChosen();
4783 break;
4784 case SCI_CHARRIGHTRECTEXTEND:
4785 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
4786 SelectionPosition spCaret = sel.RangeMain().caret;
4787 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
4788 MovePositionTo(spCaret, Selection::selRectangle);
4789 } else {
4790 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
4792 SetLastXChosen();
4793 break;
4794 case SCI_WORDLEFT:
4795 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
4796 SetLastXChosen();
4797 break;
4798 case SCI_WORDLEFTEXTEND:
4799 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
4800 SetLastXChosen();
4801 break;
4802 case SCI_WORDRIGHT:
4803 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
4804 SetLastXChosen();
4805 break;
4806 case SCI_WORDRIGHTEXTEND:
4807 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
4808 SetLastXChosen();
4809 break;
4811 case SCI_WORDLEFTEND:
4812 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
4813 SetLastXChosen();
4814 break;
4815 case SCI_WORDLEFTENDEXTEND:
4816 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
4817 SetLastXChosen();
4818 break;
4819 case SCI_WORDRIGHTEND:
4820 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
4821 SetLastXChosen();
4822 break;
4823 case SCI_WORDRIGHTENDEXTEND:
4824 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
4825 SetLastXChosen();
4826 break;
4828 case SCI_HOME:
4829 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
4830 SetLastXChosen();
4831 break;
4832 case SCI_HOMEEXTEND:
4833 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
4834 SetLastXChosen();
4835 break;
4836 case SCI_HOMERECTEXTEND:
4837 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
4838 SetLastXChosen();
4839 break;
4840 case SCI_LINEEND:
4841 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
4842 SetLastXChosen();
4843 break;
4844 case SCI_LINEENDEXTEND:
4845 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
4846 SetLastXChosen();
4847 break;
4848 case SCI_LINEENDRECTEXTEND:
4849 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
4850 SetLastXChosen();
4851 break;
4852 case SCI_HOMEWRAP: {
4853 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
4854 if (sel.RangeMain().caret <= homePos)
4855 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
4856 MovePositionTo(homePos);
4857 SetLastXChosen();
4859 break;
4860 case SCI_HOMEWRAPEXTEND: {
4861 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
4862 if (sel.RangeMain().caret <= homePos)
4863 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
4864 MovePositionTo(homePos, Selection::selStream);
4865 SetLastXChosen();
4867 break;
4868 case SCI_LINEENDWRAP: {
4869 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
4870 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
4871 if (endPos > realEndPos // if moved past visible EOLs
4872 || sel.RangeMain().caret >= endPos) // if at end of display line already
4873 endPos = realEndPos;
4874 MovePositionTo(endPos);
4875 SetLastXChosen();
4877 break;
4878 case SCI_LINEENDWRAPEXTEND: {
4879 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
4880 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
4881 if (endPos > realEndPos // if moved past visible EOLs
4882 || sel.RangeMain().caret >= endPos) // if at end of display line already
4883 endPos = realEndPos;
4884 MovePositionTo(endPos, Selection::selStream);
4885 SetLastXChosen();
4887 break;
4888 case SCI_DOCUMENTSTART:
4889 MovePositionTo(0);
4890 SetLastXChosen();
4891 break;
4892 case SCI_DOCUMENTSTARTEXTEND:
4893 MovePositionTo(0, Selection::selStream);
4894 SetLastXChosen();
4895 break;
4896 case SCI_DOCUMENTEND:
4897 MovePositionTo(pdoc->Length());
4898 SetLastXChosen();
4899 break;
4900 case SCI_DOCUMENTENDEXTEND:
4901 MovePositionTo(pdoc->Length(), Selection::selStream);
4902 SetLastXChosen();
4903 break;
4904 case SCI_STUTTEREDPAGEUP:
4905 PageMove(-1, Selection::noSel, true);
4906 break;
4907 case SCI_STUTTEREDPAGEUPEXTEND:
4908 PageMove(-1, Selection::selStream, true);
4909 break;
4910 case SCI_STUTTEREDPAGEDOWN:
4911 PageMove(1, Selection::noSel, true);
4912 break;
4913 case SCI_STUTTEREDPAGEDOWNEXTEND:
4914 PageMove(1, Selection::selStream, true);
4915 break;
4916 case SCI_PAGEUP:
4917 PageMove(-1);
4918 break;
4919 case SCI_PAGEUPEXTEND:
4920 PageMove(-1, Selection::selStream);
4921 break;
4922 case SCI_PAGEUPRECTEXTEND:
4923 PageMove(-1, Selection::selRectangle);
4924 break;
4925 case SCI_PAGEDOWN:
4926 PageMove(1);
4927 break;
4928 case SCI_PAGEDOWNEXTEND:
4929 PageMove(1, Selection::selStream);
4930 break;
4931 case SCI_PAGEDOWNRECTEXTEND:
4932 PageMove(1, Selection::selRectangle);
4933 break;
4934 case SCI_EDITTOGGLEOVERTYPE:
4935 inOverstrike = !inOverstrike;
4936 DropCaret();
4937 ShowCaretAtCurrentPosition();
4938 NotifyUpdateUI();
4939 break;
4940 case SCI_CANCEL: // Cancel any modes - handled in subclass
4941 // Also unselect text
4942 CancelModes();
4943 break;
4944 case SCI_DELETEBACK:
4945 DelCharBack(true);
4946 if (!caretSticky) {
4947 SetLastXChosen();
4949 EnsureCaretVisible();
4950 break;
4951 case SCI_DELETEBACKNOTLINE:
4952 DelCharBack(false);
4953 if (!caretSticky) {
4954 SetLastXChosen();
4956 EnsureCaretVisible();
4957 break;
4958 case SCI_TAB:
4959 Indent(true);
4960 if (!caretSticky) {
4961 SetLastXChosen();
4963 EnsureCaretVisible();
4964 ShowCaretAtCurrentPosition(); // Avoid blinking
4965 break;
4966 case SCI_BACKTAB:
4967 Indent(false);
4968 if (!caretSticky) {
4969 SetLastXChosen();
4971 EnsureCaretVisible();
4972 ShowCaretAtCurrentPosition(); // Avoid blinking
4973 break;
4974 case SCI_NEWLINE:
4975 NewLine();
4976 break;
4977 case SCI_FORMFEED:
4978 AddChar('\f');
4979 break;
4980 case SCI_VCHOME:
4981 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
4982 SetLastXChosen();
4983 break;
4984 case SCI_VCHOMEEXTEND:
4985 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
4986 SetLastXChosen();
4987 break;
4988 case SCI_VCHOMERECTEXTEND:
4989 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
4990 SetLastXChosen();
4991 break;
4992 case SCI_VCHOMEWRAP: {
4993 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
4994 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
4995 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
4996 homePos = viewLineStart;
4998 MovePositionTo(homePos);
4999 SetLastXChosen();
5001 break;
5002 case SCI_VCHOMEWRAPEXTEND: {
5003 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5004 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5005 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5006 homePos = viewLineStart;
5008 MovePositionTo(homePos, Selection::selStream);
5009 SetLastXChosen();
5011 break;
5012 case SCI_ZOOMIN:
5013 if (vs.zoomLevel < 20) {
5014 vs.zoomLevel++;
5015 InvalidateStyleRedraw();
5016 NotifyZoom();
5018 break;
5019 case SCI_ZOOMOUT:
5020 if (vs.zoomLevel > -10) {
5021 vs.zoomLevel--;
5022 InvalidateStyleRedraw();
5023 NotifyZoom();
5025 break;
5026 case SCI_DELWORDLEFT: {
5027 int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
5028 pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
5029 sel.RangeMain().ClearVirtualSpace();
5030 SetLastXChosen();
5032 break;
5033 case SCI_DELWORDRIGHT: {
5034 UndoGroup ug(pdoc);
5035 sel.RangeMain().caret = SelectionPosition(
5036 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5037 int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
5038 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5040 break;
5041 case SCI_DELWORDRIGHTEND: {
5042 UndoGroup ug(pdoc);
5043 sel.RangeMain().caret = SelectionPosition(
5044 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5045 int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
5046 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5048 break;
5049 case SCI_DELLINELEFT: {
5050 int line = pdoc->LineFromPosition(sel.MainCaret());
5051 int start = pdoc->LineStart(line);
5052 pdoc->DeleteChars(start, sel.MainCaret() - start);
5053 sel.RangeMain().ClearVirtualSpace();
5054 SetLastXChosen();
5056 break;
5057 case SCI_DELLINERIGHT: {
5058 int line = pdoc->LineFromPosition(sel.MainCaret());
5059 int end = pdoc->LineEnd(line);
5060 pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
5062 break;
5063 case SCI_LINECOPY: {
5064 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5065 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5066 CopyRangeToClipboard(pdoc->LineStart(lineStart),
5067 pdoc->LineStart(lineEnd + 1));
5069 break;
5070 case SCI_LINECUT: {
5071 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5072 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5073 int start = pdoc->LineStart(lineStart);
5074 int end = pdoc->LineStart(lineEnd + 1);
5075 SetSelection(start, end);
5076 Cut();
5077 SetLastXChosen();
5079 break;
5080 case SCI_LINEDELETE: {
5081 int line = pdoc->LineFromPosition(sel.MainCaret());
5082 int start = pdoc->LineStart(line);
5083 int end = pdoc->LineStart(line + 1);
5084 pdoc->DeleteChars(start, end - start);
5086 break;
5087 case SCI_LINETRANSPOSE:
5088 LineTranspose();
5089 break;
5090 case SCI_LINEDUPLICATE:
5091 Duplicate(true);
5092 break;
5093 case SCI_SELECTIONDUPLICATE:
5094 Duplicate(false);
5095 break;
5096 case SCI_LOWERCASE:
5097 ChangeCaseOfSelection(false);
5098 break;
5099 case SCI_UPPERCASE:
5100 ChangeCaseOfSelection(true);
5101 break;
5102 case SCI_WORDPARTLEFT:
5103 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
5104 SetLastXChosen();
5105 break;
5106 case SCI_WORDPARTLEFTEXTEND:
5107 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
5108 SetLastXChosen();
5109 break;
5110 case SCI_WORDPARTRIGHT:
5111 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
5112 SetLastXChosen();
5113 break;
5114 case SCI_WORDPARTRIGHTEXTEND:
5115 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
5116 SetLastXChosen();
5117 break;
5118 case SCI_HOMEDISPLAY:
5119 MovePositionTo(MovePositionSoVisible(
5120 StartEndDisplayLine(sel.MainCaret(), true), -1));
5121 SetLastXChosen();
5122 break;
5123 case SCI_HOMEDISPLAYEXTEND:
5124 MovePositionTo(MovePositionSoVisible(
5125 StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
5126 SetLastXChosen();
5127 break;
5128 case SCI_LINEENDDISPLAY:
5129 MovePositionTo(MovePositionSoVisible(
5130 StartEndDisplayLine(sel.MainCaret(), false), 1));
5131 SetLastXChosen();
5132 break;
5133 case SCI_LINEENDDISPLAYEXTEND:
5134 MovePositionTo(MovePositionSoVisible(
5135 StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
5136 SetLastXChosen();
5137 break;
5139 return 0;
5142 int Editor::KeyDefault(int, int) {
5143 return 0;
5146 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
5147 DwellEnd(false);
5148 int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
5149 (alt ? SCI_ALT : 0);
5150 int msg = kmap.Find(key, modifiers);
5151 if (msg) {
5152 if (consumed)
5153 *consumed = true;
5154 return WndProc(msg, 0, 0);
5155 } else {
5156 if (consumed)
5157 *consumed = false;
5158 return KeyDefault(key, modifiers);
5162 void Editor::SetWhitespaceVisible(int view) {
5163 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(view);
5166 int Editor::GetWhitespaceVisible() {
5167 return vs.viewWhitespace;
5170 void Editor::Indent(bool forwards) {
5171 for (size_t r=0; r<sel.Count(); r++) {
5172 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
5173 int caretPosition = sel.Range(r).caret.Position();
5174 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
5175 if (lineOfAnchor == lineCurrentPos) {
5176 if (forwards) {
5177 UndoGroup ug(pdoc);
5178 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
5179 caretPosition = sel.Range(r).caret.Position();
5180 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
5181 pdoc->tabIndents) {
5182 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5183 int indentationStep = pdoc->IndentSize();
5184 pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
5185 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5186 } else {
5187 if (pdoc->useTabs) {
5188 pdoc->InsertChar(caretPosition, '\t');
5189 sel.Range(r) = SelectionRange(caretPosition+1);
5190 } else {
5191 int numSpaces = (pdoc->tabInChars) -
5192 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
5193 if (numSpaces < 1)
5194 numSpaces = pdoc->tabInChars;
5195 for (int i = 0; i < numSpaces; i++) {
5196 pdoc->InsertChar(caretPosition + i, ' ');
5198 sel.Range(r) = SelectionRange(caretPosition+numSpaces);
5201 } else {
5202 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
5203 pdoc->tabIndents) {
5204 UndoGroup ug(pdoc);
5205 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5206 int indentationStep = pdoc->IndentSize();
5207 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
5208 SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
5209 } else {
5210 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
5211 pdoc->tabInChars;
5212 if (newColumn < 0)
5213 newColumn = 0;
5214 int newPos = caretPosition;
5215 while (pdoc->GetColumn(newPos) > newColumn)
5216 newPos--;
5217 sel.Range(r) = SelectionRange(newPos);
5220 } else { // Multiline
5221 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
5222 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
5223 // Multiple lines selected so indent / dedent
5224 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
5225 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
5226 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
5227 lineBottomSel--; // If not selecting any characters on a line, do not indent
5229 UndoGroup ug(pdoc);
5230 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
5232 if (lineOfAnchor < lineCurrentPos) {
5233 if (currentPosPosOnLine == 0)
5234 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5235 else
5236 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
5237 } else {
5238 if (anchorPosOnLine == 0)
5239 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5240 else
5241 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
5248 * Search of a text in the document, in the given range.
5249 * @return The position of the found text, -1 if not found.
5251 long Editor::FindText(
5252 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5253 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5254 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
5256 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
5257 int lengthFound = istrlen(ft->lpstrText);
5258 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
5259 (wParam & SCFIND_MATCHCASE) != 0,
5260 (wParam & SCFIND_WHOLEWORD) != 0,
5261 (wParam & SCFIND_WORDSTART) != 0,
5262 (wParam & SCFIND_REGEXP) != 0,
5263 wParam,
5264 &lengthFound);
5265 if (pos != -1) {
5266 ft->chrgText.cpMin = pos;
5267 ft->chrgText.cpMax = pos + lengthFound;
5269 return pos;
5273 * Relocatable search support : Searches relative to current selection
5274 * point and sets the selection to the found text range with
5275 * each search.
5278 * Anchor following searches at current selection start: This allows
5279 * multiple incremental interactive searches to be macro recorded
5280 * while still setting the selection to found text so the find/select
5281 * operation is self-contained.
5283 void Editor::SearchAnchor() {
5284 searchAnchor = SelectionStart().Position();
5288 * Find text from current search anchor: Must call @c SearchAnchor first.
5289 * Used for next text and previous text requests.
5290 * @return The position of the found text, -1 if not found.
5292 long Editor::SearchText(
5293 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
5294 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5295 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5296 sptr_t lParam) { ///< The text to search for.
5298 const char *txt = reinterpret_cast<char *>(lParam);
5299 int pos;
5300 int lengthFound = istrlen(txt);
5301 if (iMessage == SCI_SEARCHNEXT) {
5302 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
5303 (wParam & SCFIND_MATCHCASE) != 0,
5304 (wParam & SCFIND_WHOLEWORD) != 0,
5305 (wParam & SCFIND_WORDSTART) != 0,
5306 (wParam & SCFIND_REGEXP) != 0,
5307 wParam,
5308 &lengthFound);
5309 } else {
5310 pos = pdoc->FindText(searchAnchor, 0, txt,
5311 (wParam & SCFIND_MATCHCASE) != 0,
5312 (wParam & SCFIND_WHOLEWORD) != 0,
5313 (wParam & SCFIND_WORDSTART) != 0,
5314 (wParam & SCFIND_REGEXP) != 0,
5315 wParam,
5316 &lengthFound);
5319 if (pos != -1) {
5320 SetSelection(pos, pos + lengthFound);
5323 return pos;
5327 * Search for text in the target range of the document.
5328 * @return The position of the found text, -1 if not found.
5330 long Editor::SearchInTarget(const char *text, int length) {
5331 int lengthFound = length;
5332 int pos = pdoc->FindText(targetStart, targetEnd, text,
5333 (searchFlags & SCFIND_MATCHCASE) != 0,
5334 (searchFlags & SCFIND_WHOLEWORD) != 0,
5335 (searchFlags & SCFIND_WORDSTART) != 0,
5336 (searchFlags & SCFIND_REGEXP) != 0,
5337 searchFlags,
5338 &lengthFound);
5339 if (pos != -1) {
5340 targetStart = pos;
5341 targetEnd = pos + lengthFound;
5343 return pos;
5346 void Editor::GoToLine(int lineNo) {
5347 if (lineNo > pdoc->LinesTotal())
5348 lineNo = pdoc->LinesTotal();
5349 if (lineNo < 0)
5350 lineNo = 0;
5351 SetEmptySelection(pdoc->LineStart(lineNo));
5352 ShowCaretAtCurrentPosition();
5353 EnsureCaretVisible();
5356 static bool Close(Point pt1, Point pt2) {
5357 if (abs(pt1.x - pt2.x) > 3)
5358 return false;
5359 if (abs(pt1.y - pt2.y) > 3)
5360 return false;
5361 return true;
5364 char *Editor::CopyRange(int start, int end) {
5365 char *text = 0;
5366 if (start < end) {
5367 int len = end - start;
5368 text = new char[len + 1];
5369 for (int i = 0; i < len; i++) {
5370 text[i] = pdoc->CharAt(start + i);
5372 text[len] = '\0';
5374 return text;
5377 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
5378 if (sel.Empty()) {
5379 if (allowLineCopy) {
5380 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
5381 int start = pdoc->LineStart(currentLine);
5382 int end = pdoc->LineEnd(currentLine);
5384 char *text = CopyRange(start, end);
5385 int textLen = text ? strlen(text) : 0;
5386 // include room for \r\n\0
5387 textLen += 3;
5388 char *textWithEndl = new char[textLen];
5389 textWithEndl[0] = '\0';
5390 if (text)
5391 strncat(textWithEndl, text, textLen);
5392 if (pdoc->eolMode != SC_EOL_LF)
5393 strncat(textWithEndl, "\r", textLen);
5394 if (pdoc->eolMode != SC_EOL_CR)
5395 strncat(textWithEndl, "\n", textLen);
5396 ss->Set(textWithEndl, strlen(textWithEndl) + 1,
5397 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, true);
5398 delete []text;
5400 } else {
5401 int delimiterLength = 0;
5402 if (sel.selType == Selection::selRectangle) {
5403 if (pdoc->eolMode == SC_EOL_CRLF) {
5404 delimiterLength = 2;
5405 } else {
5406 delimiterLength = 1;
5409 int size = sel.Length() + delimiterLength * sel.Count();
5410 char *text = new char[size + 1];
5411 int j = 0;
5412 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
5413 if (sel.selType == Selection::selRectangle)
5414 std::sort(rangesInOrder.begin(), rangesInOrder.end());
5415 for (size_t r=0; r<rangesInOrder.size(); r++) {
5416 SelectionRange current = rangesInOrder[r];
5417 for (int i = current.Start().Position();
5418 i < current.End().Position();
5419 i++) {
5420 text[j++] = pdoc->CharAt(i);
5422 if (sel.selType == Selection::selRectangle) {
5423 if (pdoc->eolMode != SC_EOL_LF) {
5424 text[j++] = '\r';
5426 if (pdoc->eolMode != SC_EOL_CR) {
5427 text[j++] = '\n';
5431 text[size] = '\0';
5432 ss->Set(text, size + 1, pdoc->dbcsCodePage,
5433 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
5437 void Editor::CopyRangeToClipboard(int start, int end) {
5438 start = pdoc->ClampPositionIntoDocument(start);
5439 end = pdoc->ClampPositionIntoDocument(end);
5440 SelectionText selectedText;
5441 selectedText.Set(CopyRange(start, end), end - start + 1,
5442 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5443 CopyToClipboard(selectedText);
5446 void Editor::CopyText(int length, const char *text) {
5447 SelectionText selectedText;
5448 selectedText.Copy(text, length + 1,
5449 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5450 CopyToClipboard(selectedText);
5453 void Editor::SetDragPosition(SelectionPosition newPos) {
5454 if (newPos.Position() >= 0) {
5455 newPos = MovePositionOutsideChar(newPos, 1);
5456 posDrop = newPos;
5458 if (!(posDrag == newPos)) {
5459 caret.on = true;
5460 SetTicking(true);
5461 InvalidateCaret();
5462 posDrag = newPos;
5463 InvalidateCaret();
5467 void Editor::DisplayCursor(Window::Cursor c) {
5468 if (cursorMode == SC_CURSORNORMAL)
5469 wMain.SetCursor(c);
5470 else
5471 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
5474 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
5475 int xMove = ptStart.x - ptNow.x;
5476 int yMove = ptStart.y - ptNow.y;
5477 int distanceSquared = xMove * xMove + yMove * yMove;
5478 return distanceSquared > 16;
5481 void Editor::StartDrag() {
5482 // Always handled by subclasses
5483 //SetMouseCapture(true);
5484 //DisplayCursor(Window::cursorArrow);
5487 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
5488 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
5489 if (inDragDrop == ddDragging)
5490 dropWentOutside = false;
5492 bool positionWasInSelection = PositionInSelection(position.Position());
5494 bool positionOnEdgeOfSelection =
5495 (position == SelectionStart()) || (position == SelectionEnd());
5497 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
5498 (positionOnEdgeOfSelection && !moving)) {
5500 SelectionPosition selStart = SelectionStart();
5501 SelectionPosition selEnd = SelectionEnd();
5503 UndoGroup ug(pdoc);
5505 SelectionPosition positionAfterDeletion = position;
5506 if ((inDragDrop == ddDragging) && moving) {
5507 // Remove dragged out text
5508 if (rectangular || sel.selType == Selection::selLines) {
5509 for (size_t r=0; r<sel.Count(); r++) {
5510 if (position >= sel.Range(r).Start()) {
5511 if (position > sel.Range(r).End()) {
5512 positionAfterDeletion.Add(-sel.Range(r).Length());
5513 } else {
5514 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
5518 } else {
5519 if (position > selStart) {
5520 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
5523 ClearSelection();
5525 position = positionAfterDeletion;
5527 if (rectangular) {
5528 PasteRectangular(position, value, istrlen(value));
5529 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
5530 SetEmptySelection(position);
5531 } else {
5532 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
5533 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
5534 if (pdoc->InsertCString(position.Position(), value)) {
5535 SelectionPosition posAfterInsertion = position;
5536 posAfterInsertion.Add(istrlen(value));
5537 SetSelection(posAfterInsertion, position);
5540 } else if (inDragDrop == ddDragging) {
5541 SetEmptySelection(position);
5546 * @return true if given position is inside the selection,
5548 bool Editor::PositionInSelection(int pos) {
5549 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
5550 for (size_t r=0; r<sel.Count(); r++) {
5551 if (sel.Range(r).Contains(pos))
5552 return true;
5554 return false;
5557 bool Editor::PointInSelection(Point pt) {
5558 SelectionPosition pos = SPositionFromLocation(pt);
5559 int xPos = XFromPosition(pos);
5560 for (size_t r=0; r<sel.Count(); r++) {
5561 SelectionRange range = sel.Range(r);
5562 if (range.Contains(pos)) {
5563 bool hit = true;
5564 if (pos == range.Start()) {
5565 // see if just before selection
5566 if (pt.x < xPos) {
5567 hit = false;
5570 if (pos == range.End()) {
5571 // see if just after selection
5572 if (pt.x > xPos) {
5573 hit = false;
5576 if (hit)
5577 return true;
5580 return false;
5583 bool Editor::PointInSelMargin(Point pt) {
5584 // Really means: "Point in a margin"
5585 if (vs.fixedColumnWidth > 0) { // There is a margin
5586 PRectangle rcSelMargin = GetClientRectangle();
5587 rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth;
5588 return rcSelMargin.Contains(pt);
5589 } else {
5590 return false;
5594 void Editor::LineSelection(int lineCurrent_, int lineAnchor_) {
5595 if (lineAnchor_ < lineCurrent_) {
5596 SetSelection(pdoc->LineStart(lineCurrent_ + 1),
5597 pdoc->LineStart(lineAnchor_));
5598 } else if (lineAnchor_ > lineCurrent_) {
5599 SetSelection(pdoc->LineStart(lineCurrent_),
5600 pdoc->LineStart(lineAnchor_ + 1));
5601 } else { // Same line, select it
5602 SetSelection(pdoc->LineStart(lineAnchor_ + 1),
5603 pdoc->LineStart(lineAnchor_));
5607 void Editor::DwellEnd(bool mouseMoved) {
5608 if (mouseMoved)
5609 ticksToDwell = dwellDelay;
5610 else
5611 ticksToDwell = SC_TIME_FOREVER;
5612 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
5613 dwelling = false;
5614 NotifyDwelling(ptMouseLast, dwelling);
5618 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
5619 return ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0)
5620 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
5623 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
5624 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
5625 ptMouseLast = pt;
5626 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
5627 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
5628 inDragDrop = ddNone;
5629 sel.SetMoveExtends(false);
5631 bool processed = NotifyMarginClick(pt, shift, ctrl, alt);
5632 if (processed)
5633 return;
5635 NotifyIndicatorClick(true, newPos.Position(), shift, ctrl, alt);
5637 bool inSelMargin = PointInSelMargin(pt);
5638 if (shift & !inSelMargin) {
5639 SetSelection(newPos.Position());
5641 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
5642 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
5643 SetMouseCapture(true);
5644 SetEmptySelection(newPos.Position());
5645 bool doubleClick = false;
5646 // Stop mouse button bounce changing selection type
5647 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
5648 if (selectionType == selChar) {
5649 selectionType = selWord;
5650 doubleClick = true;
5651 } else if (selectionType == selWord) {
5652 selectionType = selLine;
5653 } else {
5654 selectionType = selChar;
5655 originalAnchorPos = sel.MainCaret();
5659 if (selectionType == selWord) {
5660 if (sel.MainCaret() >= originalAnchorPos) { // Moved forward
5661 SetSelection(pdoc->ExtendWordSelect(sel.MainCaret(), 1),
5662 pdoc->ExtendWordSelect(originalAnchorPos, -1));
5663 } else { // Moved backward
5664 SetSelection(pdoc->ExtendWordSelect(sel.MainCaret(), -1),
5665 pdoc->ExtendWordSelect(originalAnchorPos, 1));
5667 } else if (selectionType == selLine) {
5668 lineAnchor = LineFromLocation(pt);
5669 SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor));
5670 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
5671 } else {
5672 SetEmptySelection(sel.MainCaret());
5674 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
5675 if (doubleClick) {
5676 NotifyDoubleClick(pt, shift, ctrl, alt);
5677 if (PositionIsHotspot(newPos.Position()))
5678 NotifyHotSpotDoubleClicked(newPos.Position(), shift, ctrl, alt);
5680 } else { // Single click
5681 if (inSelMargin) {
5682 sel.selType = Selection::selStream;
5683 if (ctrl) {
5684 SelectAll();
5685 lastClickTime = curTime;
5686 return;
5688 if (!shift) {
5689 lineAnchor = LineFromLocation(pt);
5690 // Single click in margin: select whole line
5691 LineSelection(lineAnchor, lineAnchor);
5692 SetSelection(pdoc->LineStart(lineAnchor + 1),
5693 pdoc->LineStart(lineAnchor));
5694 } else {
5695 // Single shift+click in margin: select from line anchor to clicked line
5696 if (sel.MainAnchor() > sel.MainCaret())
5697 lineAnchor = pdoc->LineFromPosition(sel.MainAnchor() - 1);
5698 else
5699 lineAnchor = pdoc->LineFromPosition(sel.MainAnchor());
5700 int lineStart = LineFromLocation(pt);
5701 LineSelection(lineStart, lineAnchor);
5702 //lineAnchor = lineStart; // Keep the same anchor for ButtonMove
5705 SetDragPosition(SelectionPosition(invalidPosition));
5706 SetMouseCapture(true);
5707 selectionType = selLine;
5708 } else {
5709 if (PointIsHotspot(pt)) {
5710 NotifyHotSpotClicked(newPos.Position(), shift, ctrl, alt);
5712 if (!shift) {
5713 if (PointInSelection(pt) && !SelectionEmpty())
5714 inDragDrop = ddInitial;
5715 else
5716 inDragDrop = ddNone;
5718 SetMouseCapture(true);
5719 if (inDragDrop != ddInitial) {
5720 SetDragPosition(SelectionPosition(invalidPosition));
5721 if (!shift) {
5722 if (ctrl && multipleSelection) {
5723 SelectionRange range(newPos);
5724 sel.TentativeSelection(range);
5725 InvalidateSelection(range, true);
5726 } else {
5727 InvalidateSelection(SelectionRange(newPos), true);
5728 if (sel.Count() > 1)
5729 Redraw();
5730 sel.Clear();
5731 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
5732 SetSelection(newPos, newPos);
5735 SelectionPosition anchorCurrent = newPos;
5736 if (shift)
5737 anchorCurrent = sel.IsRectangular() ?
5738 sel.Rectangular().anchor : sel.RangeMain().anchor;
5739 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
5740 selectionType = selChar;
5741 originalAnchorPos = sel.MainCaret();
5742 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
5743 SetRectangularRange();
5747 lastClickTime = curTime;
5748 lastXChosen = pt.x;
5749 ShowCaretAtCurrentPosition();
5752 bool Editor::PositionIsHotspot(int position) {
5753 return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
5756 bool Editor::PointIsHotspot(Point pt) {
5757 int pos = PositionFromLocation(pt, true);
5758 if (pos == INVALID_POSITION)
5759 return false;
5760 return PositionIsHotspot(pos);
5763 void Editor::SetHotSpotRange(Point *pt) {
5764 if (pt) {
5765 int pos = PositionFromLocation(*pt);
5767 // If we don't limit this to word characters then the
5768 // range can encompass more than the run range and then
5769 // the underline will not be drawn properly.
5770 int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
5771 int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
5773 // Only invalidate the range if the hotspot range has changed...
5774 if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
5775 if (hsStart != -1) {
5776 InvalidateRange(hsStart, hsEnd);
5778 hsStart = hsStart_;
5779 hsEnd = hsEnd_;
5780 InvalidateRange(hsStart, hsEnd);
5782 } else {
5783 if (hsStart != -1) {
5784 int hsStart_ = hsStart;
5785 int hsEnd_ = hsEnd;
5786 hsStart = -1;
5787 hsEnd = -1;
5788 InvalidateRange(hsStart_, hsEnd_);
5789 } else {
5790 hsStart = -1;
5791 hsEnd = -1;
5796 void Editor::GetHotSpotRange(int& hsStart_, int& hsEnd_) {
5797 hsStart_ = hsStart;
5798 hsEnd_ = hsEnd;
5801 void Editor::ButtonMove(Point pt) {
5802 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
5803 DwellEnd(true);
5806 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
5807 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
5808 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
5810 if (inDragDrop == ddInitial) {
5811 if (DragThreshold(ptMouseLast, pt)) {
5812 SetMouseCapture(false);
5813 SetDragPosition(movePos);
5814 CopySelectionRange(&drag);
5815 StartDrag();
5817 return;
5820 ptMouseLast = pt;
5821 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
5822 if (HaveMouseCapture()) {
5824 // Slow down autoscrolling/selection
5825 autoScrollTimer.ticksToWait -= timer.tickSize;
5826 if (autoScrollTimer.ticksToWait > 0)
5827 return;
5828 autoScrollTimer.ticksToWait = autoScrollDelay;
5830 // Adjust selection
5831 if (posDrag.IsValid()) {
5832 SetDragPosition(movePos);
5833 } else {
5834 if (selectionType == selChar) {
5835 if (sel.IsRectangular()) {
5836 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
5837 SetSelection(movePos, sel.RangeMain().anchor);
5838 } else if (sel.Count() > 1) {
5839 SelectionRange range(movePos, sel.RangeMain().anchor);
5840 sel.TentativeSelection(range);
5841 InvalidateSelection(range, true);
5842 } else {
5843 SetSelection(movePos, sel.RangeMain().anchor);
5845 } else if (selectionType == selWord) {
5846 // Continue selecting by word
5847 if (movePos.Position() == originalAnchorPos) { // Didn't move
5848 // No need to do anything. Previously this case was lumped
5849 // in with "Moved forward", but that can be harmful in this
5850 // case: a handler for the NotifyDoubleClick re-adjusts
5851 // the selection for a fancier definition of "word" (for
5852 // example, in Perl it is useful to include the leading
5853 // '$', '%' or '@' on variables for word selection). In this
5854 // the ButtonMove() called via Tick() for auto-scrolling
5855 // could result in the fancier word selection adjustment
5856 // being unmade.
5857 } else if (movePos.Position() > originalAnchorPos) { // Moved forward
5858 SetSelection(pdoc->ExtendWordSelect(movePos.Position(), 1),
5859 pdoc->ExtendWordSelect(originalAnchorPos, -1));
5860 } else { // Moved backward
5861 SetSelection(pdoc->ExtendWordSelect(movePos.Position(), -1),
5862 pdoc->ExtendWordSelect(originalAnchorPos, 1));
5864 } else {
5865 // Continue selecting by line
5866 int lineMove = LineFromLocation(pt);
5867 LineSelection(lineMove, lineAnchor);
5871 // Autoscroll
5872 PRectangle rcClient = GetClientRectangle();
5873 if (pt.y > rcClient.bottom) {
5874 int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
5875 if (lineMove < 0) {
5876 lineMove = cs.DisplayFromDoc(pdoc->LinesTotal() - 1);
5878 ScrollTo(lineMove - LinesOnScreen() + 1);
5879 Redraw();
5880 } else if (pt.y < rcClient.top) {
5881 int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
5882 ScrollTo(lineMove - 1);
5883 Redraw();
5885 EnsureCaretVisible(false, false, true);
5887 if (hsStart != -1 && !PositionIsHotspot(movePos.Position()))
5888 SetHotSpotRange(NULL);
5890 } else {
5891 if (vs.fixedColumnWidth > 0) { // There is a margin
5892 if (PointInSelMargin(pt)) {
5893 DisplayCursor(Window::cursorReverseArrow);
5894 return; // No need to test for selection
5897 // Display regular (drag) cursor over selection
5898 if (PointInSelection(pt) && !SelectionEmpty()) {
5899 DisplayCursor(Window::cursorArrow);
5900 } else if (PointIsHotspot(pt)) {
5901 DisplayCursor(Window::cursorHand);
5902 SetHotSpotRange(&pt);
5903 } else {
5904 DisplayCursor(Window::cursorText);
5905 SetHotSpotRange(NULL);
5910 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
5911 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
5912 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
5913 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
5914 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
5915 if (inDragDrop == ddInitial) {
5916 inDragDrop = ddNone;
5917 SetEmptySelection(newPos.Position());
5919 if (HaveMouseCapture()) {
5920 if (PointInSelMargin(pt)) {
5921 DisplayCursor(Window::cursorReverseArrow);
5922 } else {
5923 DisplayCursor(Window::cursorText);
5924 SetHotSpotRange(NULL);
5926 ptMouseLast = pt;
5927 SetMouseCapture(false);
5928 NotifyIndicatorClick(false, newPos.Position(), false, false, false);
5929 if (inDragDrop == ddDragging) {
5930 SelectionPosition selStart = SelectionStart();
5931 SelectionPosition selEnd = SelectionEnd();
5932 if (selStart < selEnd) {
5933 if (drag.len) {
5934 if (ctrl) {
5935 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
5936 SetSelection(newPos.Position(), newPos.Position() + drag.len);
5938 } else if (newPos < selStart) {
5939 pdoc->DeleteChars(selStart.Position(), drag.len);
5940 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
5941 SetSelection(newPos.Position(), newPos.Position() + drag.len);
5943 } else if (newPos > selEnd) {
5944 pdoc->DeleteChars(selStart.Position(), drag.len);
5945 newPos.Add(-drag.len);
5946 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
5947 SetSelection(newPos.Position(), newPos.Position() + drag.len);
5949 } else {
5950 SetEmptySelection(newPos.Position());
5952 drag.Free();
5954 selectionType = selChar;
5956 } else {
5957 if (selectionType == selChar) {
5958 if (sel.Count() > 1) {
5959 sel.RangeMain() =
5960 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
5961 InvalidateSelection(sel.RangeMain(), true);
5962 } else {
5963 SetSelection(newPos, sel.RangeMain().anchor);
5966 sel.CommitTentative();
5968 SetRectangularRange();
5969 lastClickTime = curTime;
5970 lastClick = pt;
5971 lastXChosen = pt.x;
5972 if (sel.selType == Selection::selStream) {
5973 SetLastXChosen();
5975 inDragDrop = ddNone;
5976 EnsureCaretVisible(false);
5980 // Called frequently to perform background UI including
5981 // caret blinking and automatic scrolling.
5982 void Editor::Tick() {
5983 if (HaveMouseCapture()) {
5984 // Auto scroll
5985 ButtonMove(ptMouseLast);
5987 if (caret.period > 0) {
5988 timer.ticksToWait -= timer.tickSize;
5989 if (timer.ticksToWait <= 0) {
5990 caret.on = !caret.on;
5991 timer.ticksToWait = caret.period;
5992 if (caret.active) {
5993 InvalidateCaret();
5997 if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) {
5998 scrollWidth = lineWidthMaxSeen;
5999 SetScrollBars();
6001 if ((dwellDelay < SC_TIME_FOREVER) &&
6002 (ticksToDwell > 0) &&
6003 (!HaveMouseCapture())) {
6004 ticksToDwell -= timer.tickSize;
6005 if (ticksToDwell <= 0) {
6006 dwelling = true;
6007 NotifyDwelling(ptMouseLast, dwelling);
6012 bool Editor::Idle() {
6014 bool idleDone;
6016 bool wrappingDone = wrapState == eWrapNone;
6018 if (!wrappingDone) {
6019 // Wrap lines during idle.
6020 WrapLines(false, -1);
6021 // No more wrapping
6022 if (wrapStart == wrapEnd)
6023 wrappingDone = true;
6026 // Add more idle things to do here, but make sure idleDone is
6027 // set correctly before the function returns. returning
6028 // false will stop calling this idle funtion until SetIdle() is
6029 // called again.
6031 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
6033 return !idleDone;
6036 void Editor::SetFocusState(bool focusState) {
6037 hasFocus = focusState;
6038 NotifyFocus(hasFocus);
6039 if (hasFocus) {
6040 ShowCaretAtCurrentPosition();
6041 } else {
6042 CancelModes();
6043 DropCaret();
6047 bool Editor::PaintContains(PRectangle rc) {
6048 if (rc.Empty()) {
6049 return true;
6050 } else {
6051 return rcPaint.Contains(rc);
6055 bool Editor::PaintContainsMargin() {
6056 PRectangle rcSelMargin = GetClientRectangle();
6057 rcSelMargin.right = vs.fixedColumnWidth;
6058 return PaintContains(rcSelMargin);
6061 void Editor::CheckForChangeOutsidePaint(Range r) {
6062 if (paintState == painting && !paintingAllText) {
6063 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
6064 if (!r.Valid())
6065 return;
6067 PRectangle rcRange = RectangleFromRange(r.start, r.end);
6068 PRectangle rcText = GetTextRectangle();
6069 if (rcRange.top < rcText.top) {
6070 rcRange.top = rcText.top;
6072 if (rcRange.bottom > rcText.bottom) {
6073 rcRange.bottom = rcText.bottom;
6076 if (!PaintContains(rcRange)) {
6077 AbandonPaint();
6082 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
6083 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
6084 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
6085 CheckForChangeOutsidePaint(Range(braces[0]));
6086 CheckForChangeOutsidePaint(Range(pos0));
6087 braces[0] = pos0;
6089 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
6090 CheckForChangeOutsidePaint(Range(braces[1]));
6091 CheckForChangeOutsidePaint(Range(pos1));
6092 braces[1] = pos1;
6094 bracesMatchStyle = matchStyle;
6095 if (paintState == notPainting) {
6096 Redraw();
6101 void Editor::SetAnnotationHeights(int start, int end) {
6102 if (vs.annotationVisible) {
6103 for (int line=start; line<end; line++) {
6104 cs.SetHeight(line, pdoc->AnnotationLines(line) + 1);
6109 void Editor::SetDocPointer(Document *document) {
6110 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
6111 pdoc->RemoveWatcher(this, 0);
6112 pdoc->Release();
6113 if (document == NULL) {
6114 pdoc = new Document();
6115 } else {
6116 pdoc = document;
6118 pdoc->AddRef();
6120 // Ensure all positions within document
6121 sel.Clear();
6122 targetStart = 0;
6123 targetEnd = 0;
6125 braces[0] = invalidPosition;
6126 braces[1] = invalidPosition;
6128 // Reset the contraction state to fully shown.
6129 cs.Clear();
6130 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6131 SetAnnotationHeights(0, pdoc->LinesTotal());
6132 llc.Deallocate();
6133 NeedWrapping();
6135 pdoc->AddWatcher(this, 0);
6136 SetScrollBars();
6137 Redraw();
6140 void Editor::SetAnnotationVisible(int visible) {
6141 if (vs.annotationVisible != visible) {
6142 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
6143 vs.annotationVisible = visible;
6144 if (changedFromOrToHidden) {
6145 int dir = vs.annotationVisible ? 1 : -1;
6146 for (int line=0; line<pdoc->LinesTotal(); line++) {
6147 int annotationLines = pdoc->AnnotationLines(line);
6148 if (annotationLines > 0) {
6149 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
6157 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
6159 void Editor::Expand(int &line, bool doExpand) {
6160 int lineMaxSubord = pdoc->GetLastChild(line);
6161 line++;
6162 while (line <= lineMaxSubord) {
6163 if (doExpand)
6164 cs.SetVisible(line, line, true);
6165 int level = pdoc->GetLevel(line);
6166 if (level & SC_FOLDLEVELHEADERFLAG) {
6167 if (doExpand && cs.GetExpanded(line)) {
6168 Expand(line, true);
6169 } else {
6170 Expand(line, false);
6172 } else {
6173 line++;
6178 void Editor::ToggleContraction(int line) {
6179 if (line >= 0) {
6180 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
6181 line = pdoc->GetFoldParent(line);
6182 if (line < 0)
6183 return;
6186 if (cs.GetExpanded(line)) {
6187 int lineMaxSubord = pdoc->GetLastChild(line);
6188 cs.SetExpanded(line, 0);
6189 if (lineMaxSubord > line) {
6190 cs.SetVisible(line + 1, lineMaxSubord, false);
6192 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
6193 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
6194 // This does not re-expand the fold
6195 EnsureCaretVisible();
6198 SetScrollBars();
6199 Redraw();
6202 } else {
6203 if (!(cs.GetVisible(line))) {
6204 EnsureLineVisible(line, false);
6205 GoToLine(line);
6207 cs.SetExpanded(line, 1);
6208 Expand(line, true);
6209 SetScrollBars();
6210 Redraw();
6216 * Recurse up from this line to find any folds that prevent this line from being visible
6217 * and unfold them all.
6219 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
6221 // In case in need of wrapping to ensure DisplayFromDoc works.
6222 WrapLines(true, -1);
6224 if (!cs.GetVisible(lineDoc)) {
6225 int lineParent = pdoc->GetFoldParent(lineDoc);
6226 if (lineParent >= 0) {
6227 if (lineDoc != lineParent)
6228 EnsureLineVisible(lineParent, enforcePolicy);
6229 if (!cs.GetExpanded(lineParent)) {
6230 cs.SetExpanded(lineParent, 1);
6231 Expand(lineParent, true);
6234 SetScrollBars();
6235 Redraw();
6237 if (enforcePolicy) {
6238 int lineDisplay = cs.DisplayFromDoc(lineDoc);
6239 if (visiblePolicy & VISIBLE_SLOP) {
6240 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
6241 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
6242 SetVerticalScrollPos();
6243 Redraw();
6244 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
6245 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
6246 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
6247 SetVerticalScrollPos();
6248 Redraw();
6250 } else {
6251 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
6252 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
6253 SetVerticalScrollPos();
6254 Redraw();
6260 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
6261 UndoGroup ug(pdoc);
6262 if (length == -1)
6263 length = istrlen(text);
6264 if (replacePatterns) {
6265 text = pdoc->SubstituteByPosition(text, &length);
6266 if (!text) {
6267 return 0;
6270 if (targetStart != targetEnd)
6271 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
6272 targetEnd = targetStart;
6273 pdoc->InsertString(targetStart, text, length);
6274 targetEnd = targetStart + length;
6275 return length;
6278 bool Editor::IsUnicodeMode() const {
6279 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
6282 int Editor::CodePage() const {
6283 if (pdoc)
6284 return pdoc->dbcsCodePage;
6285 else
6286 return 0;
6289 int Editor::WrapCount(int line) {
6290 AutoSurface surface(this);
6291 AutoLineLayout ll(llc, RetrieveLineLayout(line));
6293 if (surface && ll) {
6294 LayoutLine(line, surface, vs, ll, wrapWidth);
6295 return ll->lines;
6296 } else {
6297 return 1;
6301 void Editor::AddStyledText(char *buffer, int appendLength) {
6302 // The buffer consists of alternating character bytes and style bytes
6303 size_t textLength = appendLength / 2;
6304 char *text = new char[textLength];
6305 size_t i;
6306 for (i = 0;i < textLength;i++) {
6307 text[i] = buffer[i*2];
6309 pdoc->InsertString(CurrentPosition(), text, textLength);
6310 for (i = 0;i < textLength;i++) {
6311 text[i] = buffer[i*2+1];
6313 pdoc->StartStyling(CurrentPosition(), static_cast<char>(0xff));
6314 pdoc->SetStyles(textLength, text);
6315 delete []text;
6316 SetEmptySelection(sel.MainCaret() + textLength);
6319 static bool ValidMargin(unsigned long wParam) {
6320 return wParam < ViewStyle::margins;
6323 static char *CharPtrFromSPtr(sptr_t lParam) {
6324 return reinterpret_cast<char *>(lParam);
6327 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
6328 vs.EnsureStyle(wParam);
6329 switch (iMessage) {
6330 case SCI_STYLESETFORE:
6331 vs.styles[wParam].fore.desired = ColourDesired(lParam);
6332 break;
6333 case SCI_STYLESETBACK:
6334 vs.styles[wParam].back.desired = ColourDesired(lParam);
6335 break;
6336 case SCI_STYLESETBOLD:
6337 vs.styles[wParam].bold = lParam != 0;
6338 break;
6339 case SCI_STYLESETITALIC:
6340 vs.styles[wParam].italic = lParam != 0;
6341 break;
6342 case SCI_STYLESETEOLFILLED:
6343 vs.styles[wParam].eolFilled = lParam != 0;
6344 break;
6345 case SCI_STYLESETSIZE:
6346 vs.styles[wParam].size = lParam;
6347 break;
6348 case SCI_STYLESETFONT:
6349 if (lParam != 0) {
6350 vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam));
6352 break;
6353 case SCI_STYLESETUNDERLINE:
6354 vs.styles[wParam].underline = lParam != 0;
6355 break;
6356 case SCI_STYLESETCASE:
6357 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
6358 break;
6359 case SCI_STYLESETCHARACTERSET:
6360 vs.styles[wParam].characterSet = lParam;
6361 break;
6362 case SCI_STYLESETVISIBLE:
6363 vs.styles[wParam].visible = lParam != 0;
6364 break;
6365 case SCI_STYLESETCHANGEABLE:
6366 vs.styles[wParam].changeable = lParam != 0;
6367 break;
6368 case SCI_STYLESETHOTSPOT:
6369 vs.styles[wParam].hotspot = lParam != 0;
6370 break;
6372 InvalidateStyleRedraw();
6375 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
6376 vs.EnsureStyle(wParam);
6377 switch (iMessage) {
6378 case SCI_STYLEGETFORE:
6379 return vs.styles[wParam].fore.desired.AsLong();
6380 case SCI_STYLEGETBACK:
6381 return vs.styles[wParam].back.desired.AsLong();
6382 case SCI_STYLEGETBOLD:
6383 return vs.styles[wParam].bold ? 1 : 0;
6384 case SCI_STYLEGETITALIC:
6385 return vs.styles[wParam].italic ? 1 : 0;
6386 case SCI_STYLEGETEOLFILLED:
6387 return vs.styles[wParam].eolFilled ? 1 : 0;
6388 case SCI_STYLEGETSIZE:
6389 return vs.styles[wParam].size;
6390 case SCI_STYLEGETFONT:
6391 if (!vs.styles[wParam].fontName)
6392 return 0;
6393 if (lParam != 0)
6394 strcpy(CharPtrFromSPtr(lParam), vs.styles[wParam].fontName);
6395 return strlen(vs.styles[wParam].fontName);
6396 case SCI_STYLEGETUNDERLINE:
6397 return vs.styles[wParam].underline ? 1 : 0;
6398 case SCI_STYLEGETCASE:
6399 return static_cast<int>(vs.styles[wParam].caseForce);
6400 case SCI_STYLEGETCHARACTERSET:
6401 return vs.styles[wParam].characterSet;
6402 case SCI_STYLEGETVISIBLE:
6403 return vs.styles[wParam].visible ? 1 : 0;
6404 case SCI_STYLEGETCHANGEABLE:
6405 return vs.styles[wParam].changeable ? 1 : 0;
6406 case SCI_STYLEGETHOTSPOT:
6407 return vs.styles[wParam].hotspot ? 1 : 0;
6409 return 0;
6412 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
6413 const int n = strlen(val);
6414 if (lParam != 0) {
6415 char *ptr = reinterpret_cast<char *>(lParam);
6416 strcpy(ptr, val);
6418 return n; // Not including NUL
6421 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
6422 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
6424 // Optional macro recording hook
6425 if (recordingMacro)
6426 NotifyMacroRecord(iMessage, wParam, lParam);
6428 switch (iMessage) {
6430 case SCI_GETTEXT: {
6431 if (lParam == 0)
6432 return pdoc->Length() + 1;
6433 if (wParam == 0)
6434 return 0;
6435 char *ptr = CharPtrFromSPtr(lParam);
6436 unsigned int iChar = 0;
6437 for (; iChar < wParam - 1; iChar++)
6438 ptr[iChar] = pdoc->CharAt(iChar);
6439 ptr[iChar] = '\0';
6440 return iChar;
6443 case SCI_SETTEXT: {
6444 if (lParam == 0)
6445 return 0;
6446 UndoGroup ug(pdoc);
6447 pdoc->DeleteChars(0, pdoc->Length());
6448 SetEmptySelection(0);
6449 pdoc->InsertCString(0, CharPtrFromSPtr(lParam));
6450 return 1;
6453 case SCI_GETTEXTLENGTH:
6454 return pdoc->Length();
6456 case SCI_CUT:
6457 Cut();
6458 SetLastXChosen();
6459 break;
6461 case SCI_COPY:
6462 Copy();
6463 break;
6465 case SCI_COPYALLOWLINE:
6466 CopyAllowLine();
6467 break;
6469 case SCI_COPYRANGE:
6470 CopyRangeToClipboard(wParam, lParam);
6471 break;
6473 case SCI_COPYTEXT:
6474 CopyText(wParam, CharPtrFromSPtr(lParam));
6475 break;
6477 case SCI_PASTE:
6478 Paste();
6479 if (!caretSticky) {
6480 SetLastXChosen();
6482 EnsureCaretVisible();
6483 break;
6485 case SCI_CLEAR:
6486 Clear();
6487 SetLastXChosen();
6488 EnsureCaretVisible();
6489 break;
6491 case SCI_UNDO:
6492 Undo();
6493 SetLastXChosen();
6494 break;
6496 case SCI_CANUNDO:
6497 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
6499 case SCI_EMPTYUNDOBUFFER:
6500 pdoc->DeleteUndoHistory();
6501 return 0;
6503 case SCI_GETFIRSTVISIBLELINE:
6504 return topLine;
6506 case SCI_SETFIRSTVISIBLELINE:
6507 ScrollTo(wParam);
6508 break;
6510 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
6511 int lineStart = pdoc->LineStart(wParam);
6512 int lineEnd = pdoc->LineStart(wParam + 1);
6513 if (lParam == 0) {
6514 return lineEnd - lineStart;
6516 char *ptr = CharPtrFromSPtr(lParam);
6517 int iPlace = 0;
6518 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
6519 ptr[iPlace++] = pdoc->CharAt(iChar);
6521 return iPlace;
6524 case SCI_GETLINECOUNT:
6525 if (pdoc->LinesTotal() == 0)
6526 return 1;
6527 else
6528 return pdoc->LinesTotal();
6530 case SCI_GETMODIFY:
6531 return !pdoc->IsSavePoint();
6533 case SCI_SETSEL: {
6534 int nStart = static_cast<int>(wParam);
6535 int nEnd = static_cast<int>(lParam);
6536 if (nEnd < 0)
6537 nEnd = pdoc->Length();
6538 if (nStart < 0)
6539 nStart = nEnd; // Remove selection
6540 InvalidateSelection(SelectionRange(nStart, nEnd));
6541 sel.Clear();
6542 sel.selType = Selection::selStream;
6543 SetSelection(nEnd, nStart);
6544 EnsureCaretVisible();
6546 break;
6548 case SCI_GETSELTEXT: {
6549 SelectionText selectedText;
6550 CopySelectionRange(&selectedText);
6551 if (lParam == 0) {
6552 return selectedText.len ? selectedText.len : 1;
6553 } else {
6554 char *ptr = CharPtrFromSPtr(lParam);
6555 int iChar = 0;
6556 if (selectedText.len) {
6557 for (; iChar < selectedText.len; iChar++)
6558 ptr[iChar] = selectedText.s[iChar];
6559 } else {
6560 ptr[0] = '\0';
6562 return iChar;
6566 case SCI_LINEFROMPOSITION:
6567 if (static_cast<int>(wParam) < 0)
6568 return 0;
6569 return pdoc->LineFromPosition(wParam);
6571 case SCI_POSITIONFROMLINE:
6572 if (static_cast<int>(wParam) < 0)
6573 wParam = pdoc->LineFromPosition(SelectionStart().Position());
6574 if (wParam == 0)
6575 return 0; // Even if there is no text, there is a first line that starts at 0
6576 if (static_cast<int>(wParam) > pdoc->LinesTotal())
6577 return -1;
6578 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
6579 // return -1;
6580 return pdoc->LineStart(wParam);
6582 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
6583 case SCI_LINELENGTH:
6584 if ((static_cast<int>(wParam) < 0) ||
6585 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
6586 return 0;
6587 return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
6589 case SCI_REPLACESEL: {
6590 if (lParam == 0)
6591 return 0;
6592 UndoGroup ug(pdoc);
6593 ClearSelection();
6594 char *replacement = CharPtrFromSPtr(lParam);
6595 pdoc->InsertCString(sel.MainCaret(), replacement);
6596 SetEmptySelection(sel.MainCaret() + istrlen(replacement));
6597 EnsureCaretVisible();
6599 break;
6601 case SCI_SETTARGETSTART:
6602 targetStart = wParam;
6603 break;
6605 case SCI_GETTARGETSTART:
6606 return targetStart;
6608 case SCI_SETTARGETEND:
6609 targetEnd = wParam;
6610 break;
6612 case SCI_GETTARGETEND:
6613 return targetEnd;
6615 case SCI_TARGETFROMSELECTION:
6616 if (sel.MainCaret() < sel.MainAnchor()) {
6617 targetStart = sel.MainCaret();
6618 targetEnd = sel.MainAnchor();
6619 } else {
6620 targetStart = sel.MainAnchor();
6621 targetEnd = sel.MainCaret();
6623 break;
6625 case SCI_REPLACETARGET:
6626 PLATFORM_ASSERT(lParam);
6627 return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam);
6629 case SCI_REPLACETARGETRE:
6630 PLATFORM_ASSERT(lParam);
6631 return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam);
6633 case SCI_SEARCHINTARGET:
6634 PLATFORM_ASSERT(lParam);
6635 return SearchInTarget(CharPtrFromSPtr(lParam), wParam);
6637 case SCI_SETSEARCHFLAGS:
6638 searchFlags = wParam;
6639 break;
6641 case SCI_GETSEARCHFLAGS:
6642 return searchFlags;
6644 case SCI_POSITIONBEFORE:
6645 return pdoc->MovePositionOutsideChar(wParam - 1, -1, true);
6647 case SCI_POSITIONAFTER:
6648 return pdoc->MovePositionOutsideChar(wParam + 1, 1, true);
6650 case SCI_LINESCROLL:
6651 ScrollTo(topLine + lParam);
6652 HorizontalScrollTo(xOffset + wParam * vs.spaceWidth);
6653 return 1;
6655 case SCI_SETXOFFSET:
6656 xOffset = wParam;
6657 SetHorizontalScrollPos();
6658 Redraw();
6659 break;
6661 case SCI_GETXOFFSET:
6662 return xOffset;
6664 case SCI_CHOOSECARETX:
6665 SetLastXChosen();
6666 break;
6668 case SCI_SCROLLCARET:
6669 EnsureCaretVisible();
6670 break;
6672 case SCI_SETREADONLY:
6673 pdoc->SetReadOnly(wParam != 0);
6674 return 1;
6676 case SCI_GETREADONLY:
6677 return pdoc->IsReadOnly();
6679 case SCI_CANPASTE:
6680 return CanPaste();
6682 case SCI_POINTXFROMPOSITION:
6683 if (lParam < 0) {
6684 return 0;
6685 } else {
6686 Point pt = LocationFromPosition(lParam);
6687 return pt.x;
6690 case SCI_POINTYFROMPOSITION:
6691 if (lParam < 0) {
6692 return 0;
6693 } else {
6694 Point pt = LocationFromPosition(lParam);
6695 return pt.y;
6698 case SCI_FINDTEXT:
6699 return FindText(wParam, lParam);
6701 case SCI_GETTEXTRANGE: {
6702 if (lParam == 0)
6703 return 0;
6704 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
6705 int cpMax = tr->chrg.cpMax;
6706 if (cpMax == -1)
6707 cpMax = pdoc->Length();
6708 PLATFORM_ASSERT(cpMax <= pdoc->Length());
6709 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
6710 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
6711 // Spec says copied text is terminated with a NUL
6712 tr->lpstrText[len] = '\0';
6713 return len; // Not including NUL
6716 case SCI_HIDESELECTION:
6717 hideSelection = wParam != 0;
6718 Redraw();
6719 break;
6721 case SCI_FORMATRANGE:
6722 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
6724 case SCI_GETMARGINLEFT:
6725 return vs.leftMarginWidth;
6727 case SCI_GETMARGINRIGHT:
6728 return vs.rightMarginWidth;
6730 case SCI_SETMARGINLEFT:
6731 vs.leftMarginWidth = lParam;
6732 InvalidateStyleRedraw();
6733 break;
6735 case SCI_SETMARGINRIGHT:
6736 vs.rightMarginWidth = lParam;
6737 InvalidateStyleRedraw();
6738 break;
6740 // Control specific mesages
6742 case SCI_ADDTEXT: {
6743 if (lParam == 0)
6744 return 0;
6745 pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam);
6746 SetEmptySelection(sel.MainCaret() + wParam);
6747 return 0;
6750 case SCI_ADDSTYLEDTEXT:
6751 if (lParam)
6752 AddStyledText(CharPtrFromSPtr(lParam), wParam);
6753 return 0;
6755 case SCI_INSERTTEXT: {
6756 if (lParam == 0)
6757 return 0;
6758 int insertPos = wParam;
6759 if (static_cast<int>(wParam) == -1)
6760 insertPos = CurrentPosition();
6761 int newCurrent = CurrentPosition();
6762 char *sz = CharPtrFromSPtr(lParam);
6763 pdoc->InsertCString(insertPos, sz);
6764 if (newCurrent > insertPos)
6765 newCurrent += istrlen(sz);
6766 SetEmptySelection(newCurrent);
6767 return 0;
6770 case SCI_APPENDTEXT:
6771 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam);
6772 return 0;
6774 case SCI_CLEARALL:
6775 ClearAll();
6776 return 0;
6778 case SCI_CLEARDOCUMENTSTYLE:
6779 ClearDocumentStyle();
6780 return 0;
6782 case SCI_SETUNDOCOLLECTION:
6783 pdoc->SetUndoCollection(wParam != 0);
6784 return 0;
6786 case SCI_GETUNDOCOLLECTION:
6787 return pdoc->IsCollectingUndo();
6789 case SCI_BEGINUNDOACTION:
6790 pdoc->BeginUndoAction();
6791 return 0;
6793 case SCI_ENDUNDOACTION:
6794 pdoc->EndUndoAction();
6795 return 0;
6797 case SCI_GETCARETPERIOD:
6798 return caret.period;
6800 case SCI_SETCARETPERIOD:
6801 caret.period = wParam;
6802 break;
6804 case SCI_SETWORDCHARS: {
6805 pdoc->SetDefaultCharClasses(false);
6806 if (lParam == 0)
6807 return 0;
6808 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
6810 break;
6812 case SCI_SETWHITESPACECHARS: {
6813 if (lParam == 0)
6814 return 0;
6815 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
6817 break;
6819 case SCI_SETCHARSDEFAULT:
6820 pdoc->SetDefaultCharClasses(true);
6821 break;
6823 case SCI_GETLENGTH:
6824 return pdoc->Length();
6826 case SCI_ALLOCATE:
6827 pdoc->Allocate(wParam);
6828 break;
6830 case SCI_GETCHARAT:
6831 return pdoc->CharAt(wParam);
6833 case SCI_SETCURRENTPOS:
6834 if (sel.IsRectangular()) {
6835 sel.Rectangular().caret.SetPosition(wParam);
6836 SetRectangularRange();
6837 Redraw();
6838 } else {
6839 SetSelection(wParam, sel.MainAnchor());
6841 break;
6843 case SCI_GETCURRENTPOS:
6844 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
6846 case SCI_SETANCHOR:
6847 if (sel.IsRectangular()) {
6848 sel.Rectangular().anchor.SetPosition(wParam);
6849 SetRectangularRange();
6850 Redraw();
6851 } else {
6852 SetSelection(sel.MainCaret(), wParam);
6854 break;
6856 case SCI_GETANCHOR:
6857 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
6859 case SCI_SETSELECTIONSTART:
6860 SetSelection(Platform::Maximum(sel.MainCaret(), wParam), wParam);
6861 break;
6863 case SCI_GETSELECTIONSTART:
6864 return sel.LimitsForRectangularElseMain().start.Position();
6866 case SCI_SETSELECTIONEND:
6867 SetSelection(wParam, Platform::Minimum(sel.MainAnchor(), wParam));
6868 break;
6870 case SCI_GETSELECTIONEND:
6871 return sel.LimitsForRectangularElseMain().end.Position();
6873 case SCI_SETPRINTMAGNIFICATION:
6874 printMagnification = wParam;
6875 break;
6877 case SCI_GETPRINTMAGNIFICATION:
6878 return printMagnification;
6880 case SCI_SETPRINTCOLOURMODE:
6881 printColourMode = wParam;
6882 break;
6884 case SCI_GETPRINTCOLOURMODE:
6885 return printColourMode;
6887 case SCI_SETPRINTWRAPMODE:
6888 printWrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
6889 break;
6891 case SCI_GETPRINTWRAPMODE:
6892 return printWrapState;
6894 case SCI_GETSTYLEAT:
6895 if (static_cast<int>(wParam) >= pdoc->Length())
6896 return 0;
6897 else
6898 return pdoc->StyleAt(wParam);
6900 case SCI_REDO:
6901 Redo();
6902 break;
6904 case SCI_SELECTALL:
6905 SelectAll();
6906 break;
6908 case SCI_SETSAVEPOINT:
6909 pdoc->SetSavePoint();
6910 break;
6912 case SCI_GETSTYLEDTEXT: {
6913 if (lParam == 0)
6914 return 0;
6915 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
6916 int iPlace = 0;
6917 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
6918 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
6919 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
6921 tr->lpstrText[iPlace] = '\0';
6922 tr->lpstrText[iPlace + 1] = '\0';
6923 return iPlace;
6926 case SCI_CANREDO:
6927 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
6929 case SCI_MARKERLINEFROMHANDLE:
6930 return pdoc->LineFromHandle(wParam);
6932 case SCI_MARKERDELETEHANDLE:
6933 pdoc->DeleteMarkFromHandle(wParam);
6934 break;
6936 case SCI_GETVIEWWS:
6937 return vs.viewWhitespace;
6939 case SCI_SETVIEWWS:
6940 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
6941 Redraw();
6942 break;
6944 case SCI_GETWHITESPACESIZE:
6945 return vs.whitespaceSize;
6947 case SCI_SETWHITESPACESIZE:
6948 vs.whitespaceSize = static_cast<int>(wParam);
6949 Redraw();
6950 break;
6952 case SCI_POSITIONFROMPOINT:
6953 return PositionFromLocation(Point(wParam, lParam), false, false);
6955 case SCI_POSITIONFROMPOINTCLOSE:
6956 return PositionFromLocation(Point(wParam, lParam), true, false);
6958 case SCI_CHARPOSITIONFROMPOINT:
6959 return PositionFromLocation(Point(wParam, lParam), false, true);
6961 case SCI_CHARPOSITIONFROMPOINTCLOSE:
6962 return PositionFromLocation(Point(wParam, lParam), true, true);
6964 case SCI_GOTOLINE:
6965 GoToLine(wParam);
6966 break;
6968 case SCI_GOTOPOS:
6969 SetEmptySelection(wParam);
6970 EnsureCaretVisible();
6971 Redraw();
6972 break;
6974 case SCI_GETCURLINE: {
6975 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
6976 int lineStart = pdoc->LineStart(lineCurrentPos);
6977 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
6978 if (lParam == 0) {
6979 return 1 + lineEnd - lineStart;
6981 PLATFORM_ASSERT(wParam > 0);
6982 char *ptr = CharPtrFromSPtr(lParam);
6983 unsigned int iPlace = 0;
6984 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
6985 ptr[iPlace++] = pdoc->CharAt(iChar);
6987 ptr[iPlace] = '\0';
6988 return sel.MainCaret() - lineStart;
6991 case SCI_GETENDSTYLED:
6992 return pdoc->GetEndStyled();
6994 case SCI_GETEOLMODE:
6995 return pdoc->eolMode;
6997 case SCI_SETEOLMODE:
6998 pdoc->eolMode = wParam;
6999 break;
7001 case SCI_STARTSTYLING:
7002 pdoc->StartStyling(wParam, static_cast<char>(lParam));
7003 break;
7005 case SCI_SETSTYLING:
7006 pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
7007 break;
7009 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
7010 if (lParam == 0)
7011 return 0;
7012 pdoc->SetStyles(wParam, CharPtrFromSPtr(lParam));
7013 break;
7015 case SCI_SETBUFFEREDDRAW:
7016 bufferedDraw = wParam != 0;
7017 break;
7019 case SCI_GETBUFFEREDDRAW:
7020 return bufferedDraw;
7022 case SCI_GETTWOPHASEDRAW:
7023 return twoPhaseDraw;
7025 case SCI_SETTWOPHASEDRAW:
7026 twoPhaseDraw = wParam != 0;
7027 InvalidateStyleRedraw();
7028 break;
7030 case SCI_SETFONTQUALITY:
7031 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
7032 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
7033 InvalidateStyleRedraw();
7034 break;
7036 case SCI_GETFONTQUALITY:
7037 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
7039 case SCI_SETTABWIDTH:
7040 if (wParam > 0) {
7041 pdoc->tabInChars = wParam;
7042 if (pdoc->indentInChars == 0)
7043 pdoc->actualIndentInChars = pdoc->tabInChars;
7045 InvalidateStyleRedraw();
7046 break;
7048 case SCI_GETTABWIDTH:
7049 return pdoc->tabInChars;
7051 case SCI_SETINDENT:
7052 pdoc->indentInChars = wParam;
7053 if (pdoc->indentInChars != 0)
7054 pdoc->actualIndentInChars = pdoc->indentInChars;
7055 else
7056 pdoc->actualIndentInChars = pdoc->tabInChars;
7057 InvalidateStyleRedraw();
7058 break;
7060 case SCI_GETINDENT:
7061 return pdoc->indentInChars;
7063 case SCI_SETUSETABS:
7064 pdoc->useTabs = wParam != 0;
7065 InvalidateStyleRedraw();
7066 break;
7068 case SCI_GETUSETABS:
7069 return pdoc->useTabs;
7071 case SCI_SETLINEINDENTATION:
7072 pdoc->SetLineIndentation(wParam, lParam);
7073 break;
7075 case SCI_GETLINEINDENTATION:
7076 return pdoc->GetLineIndentation(wParam);
7078 case SCI_GETLINEINDENTPOSITION:
7079 return pdoc->GetLineIndentPosition(wParam);
7081 case SCI_SETTABINDENTS:
7082 pdoc->tabIndents = wParam != 0;
7083 break;
7085 case SCI_GETTABINDENTS:
7086 return pdoc->tabIndents;
7088 case SCI_SETBACKSPACEUNINDENTS:
7089 pdoc->backspaceUnindents = wParam != 0;
7090 break;
7092 case SCI_GETBACKSPACEUNINDENTS:
7093 return pdoc->backspaceUnindents;
7095 case SCI_SETMOUSEDWELLTIME:
7096 dwellDelay = wParam;
7097 ticksToDwell = dwellDelay;
7098 break;
7100 case SCI_GETMOUSEDWELLTIME:
7101 return dwellDelay;
7103 case SCI_WORDSTARTPOSITION:
7104 return pdoc->ExtendWordSelect(wParam, -1, lParam != 0);
7106 case SCI_WORDENDPOSITION:
7107 return pdoc->ExtendWordSelect(wParam, 1, lParam != 0);
7109 case SCI_SETWRAPMODE:
7110 switch (wParam) {
7111 case SC_WRAP_WORD:
7112 wrapState = eWrapWord;
7113 break;
7114 case SC_WRAP_CHAR:
7115 wrapState = eWrapChar;
7116 break;
7117 default:
7118 wrapState = eWrapNone;
7119 break;
7121 xOffset = 0;
7122 InvalidateStyleRedraw();
7123 ReconfigureScrollBars();
7124 break;
7126 case SCI_GETWRAPMODE:
7127 return wrapState;
7129 case SCI_SETWRAPVISUALFLAGS:
7130 wrapVisualFlags = wParam;
7131 InvalidateStyleRedraw();
7132 ReconfigureScrollBars();
7133 break;
7135 case SCI_GETWRAPVISUALFLAGS:
7136 return wrapVisualFlags;
7138 case SCI_SETWRAPVISUALFLAGSLOCATION:
7139 wrapVisualFlagsLocation = wParam;
7140 InvalidateStyleRedraw();
7141 break;
7143 case SCI_GETWRAPVISUALFLAGSLOCATION:
7144 return wrapVisualFlagsLocation;
7146 case SCI_SETWRAPSTARTINDENT:
7147 wrapVisualStartIndent = wParam;
7148 InvalidateStyleRedraw();
7149 ReconfigureScrollBars();
7150 break;
7152 case SCI_GETWRAPSTARTINDENT:
7153 return wrapVisualStartIndent;
7155 case SCI_SETWRAPINDENTMODE:
7156 wrapIndentMode = wParam;
7157 InvalidateStyleRedraw();
7158 ReconfigureScrollBars();
7159 break;
7161 case SCI_GETWRAPINDENTMODE:
7162 return wrapIndentMode;
7164 case SCI_SETLAYOUTCACHE:
7165 llc.SetLevel(wParam);
7166 break;
7168 case SCI_GETLAYOUTCACHE:
7169 return llc.GetLevel();
7171 case SCI_SETPOSITIONCACHE:
7172 posCache.SetSize(wParam);
7173 break;
7175 case SCI_GETPOSITIONCACHE:
7176 return posCache.GetSize();
7178 case SCI_SETSCROLLWIDTH:
7179 PLATFORM_ASSERT(wParam > 0);
7180 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
7181 lineWidthMaxSeen = 0;
7182 scrollWidth = wParam;
7183 SetScrollBars();
7185 break;
7187 case SCI_GETSCROLLWIDTH:
7188 return scrollWidth;
7190 case SCI_SETSCROLLWIDTHTRACKING:
7191 trackLineWidth = wParam != 0;
7192 break;
7194 case SCI_GETSCROLLWIDTHTRACKING:
7195 return trackLineWidth;
7197 case SCI_LINESJOIN:
7198 LinesJoin();
7199 break;
7201 case SCI_LINESSPLIT:
7202 LinesSplit(wParam);
7203 break;
7205 case SCI_TEXTWIDTH:
7206 PLATFORM_ASSERT(wParam < vs.stylesSize);
7207 PLATFORM_ASSERT(lParam);
7208 return TextWidth(wParam, CharPtrFromSPtr(lParam));
7210 case SCI_TEXTHEIGHT:
7211 return vs.lineHeight;
7213 case SCI_SETENDATLASTLINE:
7214 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
7215 if (endAtLastLine != (wParam != 0)) {
7216 endAtLastLine = wParam != 0;
7217 SetScrollBars();
7219 break;
7221 case SCI_GETENDATLASTLINE:
7222 return endAtLastLine;
7224 case SCI_SETCARETSTICKY:
7225 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
7226 if (caretSticky != (wParam != 0)) {
7227 caretSticky = wParam != 0;
7229 break;
7231 case SCI_GETCARETSTICKY:
7232 return caretSticky;
7234 case SCI_TOGGLECARETSTICKY:
7235 caretSticky = !caretSticky;
7236 break;
7238 case SCI_GETCOLUMN:
7239 return pdoc->GetColumn(wParam);
7241 case SCI_FINDCOLUMN:
7242 return pdoc->FindColumn(wParam, lParam);
7244 case SCI_SETHSCROLLBAR :
7245 if (horizontalScrollBarVisible != (wParam != 0)) {
7246 horizontalScrollBarVisible = wParam != 0;
7247 SetScrollBars();
7248 ReconfigureScrollBars();
7250 break;
7252 case SCI_GETHSCROLLBAR:
7253 return horizontalScrollBarVisible;
7255 case SCI_SETVSCROLLBAR:
7256 if (verticalScrollBarVisible != (wParam != 0)) {
7257 verticalScrollBarVisible = wParam != 0;
7258 SetScrollBars();
7259 ReconfigureScrollBars();
7261 break;
7263 case SCI_GETVSCROLLBAR:
7264 return verticalScrollBarVisible;
7266 case SCI_SETINDENTATIONGUIDES:
7267 vs.viewIndentationGuides = IndentView(wParam);
7268 Redraw();
7269 break;
7271 case SCI_GETINDENTATIONGUIDES:
7272 return vs.viewIndentationGuides;
7274 case SCI_SETHIGHLIGHTGUIDE:
7275 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
7276 highlightGuideColumn = wParam;
7277 Redraw();
7279 break;
7281 case SCI_GETHIGHLIGHTGUIDE:
7282 return highlightGuideColumn;
7284 case SCI_GETLINEENDPOSITION:
7285 return pdoc->LineEnd(wParam);
7287 case SCI_SETCODEPAGE:
7288 if (ValidCodePage(wParam)) {
7289 pdoc->dbcsCodePage = wParam;
7290 InvalidateStyleRedraw();
7292 break;
7294 case SCI_GETCODEPAGE:
7295 return pdoc->dbcsCodePage;
7297 case SCI_SETUSEPALETTE:
7298 palette.allowRealization = wParam != 0;
7299 InvalidateStyleRedraw();
7300 break;
7302 case SCI_GETUSEPALETTE:
7303 return palette.allowRealization;
7305 // Marker definition and setting
7306 case SCI_MARKERDEFINE:
7307 if (wParam <= MARKER_MAX)
7308 vs.markers[wParam].markType = lParam;
7309 InvalidateStyleData();
7310 RedrawSelMargin();
7311 break;
7313 case SCI_MARKERSYMBOLDEFINED:
7314 if (wParam <= MARKER_MAX)
7315 return vs.markers[wParam].markType;
7316 else
7317 return 0;
7319 case SCI_MARKERSETFORE:
7320 if (wParam <= MARKER_MAX)
7321 vs.markers[wParam].fore.desired = ColourDesired(lParam);
7322 InvalidateStyleData();
7323 RedrawSelMargin();
7324 break;
7325 case SCI_MARKERSETBACK:
7326 if (wParam <= MARKER_MAX)
7327 vs.markers[wParam].back.desired = ColourDesired(lParam);
7328 InvalidateStyleData();
7329 RedrawSelMargin();
7330 break;
7331 case SCI_MARKERSETALPHA:
7332 if (wParam <= MARKER_MAX)
7333 vs.markers[wParam].alpha = lParam;
7334 InvalidateStyleRedraw();
7335 break;
7336 case SCI_MARKERADD: {
7337 int markerID = pdoc->AddMark(wParam, lParam);
7338 return markerID;
7340 case SCI_MARKERADDSET:
7341 if (lParam != 0)
7342 pdoc->AddMarkSet(wParam, lParam);
7343 break;
7345 case SCI_MARKERDELETE:
7346 pdoc->DeleteMark(wParam, lParam);
7347 break;
7349 case SCI_MARKERDELETEALL:
7350 pdoc->DeleteAllMarks(static_cast<int>(wParam));
7351 break;
7353 case SCI_MARKERGET:
7354 return pdoc->GetMark(wParam);
7356 case SCI_MARKERNEXT: {
7357 int lt = pdoc->LinesTotal();
7358 for (int iLine = wParam; iLine < lt; iLine++) {
7359 if ((pdoc->GetMark(iLine) & lParam) != 0)
7360 return iLine;
7363 return -1;
7365 case SCI_MARKERPREVIOUS: {
7366 for (int iLine = wParam; iLine >= 0; iLine--) {
7367 if ((pdoc->GetMark(iLine) & lParam) != 0)
7368 return iLine;
7371 return -1;
7373 case SCI_MARKERDEFINEPIXMAP:
7374 if (wParam <= MARKER_MAX) {
7375 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
7377 InvalidateStyleData();
7378 RedrawSelMargin();
7379 break;
7381 case SCI_SETMARGINTYPEN:
7382 if (ValidMargin(wParam)) {
7383 vs.ms[wParam].style = lParam;
7384 InvalidateStyleRedraw();
7386 break;
7388 case SCI_GETMARGINTYPEN:
7389 if (ValidMargin(wParam))
7390 return vs.ms[wParam].style;
7391 else
7392 return 0;
7394 case SCI_SETMARGINWIDTHN:
7395 if (ValidMargin(wParam)) {
7396 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
7397 if (vs.ms[wParam].width != lParam) {
7398 vs.ms[wParam].width = lParam;
7399 InvalidateStyleRedraw();
7402 break;
7404 case SCI_GETMARGINWIDTHN:
7405 if (ValidMargin(wParam))
7406 return vs.ms[wParam].width;
7407 else
7408 return 0;
7410 case SCI_SETMARGINMASKN:
7411 if (ValidMargin(wParam)) {
7412 vs.ms[wParam].mask = lParam;
7413 InvalidateStyleRedraw();
7415 break;
7417 case SCI_GETMARGINMASKN:
7418 if (ValidMargin(wParam))
7419 return vs.ms[wParam].mask;
7420 else
7421 return 0;
7423 case SCI_SETMARGINSENSITIVEN:
7424 if (ValidMargin(wParam)) {
7425 vs.ms[wParam].sensitive = lParam != 0;
7426 InvalidateStyleRedraw();
7428 break;
7430 case SCI_GETMARGINSENSITIVEN:
7431 if (ValidMargin(wParam))
7432 return vs.ms[wParam].sensitive ? 1 : 0;
7433 else
7434 return 0;
7436 case SCI_STYLECLEARALL:
7437 vs.ClearStyles();
7438 InvalidateStyleRedraw();
7439 break;
7441 case SCI_STYLESETFORE:
7442 case SCI_STYLESETBACK:
7443 case SCI_STYLESETBOLD:
7444 case SCI_STYLESETITALIC:
7445 case SCI_STYLESETEOLFILLED:
7446 case SCI_STYLESETSIZE:
7447 case SCI_STYLESETFONT:
7448 case SCI_STYLESETUNDERLINE:
7449 case SCI_STYLESETCASE:
7450 case SCI_STYLESETCHARACTERSET:
7451 case SCI_STYLESETVISIBLE:
7452 case SCI_STYLESETCHANGEABLE:
7453 case SCI_STYLESETHOTSPOT:
7454 StyleSetMessage(iMessage, wParam, lParam);
7455 break;
7457 case SCI_STYLEGETFORE:
7458 case SCI_STYLEGETBACK:
7459 case SCI_STYLEGETBOLD:
7460 case SCI_STYLEGETITALIC:
7461 case SCI_STYLEGETEOLFILLED:
7462 case SCI_STYLEGETSIZE:
7463 case SCI_STYLEGETFONT:
7464 case SCI_STYLEGETUNDERLINE:
7465 case SCI_STYLEGETCASE:
7466 case SCI_STYLEGETCHARACTERSET:
7467 case SCI_STYLEGETVISIBLE:
7468 case SCI_STYLEGETCHANGEABLE:
7469 case SCI_STYLEGETHOTSPOT:
7470 return StyleGetMessage(iMessage, wParam, lParam);
7472 case SCI_STYLERESETDEFAULT:
7473 vs.ResetDefaultStyle();
7474 InvalidateStyleRedraw();
7475 break;
7476 case SCI_SETSTYLEBITS:
7477 vs.EnsureStyle((1 << wParam) - 1);
7478 pdoc->SetStylingBits(wParam);
7479 break;
7481 case SCI_GETSTYLEBITS:
7482 return pdoc->stylingBits;
7484 case SCI_SETLINESTATE:
7485 return pdoc->SetLineState(wParam, lParam);
7487 case SCI_GETLINESTATE:
7488 return pdoc->GetLineState(wParam);
7490 case SCI_GETMAXLINESTATE:
7491 return pdoc->GetMaxLineState();
7493 case SCI_GETCARETLINEVISIBLE:
7494 return vs.showCaretLineBackground;
7495 case SCI_SETCARETLINEVISIBLE:
7496 vs.showCaretLineBackground = wParam != 0;
7497 InvalidateStyleRedraw();
7498 break;
7499 case SCI_GETCARETLINEBACK:
7500 return vs.caretLineBackground.desired.AsLong();
7501 case SCI_SETCARETLINEBACK:
7502 vs.caretLineBackground.desired = wParam;
7503 InvalidateStyleRedraw();
7504 break;
7505 case SCI_GETCARETLINEBACKALPHA:
7506 return vs.caretLineAlpha;
7507 case SCI_SETCARETLINEBACKALPHA:
7508 vs.caretLineAlpha = wParam;
7509 InvalidateStyleRedraw();
7510 break;
7512 // Folding messages
7514 case SCI_VISIBLEFROMDOCLINE:
7515 return cs.DisplayFromDoc(wParam);
7517 case SCI_DOCLINEFROMVISIBLE:
7518 return cs.DocFromDisplay(wParam);
7520 case SCI_WRAPCOUNT:
7521 return WrapCount(wParam);
7523 case SCI_SETFOLDLEVEL: {
7524 int prev = pdoc->SetLevel(wParam, lParam);
7525 if (prev != lParam)
7526 RedrawSelMargin();
7527 return prev;
7530 case SCI_GETFOLDLEVEL:
7531 return pdoc->GetLevel(wParam);
7533 case SCI_GETLASTCHILD:
7534 return pdoc->GetLastChild(wParam, lParam);
7536 case SCI_GETFOLDPARENT:
7537 return pdoc->GetFoldParent(wParam);
7539 case SCI_SHOWLINES:
7540 cs.SetVisible(wParam, lParam, true);
7541 SetScrollBars();
7542 Redraw();
7543 break;
7545 case SCI_HIDELINES:
7546 if (wParam > 0)
7547 cs.SetVisible(wParam, lParam, false);
7548 SetScrollBars();
7549 Redraw();
7550 break;
7552 case SCI_GETLINEVISIBLE:
7553 return cs.GetVisible(wParam);
7555 case SCI_SETFOLDEXPANDED:
7556 if (cs.SetExpanded(wParam, lParam != 0)) {
7557 RedrawSelMargin();
7559 break;
7561 case SCI_GETFOLDEXPANDED:
7562 return cs.GetExpanded(wParam);
7564 case SCI_SETFOLDFLAGS:
7565 foldFlags = wParam;
7566 Redraw();
7567 break;
7569 case SCI_TOGGLEFOLD:
7570 ToggleContraction(wParam);
7571 break;
7573 case SCI_ENSUREVISIBLE:
7574 EnsureLineVisible(wParam, false);
7575 break;
7577 case SCI_ENSUREVISIBLEENFORCEPOLICY:
7578 EnsureLineVisible(wParam, true);
7579 break;
7581 case SCI_SEARCHANCHOR:
7582 SearchAnchor();
7583 break;
7585 case SCI_SEARCHNEXT:
7586 case SCI_SEARCHPREV:
7587 return SearchText(iMessage, wParam, lParam);
7589 case SCI_SETXCARETPOLICY:
7590 caretXPolicy = wParam;
7591 caretXSlop = lParam;
7592 break;
7594 case SCI_SETYCARETPOLICY:
7595 caretYPolicy = wParam;
7596 caretYSlop = lParam;
7597 break;
7599 case SCI_SETVISIBLEPOLICY:
7600 visiblePolicy = wParam;
7601 visibleSlop = lParam;
7602 break;
7604 case SCI_LINESONSCREEN:
7605 return LinesOnScreen();
7607 case SCI_SETSELFORE:
7608 vs.selforeset = wParam != 0;
7609 vs.selforeground.desired = ColourDesired(lParam);
7610 vs.selAdditionalForeground.desired = ColourDesired(lParam);
7611 InvalidateStyleRedraw();
7612 break;
7614 case SCI_SETSELBACK:
7615 vs.selbackset = wParam != 0;
7616 vs.selbackground.desired = ColourDesired(lParam);
7617 vs.selAdditionalBackground.desired = ColourDesired(lParam);
7618 InvalidateStyleRedraw();
7619 break;
7621 case SCI_SETSELALPHA:
7622 vs.selAlpha = wParam;
7623 vs.selAdditionalAlpha = wParam;
7624 InvalidateStyleRedraw();
7625 break;
7627 case SCI_GETSELALPHA:
7628 return vs.selAlpha;
7630 case SCI_GETSELEOLFILLED:
7631 return vs.selEOLFilled;
7633 case SCI_SETSELEOLFILLED:
7634 vs.selEOLFilled = wParam != 0;
7635 InvalidateStyleRedraw();
7636 break;
7638 case SCI_SETWHITESPACEFORE:
7639 vs.whitespaceForegroundSet = wParam != 0;
7640 vs.whitespaceForeground.desired = ColourDesired(lParam);
7641 InvalidateStyleRedraw();
7642 break;
7644 case SCI_SETWHITESPACEBACK:
7645 vs.whitespaceBackgroundSet = wParam != 0;
7646 vs.whitespaceBackground.desired = ColourDesired(lParam);
7647 InvalidateStyleRedraw();
7648 break;
7650 case SCI_SETCARETFORE:
7651 vs.caretcolour.desired = ColourDesired(wParam);
7652 InvalidateStyleRedraw();
7653 break;
7655 case SCI_GETCARETFORE:
7656 return vs.caretcolour.desired.AsLong();
7658 case SCI_SETCARETSTYLE:
7659 if (wParam >= CARETSTYLE_INVISIBLE && wParam <= CARETSTYLE_BLOCK)
7660 vs.caretStyle = wParam;
7661 else
7662 /* Default to the line caret */
7663 vs.caretStyle = CARETSTYLE_LINE;
7664 InvalidateStyleRedraw();
7665 break;
7667 case SCI_GETCARETSTYLE:
7668 return vs.caretStyle;
7670 case SCI_SETCARETWIDTH:
7671 if (wParam <= 0)
7672 vs.caretWidth = 0;
7673 else if (wParam >= 3)
7674 vs.caretWidth = 3;
7675 else
7676 vs.caretWidth = wParam;
7677 InvalidateStyleRedraw();
7678 break;
7680 case SCI_GETCARETWIDTH:
7681 return vs.caretWidth;
7683 case SCI_ASSIGNCMDKEY:
7684 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
7685 Platform::HighShortFromLong(wParam), lParam);
7686 break;
7688 case SCI_CLEARCMDKEY:
7689 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
7690 Platform::HighShortFromLong(wParam), SCI_NULL);
7691 break;
7693 case SCI_CLEARALLCMDKEYS:
7694 kmap.Clear();
7695 break;
7697 case SCI_INDICSETSTYLE:
7698 if (wParam <= INDIC_MAX) {
7699 vs.indicators[wParam].style = lParam;
7700 InvalidateStyleRedraw();
7702 break;
7704 case SCI_INDICGETSTYLE:
7705 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
7707 case SCI_INDICSETFORE:
7708 if (wParam <= INDIC_MAX) {
7709 vs.indicators[wParam].fore.desired = ColourDesired(lParam);
7710 InvalidateStyleRedraw();
7712 break;
7714 case SCI_INDICGETFORE:
7715 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.desired.AsLong() : 0;
7717 case SCI_INDICSETUNDER:
7718 if (wParam <= INDIC_MAX) {
7719 vs.indicators[wParam].under = lParam != 0;
7720 InvalidateStyleRedraw();
7722 break;
7724 case SCI_INDICGETUNDER:
7725 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
7727 case SCI_INDICSETALPHA:
7728 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 100) {
7729 vs.indicators[wParam].fillAlpha = lParam;
7730 InvalidateStyleRedraw();
7732 break;
7734 case SCI_INDICGETALPHA:
7735 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
7737 case SCI_SETINDICATORCURRENT:
7738 pdoc->decorations.SetCurrentIndicator(wParam);
7739 break;
7740 case SCI_GETINDICATORCURRENT:
7741 return pdoc->decorations.GetCurrentIndicator();
7742 case SCI_SETINDICATORVALUE:
7743 pdoc->decorations.SetCurrentValue(wParam);
7744 break;
7745 case SCI_GETINDICATORVALUE:
7746 return pdoc->decorations.GetCurrentValue();
7748 case SCI_INDICATORFILLRANGE:
7749 pdoc->DecorationFillRange(wParam, pdoc->decorations.GetCurrentValue(), lParam);
7750 break;
7752 case SCI_INDICATORCLEARRANGE:
7753 pdoc->DecorationFillRange(wParam, 0, lParam);
7754 break;
7756 case SCI_INDICATORALLONFOR:
7757 return pdoc->decorations.AllOnFor(wParam);
7759 case SCI_INDICATORVALUEAT:
7760 return pdoc->decorations.ValueAt(wParam, lParam);
7762 case SCI_INDICATORSTART:
7763 return pdoc->decorations.Start(wParam, lParam);
7765 case SCI_INDICATOREND:
7766 return pdoc->decorations.End(wParam, lParam);
7768 case SCI_LINEDOWN:
7769 case SCI_LINEDOWNEXTEND:
7770 case SCI_PARADOWN:
7771 case SCI_PARADOWNEXTEND:
7772 case SCI_LINEUP:
7773 case SCI_LINEUPEXTEND:
7774 case SCI_PARAUP:
7775 case SCI_PARAUPEXTEND:
7776 case SCI_CHARLEFT:
7777 case SCI_CHARLEFTEXTEND:
7778 case SCI_CHARRIGHT:
7779 case SCI_CHARRIGHTEXTEND:
7780 case SCI_WORDLEFT:
7781 case SCI_WORDLEFTEXTEND:
7782 case SCI_WORDRIGHT:
7783 case SCI_WORDRIGHTEXTEND:
7784 case SCI_WORDLEFTEND:
7785 case SCI_WORDLEFTENDEXTEND:
7786 case SCI_WORDRIGHTEND:
7787 case SCI_WORDRIGHTENDEXTEND:
7788 case SCI_HOME:
7789 case SCI_HOMEEXTEND:
7790 case SCI_LINEEND:
7791 case SCI_LINEENDEXTEND:
7792 case SCI_HOMEWRAP:
7793 case SCI_HOMEWRAPEXTEND:
7794 case SCI_LINEENDWRAP:
7795 case SCI_LINEENDWRAPEXTEND:
7796 case SCI_DOCUMENTSTART:
7797 case SCI_DOCUMENTSTARTEXTEND:
7798 case SCI_DOCUMENTEND:
7799 case SCI_DOCUMENTENDEXTEND:
7801 case SCI_STUTTEREDPAGEUP:
7802 case SCI_STUTTEREDPAGEUPEXTEND:
7803 case SCI_STUTTEREDPAGEDOWN:
7804 case SCI_STUTTEREDPAGEDOWNEXTEND:
7806 case SCI_PAGEUP:
7807 case SCI_PAGEUPEXTEND:
7808 case SCI_PAGEDOWN:
7809 case SCI_PAGEDOWNEXTEND:
7810 case SCI_EDITTOGGLEOVERTYPE:
7811 case SCI_CANCEL:
7812 case SCI_DELETEBACK:
7813 case SCI_TAB:
7814 case SCI_BACKTAB:
7815 case SCI_NEWLINE:
7816 case SCI_FORMFEED:
7817 case SCI_VCHOME:
7818 case SCI_VCHOMEEXTEND:
7819 case SCI_VCHOMEWRAP:
7820 case SCI_VCHOMEWRAPEXTEND:
7821 case SCI_ZOOMIN:
7822 case SCI_ZOOMOUT:
7823 case SCI_DELWORDLEFT:
7824 case SCI_DELWORDRIGHT:
7825 case SCI_DELWORDRIGHTEND:
7826 case SCI_DELLINELEFT:
7827 case SCI_DELLINERIGHT:
7828 case SCI_LINECOPY:
7829 case SCI_LINECUT:
7830 case SCI_LINEDELETE:
7831 case SCI_LINETRANSPOSE:
7832 case SCI_LINEDUPLICATE:
7833 case SCI_LOWERCASE:
7834 case SCI_UPPERCASE:
7835 case SCI_LINESCROLLDOWN:
7836 case SCI_LINESCROLLUP:
7837 case SCI_WORDPARTLEFT:
7838 case SCI_WORDPARTLEFTEXTEND:
7839 case SCI_WORDPARTRIGHT:
7840 case SCI_WORDPARTRIGHTEXTEND:
7841 case SCI_DELETEBACKNOTLINE:
7842 case SCI_HOMEDISPLAY:
7843 case SCI_HOMEDISPLAYEXTEND:
7844 case SCI_LINEENDDISPLAY:
7845 case SCI_LINEENDDISPLAYEXTEND:
7846 case SCI_LINEDOWNRECTEXTEND:
7847 case SCI_LINEUPRECTEXTEND:
7848 case SCI_CHARLEFTRECTEXTEND:
7849 case SCI_CHARRIGHTRECTEXTEND:
7850 case SCI_HOMERECTEXTEND:
7851 case SCI_VCHOMERECTEXTEND:
7852 case SCI_LINEENDRECTEXTEND:
7853 case SCI_PAGEUPRECTEXTEND:
7854 case SCI_PAGEDOWNRECTEXTEND:
7855 case SCI_SELECTIONDUPLICATE:
7856 return KeyCommand(iMessage);
7858 case SCI_BRACEHIGHLIGHT:
7859 SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
7860 break;
7862 case SCI_BRACEBADLIGHT:
7863 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
7864 break;
7866 case SCI_BRACEMATCH:
7867 // wParam is position of char to find brace for,
7868 // lParam is maximum amount of text to restyle to find it
7869 return pdoc->BraceMatch(wParam, lParam);
7871 case SCI_GETVIEWEOL:
7872 return vs.viewEOL;
7874 case SCI_SETVIEWEOL:
7875 vs.viewEOL = wParam != 0;
7876 InvalidateStyleRedraw();
7877 break;
7879 case SCI_SETZOOM:
7880 vs.zoomLevel = wParam;
7881 InvalidateStyleRedraw();
7882 NotifyZoom();
7883 break;
7885 case SCI_GETZOOM:
7886 return vs.zoomLevel;
7888 case SCI_GETEDGECOLUMN:
7889 return theEdge;
7891 case SCI_SETEDGECOLUMN:
7892 theEdge = wParam;
7893 InvalidateStyleRedraw();
7894 break;
7896 case SCI_GETEDGEMODE:
7897 return vs.edgeState;
7899 case SCI_SETEDGEMODE:
7900 vs.edgeState = wParam;
7901 InvalidateStyleRedraw();
7902 break;
7904 case SCI_GETEDGECOLOUR:
7905 return vs.edgecolour.desired.AsLong();
7907 case SCI_SETEDGECOLOUR:
7908 vs.edgecolour.desired = ColourDesired(wParam);
7909 InvalidateStyleRedraw();
7910 break;
7912 case SCI_GETDOCPOINTER:
7913 return reinterpret_cast<sptr_t>(pdoc);
7915 case SCI_SETDOCPOINTER:
7916 CancelModes();
7917 SetDocPointer(reinterpret_cast<Document *>(lParam));
7918 return 0;
7920 case SCI_CREATEDOCUMENT: {
7921 Document *doc = new Document();
7922 if (doc) {
7923 doc->AddRef();
7925 return reinterpret_cast<sptr_t>(doc);
7928 case SCI_ADDREFDOCUMENT:
7929 (reinterpret_cast<Document *>(lParam))->AddRef();
7930 break;
7932 case SCI_RELEASEDOCUMENT:
7933 (reinterpret_cast<Document *>(lParam))->Release();
7934 break;
7936 case SCI_SETMODEVENTMASK:
7937 modEventMask = wParam;
7938 return 0;
7940 case SCI_GETMODEVENTMASK:
7941 return modEventMask;
7943 case SCI_CONVERTEOLS:
7944 pdoc->ConvertLineEnds(wParam);
7945 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
7946 return 0;
7948 case SCI_SETLENGTHFORENCODE:
7949 lengthForEncode = wParam;
7950 return 0;
7952 case SCI_SELECTIONISRECTANGLE:
7953 return sel.selType == Selection::selRectangle ? 1 : 0;
7955 case SCI_SETSELECTIONMODE: {
7956 switch (wParam) {
7957 case SC_SEL_STREAM:
7958 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7959 sel.selType = Selection::selStream;
7960 break;
7961 case SC_SEL_RECTANGLE:
7962 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
7963 sel.selType = Selection::selRectangle;
7964 break;
7965 case SC_SEL_LINES:
7966 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
7967 sel.selType = Selection::selLines;
7968 break;
7969 case SC_SEL_THIN:
7970 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
7971 sel.selType = Selection::selThin;
7972 break;
7973 default:
7974 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7975 sel.selType = Selection::selStream;
7977 InvalidateSelection(sel.RangeMain(), true);
7979 case SCI_GETSELECTIONMODE:
7980 switch (sel.selType) {
7981 case Selection::selStream:
7982 return SC_SEL_STREAM;
7983 case Selection::selRectangle:
7984 return SC_SEL_RECTANGLE;
7985 case Selection::selLines:
7986 return SC_SEL_LINES;
7987 case Selection::selThin:
7988 return SC_SEL_THIN;
7989 default: // ?!
7990 return SC_SEL_STREAM;
7992 case SCI_GETLINESELSTARTPOSITION:
7993 case SCI_GETLINESELENDPOSITION: {
7994 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(wParam)),
7995 SelectionPosition(pdoc->LineEnd(wParam)));
7996 for (size_t r=0; r<sel.Count(); r++) {
7997 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
7998 if (portion.start.IsValid()) {
7999 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
8002 return INVALID_POSITION;
8005 case SCI_SETOVERTYPE:
8006 inOverstrike = wParam != 0;
8007 break;
8009 case SCI_GETOVERTYPE:
8010 return inOverstrike ? 1 : 0;
8012 case SCI_SETFOCUS:
8013 SetFocusState(wParam != 0);
8014 break;
8016 case SCI_GETFOCUS:
8017 return hasFocus;
8019 case SCI_SETSTATUS:
8020 errorStatus = wParam;
8021 break;
8023 case SCI_GETSTATUS:
8024 return errorStatus;
8026 case SCI_SETMOUSEDOWNCAPTURES:
8027 mouseDownCaptures = wParam != 0;
8028 break;
8030 case SCI_GETMOUSEDOWNCAPTURES:
8031 return mouseDownCaptures;
8033 case SCI_SETCURSOR:
8034 cursorMode = wParam;
8035 DisplayCursor(Window::cursorText);
8036 break;
8038 case SCI_GETCURSOR:
8039 return cursorMode;
8041 case SCI_SETCONTROLCHARSYMBOL:
8042 controlCharSymbol = wParam;
8043 break;
8045 case SCI_GETCONTROLCHARSYMBOL:
8046 return controlCharSymbol;
8048 case SCI_STARTRECORD:
8049 recordingMacro = true;
8050 return 0;
8052 case SCI_STOPRECORD:
8053 recordingMacro = false;
8054 return 0;
8056 case SCI_MOVECARETINSIDEVIEW:
8057 MoveCaretInsideView();
8058 break;
8060 case SCI_SETFOLDMARGINCOLOUR:
8061 vs.foldmarginColourSet = wParam != 0;
8062 vs.foldmarginColour.desired = ColourDesired(lParam);
8063 InvalidateStyleRedraw();
8064 break;
8066 case SCI_SETFOLDMARGINHICOLOUR:
8067 vs.foldmarginHighlightColourSet = wParam != 0;
8068 vs.foldmarginHighlightColour.desired = ColourDesired(lParam);
8069 InvalidateStyleRedraw();
8070 break;
8072 case SCI_SETHOTSPOTACTIVEFORE:
8073 vs.hotspotForegroundSet = wParam != 0;
8074 vs.hotspotForeground.desired = ColourDesired(lParam);
8075 InvalidateStyleRedraw();
8076 break;
8078 case SCI_GETHOTSPOTACTIVEFORE:
8079 return vs.hotspotForeground.desired.AsLong();
8081 case SCI_SETHOTSPOTACTIVEBACK:
8082 vs.hotspotBackgroundSet = wParam != 0;
8083 vs.hotspotBackground.desired = ColourDesired(lParam);
8084 InvalidateStyleRedraw();
8085 break;
8087 case SCI_GETHOTSPOTACTIVEBACK:
8088 return vs.hotspotBackground.desired.AsLong();
8090 case SCI_SETHOTSPOTACTIVEUNDERLINE:
8091 vs.hotspotUnderline = wParam != 0;
8092 InvalidateStyleRedraw();
8093 break;
8095 case SCI_GETHOTSPOTACTIVEUNDERLINE:
8096 return vs.hotspotUnderline ? 1 : 0;
8098 case SCI_SETHOTSPOTSINGLELINE:
8099 vs.hotspotSingleLine = wParam != 0;
8100 InvalidateStyleRedraw();
8101 break;
8103 case SCI_GETHOTSPOTSINGLELINE:
8104 return vs.hotspotSingleLine ? 1 : 0;
8106 case SCI_SETPASTECONVERTENDINGS:
8107 convertPastes = wParam != 0;
8108 break;
8110 case SCI_GETPASTECONVERTENDINGS:
8111 return convertPastes ? 1 : 0;
8113 case SCI_GETCHARACTERPOINTER:
8114 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
8116 case SCI_SETEXTRAASCENT:
8117 vs.extraAscent = wParam;
8118 InvalidateStyleRedraw();
8119 break;
8121 case SCI_GETEXTRAASCENT:
8122 return vs.extraAscent;
8124 case SCI_SETEXTRADESCENT:
8125 vs.extraDescent = wParam;
8126 InvalidateStyleRedraw();
8127 break;
8129 case SCI_GETEXTRADESCENT:
8130 return vs.extraDescent;
8132 case SCI_MARGINSETSTYLEOFFSET:
8133 vs.marginStyleOffset = wParam;
8134 InvalidateStyleRedraw();
8135 break;
8137 case SCI_MARGINGETSTYLEOFFSET:
8138 return vs.marginStyleOffset;
8140 case SCI_MARGINSETTEXT:
8141 pdoc->MarginSetText(wParam, CharPtrFromSPtr(lParam));
8142 break;
8144 case SCI_MARGINGETTEXT: {
8145 const StyledText st = pdoc->MarginStyledText(wParam);
8146 if (lParam) {
8147 if (st.text)
8148 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
8149 else
8150 strcpy(CharPtrFromSPtr(lParam), "");
8152 return st.length;
8155 case SCI_MARGINSETSTYLE:
8156 pdoc->MarginSetStyle(wParam, lParam);
8157 break;
8159 case SCI_MARGINGETSTYLE: {
8160 const StyledText st = pdoc->MarginStyledText(wParam);
8161 return st.style;
8164 case SCI_MARGINSETSTYLES:
8165 pdoc->MarginSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
8166 break;
8168 case SCI_MARGINGETSTYLES: {
8169 const StyledText st = pdoc->MarginStyledText(wParam);
8170 if (lParam) {
8171 if (st.styles)
8172 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
8173 else
8174 strcpy(CharPtrFromSPtr(lParam), "");
8176 return st.styles ? st.length : 0;
8179 case SCI_MARGINTEXTCLEARALL:
8180 pdoc->MarginClearAll();
8181 break;
8183 case SCI_ANNOTATIONSETTEXT:
8184 pdoc->AnnotationSetText(wParam, CharPtrFromSPtr(lParam));
8185 break;
8187 case SCI_ANNOTATIONGETTEXT: {
8188 const StyledText st = pdoc->AnnotationStyledText(wParam);
8189 if (lParam) {
8190 if (st.text)
8191 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
8192 else
8193 strcpy(CharPtrFromSPtr(lParam), "");
8195 return st.length;
8198 case SCI_ANNOTATIONGETSTYLE: {
8199 const StyledText st = pdoc->AnnotationStyledText(wParam);
8200 return st.style;
8203 case SCI_ANNOTATIONSETSTYLE:
8204 pdoc->AnnotationSetStyle(wParam, lParam);
8205 break;
8207 case SCI_ANNOTATIONSETSTYLES:
8208 pdoc->AnnotationSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
8209 break;
8211 case SCI_ANNOTATIONGETSTYLES: {
8212 const StyledText st = pdoc->AnnotationStyledText(wParam);
8213 if (lParam) {
8214 if (st.styles)
8215 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
8216 else
8217 strcpy(CharPtrFromSPtr(lParam), "");
8219 return st.styles ? st.length : 0;
8222 case SCI_ANNOTATIONGETLINES:
8223 return pdoc->AnnotationLines(wParam);
8225 case SCI_ANNOTATIONCLEARALL:
8226 pdoc->AnnotationClearAll();
8227 break;
8229 case SCI_ANNOTATIONSETVISIBLE:
8230 SetAnnotationVisible(wParam);
8231 break;
8233 case SCI_ANNOTATIONGETVISIBLE:
8234 return vs.annotationVisible;
8236 case SCI_ANNOTATIONSETSTYLEOFFSET:
8237 vs.annotationStyleOffset = wParam;
8238 InvalidateStyleRedraw();
8239 break;
8241 case SCI_ANNOTATIONGETSTYLEOFFSET:
8242 return vs.annotationStyleOffset;
8244 case SCI_ADDUNDOACTION:
8245 pdoc->AddUndoAction(wParam, lParam & UNDO_MAY_COALESCE);
8246 break;
8248 case SCI_SETMULTIPLESELECTION:
8249 multipleSelection = wParam != 0;
8250 InvalidateCaret();
8251 break;
8253 case SCI_GETMULTIPLESELECTION:
8254 return multipleSelection;
8256 case SCI_SETADDITIONALSELECTIONTYPING:
8257 additionalSelectionTyping = wParam != 0;
8258 InvalidateCaret();
8259 break;
8261 case SCI_GETADDITIONALSELECTIONTYPING:
8262 return additionalSelectionTyping;
8264 case SCI_SETADDITIONALCARETSBLINK:
8265 additionalCaretsBlink = wParam != 0;
8266 InvalidateCaret();
8267 break;
8269 case SCI_GETADDITIONALCARETSBLINK:
8270 return additionalCaretsBlink;
8272 case SCI_SETADDITIONALCARETSVISIBLE:
8273 additionalCaretsVisible = wParam != 0;
8274 InvalidateCaret();
8275 break;
8277 case SCI_GETADDITIONALCARETSVISIBLE:
8278 return additionalCaretsVisible;
8280 case SCI_GETSELECTIONS:
8281 return sel.Count();
8283 case SCI_CLEARSELECTIONS:
8284 sel.Clear();
8285 Redraw();
8286 break;
8288 case SCI_SETSELECTION:
8289 sel.SetSelection(SelectionRange(wParam, lParam));
8290 Redraw();
8291 break;
8293 case SCI_ADDSELECTION:
8294 sel.AddSelection(SelectionRange(wParam, lParam));
8295 Redraw();
8296 break;
8298 case SCI_SETMAINSELECTION:
8299 sel.SetMain(wParam);
8300 Redraw();
8301 break;
8303 case SCI_GETMAINSELECTION:
8304 return sel.Main();
8306 case SCI_SETSELECTIONNCARET:
8307 sel.Range(wParam).caret.SetPosition(lParam);
8308 Redraw();
8309 break;
8311 case SCI_GETSELECTIONNCARET:
8312 return sel.Range(wParam).caret.Position();
8314 case SCI_SETSELECTIONNANCHOR:
8315 sel.Range(wParam).anchor.SetPosition(lParam);
8316 Redraw();
8317 break;
8318 case SCI_GETSELECTIONNANCHOR:
8319 return sel.Range(wParam).anchor.Position();
8321 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
8322 sel.Range(wParam).caret.SetVirtualSpace(lParam);
8323 Redraw();
8324 break;
8326 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
8327 return sel.Range(wParam).caret.VirtualSpace();
8329 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
8330 sel.Range(wParam).anchor.SetVirtualSpace(lParam);
8331 Redraw();
8332 break;
8334 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
8335 return sel.Range(wParam).anchor.VirtualSpace();
8337 case SCI_SETSELECTIONNSTART:
8338 sel.Range(wParam).anchor.SetPosition(lParam);
8339 Redraw();
8340 break;
8342 case SCI_GETSELECTIONNSTART:
8343 return sel.Range(wParam).Start().Position();
8345 case SCI_SETSELECTIONNEND:
8346 sel.Range(wParam).caret.SetPosition(lParam);
8347 Redraw();
8348 break;
8350 case SCI_GETSELECTIONNEND:
8351 return sel.Range(wParam).End().Position();
8353 case SCI_SETRECTANGULARSELECTIONCARET:
8354 if (!sel.IsRectangular())
8355 sel.Clear();
8356 sel.selType = Selection::selRectangle;
8357 sel.Rectangular().caret.SetPosition(wParam);
8358 SetRectangularRange();
8359 Redraw();
8360 break;
8362 case SCI_GETRECTANGULARSELECTIONCARET:
8363 return sel.Rectangular().caret.Position();
8365 case SCI_SETRECTANGULARSELECTIONANCHOR:
8366 if (!sel.IsRectangular())
8367 sel.Clear();
8368 sel.selType = Selection::selRectangle;
8369 sel.Rectangular().anchor.SetPosition(wParam);
8370 SetRectangularRange();
8371 Redraw();
8372 break;
8374 case SCI_GETRECTANGULARSELECTIONANCHOR:
8375 return sel.Rectangular().anchor.Position();
8377 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
8378 if (!sel.IsRectangular())
8379 sel.Clear();
8380 sel.selType = Selection::selRectangle;
8381 sel.Rectangular().caret.SetVirtualSpace(wParam);
8382 SetRectangularRange();
8383 Redraw();
8384 break;
8386 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
8387 return sel.Rectangular().caret.VirtualSpace();
8389 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
8390 if (!sel.IsRectangular())
8391 sel.Clear();
8392 sel.selType = Selection::selRectangle;
8393 sel.Rectangular().anchor.SetVirtualSpace(wParam);
8394 SetRectangularRange();
8395 Redraw();
8396 break;
8398 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
8399 return sel.Rectangular().anchor.VirtualSpace();
8401 case SCI_SETVIRTUALSPACEOPTIONS:
8402 virtualSpaceOptions = wParam;
8403 break;
8405 case SCI_GETVIRTUALSPACEOPTIONS:
8406 return virtualSpaceOptions;
8408 case SCI_SETADDITIONALSELFORE:
8409 vs.selAdditionalForeground.desired = ColourDesired(wParam);
8410 InvalidateStyleRedraw();
8411 break;
8413 case SCI_SETADDITIONALSELBACK:
8414 vs.selAdditionalBackground.desired = ColourDesired(wParam);
8415 InvalidateStyleRedraw();
8416 break;
8418 case SCI_SETADDITIONALSELALPHA:
8419 vs.selAdditionalAlpha = wParam;
8420 InvalidateStyleRedraw();
8421 break;
8423 case SCI_GETADDITIONALSELALPHA:
8424 return vs.selAdditionalAlpha;
8426 case SCI_SETADDITIONALCARETFORE:
8427 vs.additionalCaretColour.desired = ColourDesired(wParam);
8428 InvalidateStyleRedraw();
8429 break;
8431 case SCI_GETADDITIONALCARETFORE:
8432 return vs.additionalCaretColour.desired.AsLong();
8434 case SCI_ROTATESELECTION:
8435 sel.RotateMain();
8436 InvalidateSelection(sel.RangeMain(), true);
8437 break;
8439 case SCI_SWAPMAINANCHORCARET:
8440 InvalidateSelection(sel.RangeMain());
8441 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
8442 break;
8444 default:
8445 return DefWndProc(iMessage, wParam, lParam);
8447 //Platform::DebugPrintf("end wnd proc\n");
8448 return 0l;