upgraded to scintilla 3.2.0
[TortoiseGit.git] / ext / scintilla / src / Editor.cxx
blob7eaec652dd48404399e70dfd2e10ea4c9d41e2b8
1 // Scintilla source code edit control
2 /** @file Editor.cxx
3 ** Main code for the edit control.
4 **/
5 // Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <ctype.h>
12 #include <assert.h>
14 #include <string>
15 #include <vector>
16 #include <map>
17 #include <algorithm>
18 #include <memory>
20 #include "Platform.h"
22 #include "ILexer.h"
23 #include "Scintilla.h"
25 #include "SplitVector.h"
26 #include "Partitioning.h"
27 #include "RunStyles.h"
28 #include "ContractionState.h"
29 #include "CellBuffer.h"
30 #include "KeyMap.h"
31 #include "Indicator.h"
32 #include "XPM.h"
33 #include "LineMarker.h"
34 #include "Style.h"
35 #include "ViewStyle.h"
36 #include "CharClassify.h"
37 #include "Decoration.h"
38 #include "Document.h"
39 #include "UniConversion.h"
40 #include "Selection.h"
41 #include "PositionCache.h"
42 #include "Editor.h"
44 #ifdef SCI_NAMESPACE
45 using namespace Scintilla;
46 #endif
49 return whether this modification represents an operation that
50 may reasonably be deferred (not done now OR [possibly] at all)
52 static bool CanDeferToLastStep(const DocModification &mh) {
53 if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
54 return true; // CAN skip
55 if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)))
56 return false; // MUST do
57 if (mh.modificationType & SC_MULTISTEPUNDOREDO)
58 return true; // CAN skip
59 return false; // PRESUMABLY must do
62 static bool CanEliminate(const DocModification &mh) {
63 return
64 (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
68 return whether this modification represents the FINAL step
69 in a [possibly lengthy] multi-step Undo/Redo sequence
71 static bool IsLastStep(const DocModification &mh) {
72 return
73 (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0
74 && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
75 && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
76 && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
79 Caret::Caret() :
80 active(false), on(false), period(500) {}
82 Timer::Timer() :
83 ticking(false), ticksToWait(0), tickerID(0) {}
85 Idler::Idler() :
86 state(false), idlerID(0) {}
88 static inline bool IsControlCharacter(int ch) {
89 // iscntrl returns true for lots of chars > 127 which are displayable
90 return ch >= 0 && ch < ' ';
93 static inline bool IsAllSpacesOrTabs(char *s, unsigned int len) {
94 for (unsigned int i = 0; i < len; i++) {
95 // This is safe because IsSpaceOrTab() will return false for null terminators
96 if (!IsSpaceOrTab(s[i]))
97 return false;
99 return true;
102 Editor::Editor() {
103 ctrlID = 0;
105 stylesValid = false;
106 technology = SC_TECHNOLOGY_DEFAULT;
108 printMagnification = 0;
109 printColourMode = SC_PRINT_NORMAL;
110 printWrapState = eWrapWord;
111 cursorMode = SC_CURSORNORMAL;
112 controlCharSymbol = 0; /* Draw the control characters */
114 hasFocus = false;
115 hideSelection = false;
116 inOverstrike = false;
117 errorStatus = 0;
118 mouseDownCaptures = true;
120 bufferedDraw = true;
121 twoPhaseDraw = true;
123 lastClickTime = 0;
124 dwellDelay = SC_TIME_FOREVER;
125 ticksToDwell = SC_TIME_FOREVER;
126 dwelling = false;
127 ptMouseLast.x = 0;
128 ptMouseLast.y = 0;
129 inDragDrop = ddNone;
130 dropWentOutside = false;
131 posDrag = SelectionPosition(invalidPosition);
132 posDrop = SelectionPosition(invalidPosition);
133 hotSpotClickPos = INVALID_POSITION;
134 selectionType = selChar;
136 lastXChosen = 0;
137 lineAnchorPos = 0;
138 originalAnchorPos = 0;
139 wordSelectAnchorStartPos = 0;
140 wordSelectAnchorEndPos = 0;
141 wordSelectInitialCaretPos = -1;
143 primarySelection = true;
145 caretXPolicy = CARET_SLOP | CARET_EVEN;
146 caretXSlop = 50;
148 caretYPolicy = CARET_EVEN;
149 caretYSlop = 0;
151 visiblePolicy = 0;
152 visibleSlop = 0;
154 searchAnchor = 0;
156 xOffset = 0;
157 xCaretMargin = 50;
158 horizontalScrollBarVisible = true;
159 scrollWidth = 2000;
160 trackLineWidth = false;
161 lineWidthMaxSeen = 0;
162 verticalScrollBarVisible = true;
163 endAtLastLine = true;
164 caretSticky = SC_CARETSTICKY_OFF;
165 marginOptions = SC_MARGINOPTION_NONE;
166 multipleSelection = false;
167 additionalSelectionTyping = false;
168 multiPasteMode = SC_MULTIPASTE_ONCE;
169 additionalCaretsBlink = true;
170 additionalCaretsVisible = true;
171 virtualSpaceOptions = SCVS_NONE;
173 pixmapLine = 0;
174 pixmapSelMargin = 0;
175 pixmapSelPattern = 0;
176 pixmapIndentGuide = 0;
177 pixmapIndentGuideHighlight = 0;
179 targetStart = 0;
180 targetEnd = 0;
181 searchFlags = 0;
183 topLine = 0;
184 posTopLine = 0;
186 lengthForEncode = -1;
188 needUpdateUI = 0;
189 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
190 braces[0] = invalidPosition;
191 braces[1] = invalidPosition;
192 bracesMatchStyle = STYLE_BRACEBAD;
193 highlightGuideColumn = 0;
195 theEdge = 0;
197 paintState = notPainting;
198 willRedrawAll = false;
200 modEventMask = SC_MODEVENTMASKALL;
202 pdoc = new Document();
203 pdoc->AddRef();
204 pdoc->AddWatcher(this, 0);
206 recordingMacro = false;
207 foldFlags = 0;
209 wrapState = eWrapNone;
210 wrapWidth = LineLayout::wrapWidthInfinite;
211 wrapStart = wrapLineLarge;
212 wrapEnd = wrapLineLarge;
213 wrapVisualFlags = 0;
214 wrapVisualFlagsLocation = 0;
215 wrapVisualStartIndent = 0;
216 wrapIndentMode = SC_WRAPINDENT_FIXED;
218 convertPastes = true;
220 hsStart = -1;
221 hsEnd = -1;
223 llc.SetLevel(LineLayoutCache::llcCaret);
224 posCache.SetSize(0x400);
227 Editor::~Editor() {
228 pdoc->RemoveWatcher(this, 0);
229 pdoc->Release();
230 pdoc = 0;
231 DropGraphics(true);
234 void Editor::Finalise() {
235 SetIdle(false);
236 CancelModes();
239 void Editor::DropGraphics(bool freeObjects) {
240 if (freeObjects) {
241 delete pixmapLine;
242 pixmapLine = 0;
243 delete pixmapSelMargin;
244 pixmapSelMargin = 0;
245 delete pixmapSelPattern;
246 pixmapSelPattern = 0;
247 delete pixmapIndentGuide;
248 pixmapIndentGuide = 0;
249 delete pixmapIndentGuideHighlight;
250 pixmapIndentGuideHighlight = 0;
251 } else {
252 if (pixmapLine)
253 pixmapLine->Release();
254 if (pixmapSelMargin)
255 pixmapSelMargin->Release();
256 if (pixmapSelPattern)
257 pixmapSelPattern->Release();
258 if (pixmapIndentGuide)
259 pixmapIndentGuide->Release();
260 if (pixmapIndentGuideHighlight)
261 pixmapIndentGuideHighlight->Release();
265 void Editor::AllocateGraphics() {
266 if (!pixmapLine)
267 pixmapLine = Surface::Allocate(technology);
268 if (!pixmapSelMargin)
269 pixmapSelMargin = Surface::Allocate(technology);
270 if (!pixmapSelPattern)
271 pixmapSelPattern = Surface::Allocate(technology);
272 if (!pixmapIndentGuide)
273 pixmapIndentGuide = Surface::Allocate(technology);
274 if (!pixmapIndentGuideHighlight)
275 pixmapIndentGuideHighlight = Surface::Allocate(technology);
278 void Editor::InvalidateStyleData() {
279 stylesValid = false;
280 vs.technology = technology;
281 DropGraphics(false);
282 AllocateGraphics();
283 llc.Invalidate(LineLayout::llInvalid);
284 posCache.Clear();
287 void Editor::InvalidateStyleRedraw() {
288 NeedWrapping();
289 InvalidateStyleData();
290 Redraw();
293 void Editor::RefreshStyleData() {
294 if (!stylesValid) {
295 stylesValid = true;
296 AutoSurface surface(this);
297 if (surface) {
298 vs.Refresh(*surface);
300 SetScrollBars();
301 SetRectangularRange();
305 PRectangle Editor::GetClientRectangle() {
306 return wMain.GetClientPosition();
309 PRectangle Editor::GetTextRectangle() {
310 PRectangle rc = GetClientRectangle();
311 rc.left += vs.fixedColumnWidth;
312 rc.right -= vs.rightMarginWidth;
313 return rc;
316 int Editor::LinesOnScreen() {
317 PRectangle rcClient = GetClientRectangle();
318 int htClient = rcClient.bottom - rcClient.top;
319 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
320 return htClient / vs.lineHeight;
323 int Editor::LinesToScroll() {
324 int retVal = LinesOnScreen() - 1;
325 if (retVal < 1)
326 return 1;
327 else
328 return retVal;
331 int Editor::MaxScrollPos() {
332 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
333 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
334 int retVal = cs.LinesDisplayed();
335 if (endAtLastLine) {
336 retVal -= LinesOnScreen();
337 } else {
338 retVal--;
340 if (retVal < 0) {
341 return 0;
342 } else {
343 return retVal;
347 const char *ControlCharacterString(unsigned char ch) {
348 const char *reps[] = {
349 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
350 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
351 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
352 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
354 if (ch < (sizeof(reps) / sizeof(reps[0]))) {
355 return reps[ch];
356 } else {
357 return "BAD";
362 * Convenience class to ensure LineLayout objects are always disposed.
364 class AutoLineLayout {
365 LineLayoutCache &llc;
366 LineLayout *ll;
367 AutoLineLayout &operator=(const AutoLineLayout &);
368 public:
369 AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
370 ~AutoLineLayout() {
371 llc.Dispose(ll);
372 ll = 0;
374 LineLayout *operator->() const {
375 return ll;
377 operator LineLayout *() const {
378 return ll;
380 void Set(LineLayout *ll_) {
381 llc.Dispose(ll);
382 ll = ll_;
386 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
387 if (sp.Position() < 0) {
388 return SelectionPosition(0);
389 } else if (sp.Position() > pdoc->Length()) {
390 return SelectionPosition(pdoc->Length());
391 } else {
392 // If not at end of line then set offset to 0
393 if (!pdoc->IsLineEndPosition(sp.Position()))
394 sp.SetVirtualSpace(0);
395 return sp;
399 Point Editor::LocationFromPosition(SelectionPosition pos) {
400 Point pt;
401 RefreshStyleData();
402 if (pos.Position() == INVALID_POSITION)
403 return pt;
404 int line = pdoc->LineFromPosition(pos.Position());
405 int lineVisible = cs.DisplayFromDoc(line);
406 //Platform::DebugPrintf("line=%d\n", line);
407 AutoSurface surface(this);
408 AutoLineLayout ll(llc, RetrieveLineLayout(line));
409 if (surface && ll) {
410 // -1 because of adding in for visible lines in following loop.
411 pt.y = (lineVisible - topLine - 1) * vs.lineHeight;
412 pt.x = 0;
413 unsigned int posLineStart = pdoc->LineStart(line);
414 LayoutLine(line, surface, vs, ll, wrapWidth);
415 int posInLine = pos.Position() - posLineStart;
416 // In case of very long line put x at arbitrary large position
417 if (posInLine > ll->maxLineLength) {
418 pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->LineStart(ll->lines)];
421 for (int subLine = 0; subLine < ll->lines; subLine++) {
422 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
423 pt.x = ll->positions[posInLine] - ll->positions[ll->LineStart(subLine)];
424 if (ll->wrapIndent != 0) {
425 int lineStart = ll->LineStart(subLine);
426 if (lineStart != 0) // Wrapped
427 pt.x += ll->wrapIndent;
430 if (posInLine >= ll->LineStart(subLine)) {
431 pt.y += vs.lineHeight;
434 pt.x += vs.fixedColumnWidth - xOffset;
436 pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
437 return pt;
440 Point Editor::LocationFromPosition(int pos) {
441 return LocationFromPosition(SelectionPosition(pos));
444 int Editor::XFromPosition(int pos) {
445 Point pt = LocationFromPosition(pos);
446 return pt.x - vs.fixedColumnWidth + xOffset;
449 int Editor::XFromPosition(SelectionPosition sp) {
450 Point pt = LocationFromPosition(sp);
451 return pt.x - vs.fixedColumnWidth + xOffset;
454 int Editor::LineFromLocation(Point pt) {
455 return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
458 void Editor::SetTopLine(int topLineNew) {
459 if (topLine != topLineNew) {
460 topLine = topLineNew;
461 ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
463 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
466 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
467 RefreshStyleData();
468 if (canReturnInvalid) {
469 PRectangle rcClient = GetTextRectangle();
470 if (!rcClient.Contains(pt))
471 return SelectionPosition(INVALID_POSITION);
472 if (pt.x < vs.fixedColumnWidth)
473 return SelectionPosition(INVALID_POSITION);
474 if (pt.y < 0)
475 return SelectionPosition(INVALID_POSITION);
477 pt.x = pt.x - vs.fixedColumnWidth + xOffset;
478 int visibleLine = pt.y / vs.lineHeight + topLine;
479 if (pt.y < 0) { // Division rounds towards 0
480 visibleLine = (static_cast<int>(pt.y) - (vs.lineHeight - 1)) / vs.lineHeight + topLine;
482 if (!canReturnInvalid && (visibleLine < 0))
483 visibleLine = 0;
484 int lineDoc = cs.DocFromDisplay(visibleLine);
485 if (canReturnInvalid && (lineDoc < 0))
486 return SelectionPosition(INVALID_POSITION);
487 if (lineDoc >= pdoc->LinesTotal())
488 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length());
489 unsigned int posLineStart = pdoc->LineStart(lineDoc);
490 SelectionPosition retVal(canReturnInvalid ? INVALID_POSITION : static_cast<int>(posLineStart));
491 AutoSurface surface(this);
492 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
493 if (surface && ll) {
494 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
495 int lineStartSet = cs.DisplayFromDoc(lineDoc);
496 int subLine = visibleLine - lineStartSet;
497 if (subLine < ll->lines) {
498 int lineStart = ll->LineStart(subLine);
499 int lineEnd = ll->LineLastVisible(subLine);
500 XYPOSITION subLineStart = ll->positions[lineStart];
502 if (ll->wrapIndent != 0) {
503 if (lineStart != 0) // Wrapped
504 pt.x -= ll->wrapIndent;
506 int i = ll->FindBefore(pt.x + subLineStart, lineStart, lineEnd);
507 while (i < lineEnd) {
508 if (charPosition) {
509 if ((pt.x + subLineStart) < (ll->positions[i + 1])) {
510 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
512 } else {
513 if ((pt.x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
514 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
517 i++;
519 if (virtualSpace) {
520 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
521 int spaceOffset = (pt.x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) /
522 spaceWidth;
523 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
524 } else if (canReturnInvalid) {
525 if (pt.x < (ll->positions[lineEnd] - subLineStart)) {
526 return SelectionPosition(pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1));
528 } else {
529 return SelectionPosition(lineEnd + posLineStart);
532 if (!canReturnInvalid)
533 return SelectionPosition(ll->numCharsInLine + posLineStart);
535 return retVal;
538 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
539 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
543 * Find the document position corresponding to an x coordinate on a particular document line.
544 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
546 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
547 RefreshStyleData();
548 if (lineDoc >= pdoc->LinesTotal())
549 return SelectionPosition(pdoc->Length());
550 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
551 AutoSurface surface(this);
552 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
553 int retVal = 0;
554 if (surface && ll) {
555 unsigned int posLineStart = pdoc->LineStart(lineDoc);
556 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
557 int subLine = 0;
558 int lineStart = ll->LineStart(subLine);
559 int lineEnd = ll->LineLastVisible(subLine);
560 XYPOSITION subLineStart = ll->positions[lineStart];
561 XYPOSITION newX = x;
563 if (ll->wrapIndent != 0) {
564 if (lineStart != 0) // Wrapped
565 newX -= ll->wrapIndent;
567 int i = ll->FindBefore(newX + subLineStart, lineStart, lineEnd);
568 while (i < lineEnd) {
569 if ((newX + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
570 retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
571 return SelectionPosition(retVal);
573 i++;
575 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
576 int spaceOffset = (newX + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) / spaceWidth;
577 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
579 return SelectionPosition(retVal);
582 int Editor::PositionFromLineX(int lineDoc, int x) {
583 return SPositionFromLineX(lineDoc, x).Position();
587 * If painting then abandon the painting because a wider redraw is needed.
588 * @return true if calling code should stop drawing.
590 bool Editor::AbandonPaint() {
591 if ((paintState == painting) && !paintingAllText) {
592 paintState = paintAbandoned;
594 return paintState == paintAbandoned;
597 void Editor::RedrawRect(PRectangle rc) {
598 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
600 // Clip the redraw rectangle into the client area
601 PRectangle rcClient = GetClientRectangle();
602 if (rc.top < rcClient.top)
603 rc.top = rcClient.top;
604 if (rc.bottom > rcClient.bottom)
605 rc.bottom = rcClient.bottom;
606 if (rc.left < rcClient.left)
607 rc.left = rcClient.left;
608 if (rc.right > rcClient.right)
609 rc.right = rcClient.right;
611 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
612 wMain.InvalidateRectangle(rc);
616 void Editor::Redraw() {
617 //Platform::DebugPrintf("Redraw all\n");
618 PRectangle rcClient = GetClientRectangle();
619 wMain.InvalidateRectangle(rcClient);
620 //wMain.InvalidateAll();
623 void Editor::RedrawSelMargin(int line, bool allAfter) {
624 if (!AbandonPaint()) {
625 if (vs.maskInLine) {
626 Redraw();
627 } else {
628 PRectangle rcSelMargin = GetClientRectangle();
629 rcSelMargin.right = vs.fixedColumnWidth;
630 if (line != -1) {
631 int position = pdoc->LineStart(line);
632 PRectangle rcLine = RectangleFromRange(position, position);
634 // Inflate line rectangle if there are image markers with height larger than line height
635 if (vs.largestMarkerHeight > vs.lineHeight) {
636 int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
637 rcLine.top -= delta;
638 rcLine.bottom += delta;
639 if (rcLine.top < rcSelMargin.top)
640 rcLine.top = rcSelMargin.top;
641 if (rcLine.bottom > rcSelMargin.bottom)
642 rcLine.bottom = rcSelMargin.bottom;
645 rcSelMargin.top = rcLine.top;
646 if (!allAfter)
647 rcSelMargin.bottom = rcLine.bottom;
649 wMain.InvalidateRectangle(rcSelMargin);
654 PRectangle Editor::RectangleFromRange(int start, int end) {
655 int minPos = start;
656 if (minPos > end)
657 minPos = end;
658 int maxPos = start;
659 if (maxPos < end)
660 maxPos = end;
661 int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos));
662 int lineDocMax = pdoc->LineFromPosition(maxPos);
663 int maxLine = cs.DisplayFromDoc(lineDocMax) + cs.GetHeight(lineDocMax) - 1;
664 PRectangle rcClient = GetTextRectangle();
665 PRectangle rc;
666 rc.left = vs.fixedColumnWidth;
667 rc.top = (minLine - topLine) * vs.lineHeight;
668 if (rc.top < 0)
669 rc.top = 0;
670 rc.right = rcClient.right;
671 rc.bottom = (maxLine - topLine + 1) * vs.lineHeight;
672 // Ensure PRectangle is within 16 bit space
673 rc.top = Platform::Clamp(rc.top, -32000, 32000);
674 rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000);
676 return rc;
679 void Editor::InvalidateRange(int start, int end) {
680 RedrawRect(RectangleFromRange(start, end));
683 int Editor::CurrentPosition() {
684 return sel.MainCaret();
687 bool Editor::SelectionEmpty() {
688 return sel.Empty();
691 SelectionPosition Editor::SelectionStart() {
692 return sel.RangeMain().Start();
695 SelectionPosition Editor::SelectionEnd() {
696 return sel.RangeMain().End();
699 void Editor::SetRectangularRange() {
700 if (sel.IsRectangular()) {
701 int xAnchor = XFromPosition(sel.Rectangular().anchor);
702 int xCaret = XFromPosition(sel.Rectangular().caret);
703 if (sel.selType == Selection::selThin) {
704 xCaret = xAnchor;
706 int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
707 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
708 int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
709 for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
710 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
711 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
712 range.ClearVirtualSpace();
713 if (line == lineAnchorRect)
714 sel.SetSelection(range);
715 else
716 sel.AddSelectionWithoutTrim(range);
721 void Editor::ThinRectangularRange() {
722 if (sel.IsRectangular()) {
723 sel.selType = Selection::selThin;
724 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
725 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
726 } else {
727 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
729 SetRectangularRange();
733 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
734 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
735 invalidateWholeSelection = true;
737 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
738 // +1 for lastAffected ensures caret repainted
739 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
740 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
741 if (invalidateWholeSelection) {
742 for (size_t r=0; r<sel.Count(); r++) {
743 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
744 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
745 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
746 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
749 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
750 InvalidateRange(firstAffected, lastAffected);
753 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
754 currentPos_ = ClampPositionIntoDocument(currentPos_);
755 anchor_ = ClampPositionIntoDocument(anchor_);
756 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
757 /* For Line selection - ensure the anchor and caret are always
758 at the beginning and end of the region lines. */
759 if (sel.selType == Selection::selLines) {
760 if (currentPos_ > anchor_) {
761 anchor_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
762 currentPos_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
763 } else {
764 currentPos_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
765 anchor_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
768 SelectionRange rangeNew(currentPos_, anchor_);
769 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
770 InvalidateSelection(rangeNew);
772 sel.RangeMain() = rangeNew;
773 SetRectangularRange();
774 ClaimSelection();
776 if (highlightDelimiter.NeedsDrawing(currentLine)) {
777 RedrawSelMargin();
781 void Editor::SetSelection(int currentPos_, int anchor_) {
782 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
785 // Just move the caret on the main selection
786 void Editor::SetSelection(SelectionPosition currentPos_) {
787 currentPos_ = ClampPositionIntoDocument(currentPos_);
788 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
789 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
790 InvalidateSelection(SelectionRange(currentPos_));
792 if (sel.IsRectangular()) {
793 sel.Rectangular() =
794 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
795 SetRectangularRange();
796 } else {
797 sel.RangeMain() =
798 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
800 ClaimSelection();
802 if (highlightDelimiter.NeedsDrawing(currentLine)) {
803 RedrawSelMargin();
807 void Editor::SetSelection(int currentPos_) {
808 SetSelection(SelectionPosition(currentPos_));
811 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
812 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
813 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
814 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
815 InvalidateSelection(rangeNew);
817 sel.Clear();
818 sel.RangeMain() = rangeNew;
819 SetRectangularRange();
820 ClaimSelection();
822 if (highlightDelimiter.NeedsDrawing(currentLine)) {
823 RedrawSelMargin();
827 void Editor::SetEmptySelection(int currentPos_) {
828 SetEmptySelection(SelectionPosition(currentPos_));
831 bool Editor::RangeContainsProtected(int start, int end) const {
832 if (vs.ProtectionActive()) {
833 if (start > end) {
834 int t = start;
835 start = end;
836 end = t;
838 int mask = pdoc->stylingBitsMask;
839 for (int pos = start; pos < end; pos++) {
840 if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())
841 return true;
844 return false;
847 bool Editor::SelectionContainsProtected() {
848 for (size_t r=0; r<sel.Count(); r++) {
849 if (RangeContainsProtected(sel.Range(r).Start().Position(),
850 sel.Range(r).End().Position())) {
851 return true;
854 return false;
858 * Asks document to find a good position and then moves out of any invisible positions.
860 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
861 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
864 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
865 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
866 if (posMoved != pos.Position())
867 pos.SetPosition(posMoved);
868 if (vs.ProtectionActive()) {
869 int mask = pdoc->stylingBitsMask;
870 if (moveDir > 0) {
871 if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()) {
872 while ((pos.Position() < pdoc->Length()) &&
873 (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()))
874 pos.Add(1);
876 } else if (moveDir < 0) {
877 if (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()) {
878 while ((pos.Position() > 0) &&
879 (vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()))
880 pos.Add(-1);
884 return pos;
887 int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
888 bool simpleCaret = (sel.Count() == 1) && sel.Empty();
889 SelectionPosition spCaret = sel.Last();
891 int delta = newPos.Position() - sel.MainCaret();
892 newPos = ClampPositionIntoDocument(newPos);
893 newPos = MovePositionOutsideChar(newPos, delta);
894 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
895 // Can't turn into multiple selection so clear additional selections
896 InvalidateSelection(SelectionRange(newPos), true);
897 SelectionRange rangeMain = sel.RangeMain();
898 sel.SetSelection(rangeMain);
900 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
901 // Switching to rectangular
902 SelectionRange rangeMain = sel.RangeMain();
903 sel.Clear();
904 sel.Rectangular() = rangeMain;
906 if (selt != Selection::noSel) {
907 sel.selType = selt;
909 if (selt != Selection::noSel || sel.MoveExtends()) {
910 SetSelection(newPos);
911 } else {
912 SetEmptySelection(newPos);
914 ShowCaretAtCurrentPosition();
916 int currentLine = pdoc->LineFromPosition(newPos.Position());
917 if (ensureVisible) {
918 // In case in need of wrapping to ensure DisplayFromDoc works.
919 if (currentLine >= wrapStart)
920 WrapLines(true, -1);
921 XYScrollPosition newXY = XYScrollToMakeVisible(true, true, true);
922 if (simpleCaret && (newXY.xOffset == xOffset)) {
923 // simple vertical scroll then invalidate
924 ScrollTo(newXY.topLine);
925 InvalidateSelection(SelectionRange(spCaret), true);
926 } else {
927 SetXYScroll(newXY);
931 if (highlightDelimiter.NeedsDrawing(currentLine)) {
932 RedrawSelMargin();
934 return 0;
937 int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
938 return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
941 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
942 pos = ClampPositionIntoDocument(pos);
943 pos = MovePositionOutsideChar(pos, moveDir);
944 int lineDoc = pdoc->LineFromPosition(pos.Position());
945 if (cs.GetVisible(lineDoc)) {
946 return pos;
947 } else {
948 int lineDisplay = cs.DisplayFromDoc(lineDoc);
949 if (moveDir > 0) {
950 // lineDisplay is already line before fold as lines in fold use display line of line after fold
951 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
952 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
953 } else {
954 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
955 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
960 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
961 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
964 Point Editor::PointMainCaret() {
965 return LocationFromPosition(sel.Range(sel.Main()).caret);
969 * Choose the x position that the caret will try to stick to
970 * as it moves up and down.
972 void Editor::SetLastXChosen() {
973 Point pt = PointMainCaret();
974 lastXChosen = pt.x + xOffset;
977 void Editor::ScrollTo(int line, bool moveThumb) {
978 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
979 if (topLineNew != topLine) {
980 // Try to optimise small scrolls
981 #ifndef UNDER_CE
982 int linesToMove = topLine - topLineNew;
983 bool performBlit = (abs(linesToMove) <= 10) && (paintState == notPainting);
984 willRedrawAll = !performBlit;
985 #endif
986 SetTopLine(topLineNew);
987 // Optimize by styling the view as this will invalidate any needed area
988 // which could abort the initial paint if discovered later.
989 StyleToPositionInView(PositionAfterArea(GetClientRectangle()));
990 #ifndef UNDER_CE
991 // Perform redraw rather than scroll if many lines would be redrawn anyway.
992 if (performBlit) {
993 ScrollText(linesToMove);
994 } else {
995 Redraw();
997 willRedrawAll = false;
998 #else
999 Redraw();
1000 #endif
1001 if (moveThumb) {
1002 SetVerticalScrollPos();
1007 void Editor::ScrollText(int /* linesToMove */) {
1008 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
1009 Redraw();
1012 void Editor::HorizontalScrollTo(int xPos) {
1013 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
1014 if (xPos < 0)
1015 xPos = 0;
1016 if ((wrapState == eWrapNone) && (xOffset != xPos)) {
1017 xOffset = xPos;
1018 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1019 SetHorizontalScrollPos();
1020 RedrawRect(GetClientRectangle());
1024 void Editor::VerticalCentreCaret() {
1025 int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
1026 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1027 int newTop = lineDisplay - (LinesOnScreen() / 2);
1028 if (topLine != newTop) {
1029 SetTopLine(newTop > 0 ? newTop : 0);
1030 RedrawRect(GetClientRectangle());
1034 // Avoid 64 bit compiler warnings.
1035 // Scintilla does not support text buffers larger than 2**31
1036 static int istrlen(const char *s) {
1037 return static_cast<int>(strlen(s));
1040 void Editor::MoveSelectedLines(int lineDelta) {
1042 // if selection doesn't start at the beginning of the line, set the new start
1043 int selectionStart = SelectionStart().Position();
1044 int startLine = pdoc->LineFromPosition(selectionStart);
1045 int beginningOfStartLine = pdoc->LineStart(startLine);
1046 selectionStart = beginningOfStartLine;
1048 // if selection doesn't end at the beginning of a line greater than that of the start,
1049 // then set it at the beginning of the next one
1050 int selectionEnd = SelectionEnd().Position();
1051 int endLine = pdoc->LineFromPosition(selectionEnd);
1052 int beginningOfEndLine = pdoc->LineStart(endLine);
1053 bool appendEol = false;
1054 if (selectionEnd > beginningOfEndLine
1055 || selectionStart == selectionEnd) {
1056 selectionEnd = pdoc->LineStart(endLine + 1);
1057 appendEol = (selectionEnd == pdoc->Length() && pdoc->LineFromPosition(selectionEnd) == endLine);
1060 // if there's nowhere for the selection to move
1061 // (i.e. at the beginning going up or at the end going down),
1062 // stop it right there!
1063 if ((selectionStart == 0 && lineDelta < 0)
1064 || (selectionEnd == pdoc->Length() && lineDelta > 0)
1065 || selectionStart == selectionEnd) {
1066 return;
1069 UndoGroup ug(pdoc);
1071 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
1072 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
1073 ClearSelection();
1074 selectionEnd = CurrentPosition();
1076 SetSelection(selectionStart, selectionEnd);
1078 SelectionText selectedText;
1079 CopySelectionRange(&selectedText);
1081 int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
1082 Point currentLocation = LocationFromPosition(CurrentPosition());
1083 int currentLine = LineFromLocation(currentLocation);
1085 if (appendEol)
1086 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
1087 ClearSelection();
1089 const char *eol = StringFromEOLMode(pdoc->eolMode);
1090 if (currentLine + lineDelta >= pdoc->LinesTotal())
1091 pdoc->InsertCString(pdoc->Length(), eol);
1092 GoToLine(currentLine + lineDelta);
1094 pdoc->InsertCString(CurrentPosition(), selectedText.s);
1095 if (appendEol) {
1096 pdoc->InsertCString(CurrentPosition() + selectionLength, eol);
1097 selectionLength += istrlen(eol);
1099 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1102 void Editor::MoveSelectedLinesUp() {
1103 MoveSelectedLines(-1);
1106 void Editor::MoveSelectedLinesDown() {
1107 MoveSelectedLines(1);
1110 void Editor::MoveCaretInsideView(bool ensureVisible) {
1111 PRectangle rcClient = GetTextRectangle();
1112 Point pt = PointMainCaret();
1113 if (pt.y < rcClient.top) {
1114 MovePositionTo(SPositionFromLocation(
1115 Point(lastXChosen - xOffset, rcClient.top),
1116 false, false, UserVirtualSpace()),
1117 Selection::noSel, ensureVisible);
1118 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1119 int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
1120 MovePositionTo(SPositionFromLocation(
1121 Point(lastXChosen - xOffset, rcClient.top + yOfLastLineFullyDisplayed),
1122 false, false, UserVirtualSpace()),
1123 Selection::noSel, ensureVisible);
1127 int Editor::DisplayFromPosition(int pos) {
1128 int lineDoc = pdoc->LineFromPosition(pos);
1129 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1130 AutoSurface surface(this);
1131 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
1132 if (surface && ll) {
1133 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
1134 unsigned int posLineStart = pdoc->LineStart(lineDoc);
1135 int posInLine = pos - posLineStart;
1136 lineDisplay--; // To make up for first increment ahead.
1137 for (int subLine = 0; subLine < ll->lines; subLine++) {
1138 if (posInLine >= ll->LineStart(subLine)) {
1139 lineDisplay++;
1143 return lineDisplay;
1147 * Ensure the caret is reasonably visible in context.
1149 Caret policy in SciTE
1151 If slop is set, we can define a slop value.
1152 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1153 This zone is defined as a number of pixels near the vertical margins,
1154 and as a number of lines near the horizontal margins.
1155 By keeping the caret away from the edges, it is seen within its context,
1156 so it is likely that the identifier that the caret is on can be completely seen,
1157 and that the current line is seen with some of the lines following it which are
1158 often dependent on that line.
1160 If strict is set, the policy is enforced... strictly.
1161 The caret is centred on the display if slop is not set,
1162 and cannot go in the UZ if slop is set.
1164 If jumps is set, the display is moved more energetically
1165 so the caret can move in the same direction longer before the policy is applied again.
1166 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1168 If even is not set, instead of having symmetrical UZs,
1169 the left and bottom UZs are extended up to right and top UZs respectively.
1170 This way, we favour the displaying of useful information: the begining of lines,
1171 where most code reside, and the lines after the caret, eg. the body of a function.
1173 | | | | |
1174 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1175 | | | | | visibility or going into the UZ) display is...
1176 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1177 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1178 0 | 0 | 0 | 1 | Yes | moved by one position
1179 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1180 0 | 0 | 1 | 1 | Yes | centred on the caret
1181 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1182 0 | 1 | - | 1 | No, caret is always centred | -
1183 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1184 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1185 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1186 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1187 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1188 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1189 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1192 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const bool useMargin, const bool vert, const bool horiz) {
1193 PRectangle rcClient = GetTextRectangle();
1194 const SelectionPosition posCaret = posDrag.IsValid() ? posDrag : sel.RangeMain().caret;
1195 const Point pt = LocationFromPosition(posCaret);
1196 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1197 const int lineCaret = DisplayFromPosition(posCaret.Position());
1199 XYScrollPosition newXY(xOffset, topLine);
1201 // Vertical positioning
1202 if (vert && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1203 const int linesOnScreen = LinesOnScreen();
1204 const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1205 const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
1206 const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
1207 const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
1208 const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
1210 // It should be possible to scroll the window to show the caret,
1211 // but this fails to remove the caret on GTK+
1212 if (bSlop) { // A margin is defined
1213 int yMoveT, yMoveB;
1214 if (bStrict) {
1215 int yMarginT, yMarginB;
1216 if (!useMargin) {
1217 // In drag mode, avoid moves
1218 // otherwise, a double click will select several lines.
1219 yMarginT = yMarginB = 0;
1220 } else {
1221 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1222 // a maximum of slightly less than half the heigth of the text area.
1223 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1224 if (bEven) {
1225 yMarginB = yMarginT;
1226 } else {
1227 yMarginB = linesOnScreen - yMarginT - 1;
1230 yMoveT = yMarginT;
1231 if (bEven) {
1232 if (bJump) {
1233 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1235 yMoveB = yMoveT;
1236 } else {
1237 yMoveB = linesOnScreen - yMoveT - 1;
1239 if (lineCaret < topLine + yMarginT) {
1240 // Caret goes too high
1241 newXY.topLine = lineCaret - yMoveT;
1242 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1243 // Caret goes too low
1244 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1246 } else { // Not strict
1247 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1248 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1249 if (bEven) {
1250 yMoveB = yMoveT;
1251 } else {
1252 yMoveB = linesOnScreen - yMoveT - 1;
1254 if (lineCaret < topLine) {
1255 // Caret goes too high
1256 newXY.topLine = lineCaret - yMoveT;
1257 } else if (lineCaret > topLine + linesOnScreen - 1) {
1258 // Caret goes too low
1259 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1262 } else { // No slop
1263 if (!bStrict && !bJump) {
1264 // Minimal move
1265 if (lineCaret < topLine) {
1266 // Caret goes too high
1267 newXY.topLine = lineCaret;
1268 } else if (lineCaret > topLine + linesOnScreen - 1) {
1269 // Caret goes too low
1270 if (bEven) {
1271 newXY.topLine = lineCaret - linesOnScreen + 1;
1272 } else {
1273 newXY.topLine = lineCaret;
1276 } else { // Strict or going out of display
1277 if (bEven) {
1278 // Always center caret
1279 newXY.topLine = lineCaret - halfScreen;
1280 } else {
1281 // Always put caret on top of display
1282 newXY.topLine = lineCaret;
1286 newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
1289 // Horizontal positioning
1290 if (horiz && (wrapState == eWrapNone)) {
1291 const int halfScreen = Platform::Maximum(rcClient.Width() - 4, 4) / 2;
1292 const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
1293 const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
1294 const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
1295 const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
1297 if (bSlop) { // A margin is defined
1298 int xMoveL, xMoveR;
1299 if (bStrict) {
1300 int xMarginL, xMarginR;
1301 if (!useMargin) {
1302 // In drag mode, avoid moves unless very near of the margin
1303 // otherwise, a simple click will select text.
1304 xMarginL = xMarginR = 2;
1305 } else {
1306 // xMargin must equal to caretXSlop, with a minimum of 2 and
1307 // a maximum of slightly less than half the width of the text area.
1308 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1309 if (bEven) {
1310 xMarginL = xMarginR;
1311 } else {
1312 xMarginL = rcClient.Width() - xMarginR - 4;
1315 if (bJump && bEven) {
1316 // Jump is used only in even mode
1317 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1318 } else {
1319 xMoveL = xMoveR = 0; // Not used, avoid a warning
1321 if (pt.x < rcClient.left + xMarginL) {
1322 // Caret is on the left of the display
1323 if (bJump && bEven) {
1324 newXY.xOffset -= xMoveL;
1325 } else {
1326 // Move just enough to allow to display the caret
1327 newXY.xOffset -= (rcClient.left + xMarginL) - pt.x;
1329 } else if (pt.x >= rcClient.right - xMarginR) {
1330 // Caret is on the right of the display
1331 if (bJump && bEven) {
1332 newXY.xOffset += xMoveR;
1333 } else {
1334 // Move just enough to allow to display the caret
1335 newXY.xOffset += pt.x - (rcClient.right - xMarginR) + 1;
1338 } else { // Not strict
1339 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1340 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1341 if (bEven) {
1342 xMoveL = xMoveR;
1343 } else {
1344 xMoveL = rcClient.Width() - xMoveR - 4;
1346 if (pt.x < rcClient.left) {
1347 // Caret is on the left of the display
1348 newXY.xOffset -= xMoveL;
1349 } else if (pt.x >= rcClient.right) {
1350 // Caret is on the right of the display
1351 newXY.xOffset += xMoveR;
1354 } else { // No slop
1355 if (bStrict ||
1356 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1357 // Strict or going out of display
1358 if (bEven) {
1359 // Center caret
1360 newXY.xOffset += pt.x - rcClient.left - halfScreen;
1361 } else {
1362 // Put caret on right
1363 newXY.xOffset += pt.x - rcClient.right + 1;
1365 } else {
1366 // Move just enough to allow to display the caret
1367 if (pt.x < rcClient.left) {
1368 // Caret is on the left of the display
1369 if (bEven) {
1370 newXY.xOffset -= rcClient.left - pt.x;
1371 } else {
1372 newXY.xOffset += pt.x - rcClient.right + 1;
1374 } else if (pt.x >= rcClient.right) {
1375 // Caret is on the right of the display
1376 newXY.xOffset += pt.x - rcClient.right + 1;
1380 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1381 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1382 newXY.xOffset = pt.x + xOffset - rcClient.left;
1383 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1384 newXY.xOffset = pt.x + xOffset - rcClient.right + 1;
1385 if (vs.caretStyle == CARETSTYLE_BLOCK) {
1386 // Ensure we can see a good portion of the block caret
1387 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1390 if (newXY.xOffset < 0) {
1391 newXY.xOffset = 0;
1395 return newXY;
1398 void Editor::SetXYScroll(XYScrollPosition newXY) {
1399 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1400 if (newXY.topLine != topLine) {
1401 SetTopLine(newXY.topLine);
1402 SetVerticalScrollPos();
1404 if (newXY.xOffset != xOffset) {
1405 xOffset = newXY.xOffset;
1406 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1407 if (newXY.xOffset > 0) {
1408 PRectangle rcText = GetTextRectangle();
1409 if (horizontalScrollBarVisible &&
1410 rcText.Width() + xOffset > scrollWidth) {
1411 scrollWidth = xOffset + rcText.Width();
1412 SetScrollBars();
1415 SetHorizontalScrollPos();
1417 Redraw();
1418 UpdateSystemCaret();
1422 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1423 SetXYScroll(XYScrollToMakeVisible(useMargin, vert, horiz));
1426 void Editor::ShowCaretAtCurrentPosition() {
1427 if (hasFocus) {
1428 caret.active = true;
1429 caret.on = true;
1430 SetTicking(true);
1431 } else {
1432 caret.active = false;
1433 caret.on = false;
1435 InvalidateCaret();
1438 void Editor::DropCaret() {
1439 caret.active = false;
1440 InvalidateCaret();
1443 void Editor::InvalidateCaret() {
1444 if (posDrag.IsValid()) {
1445 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1446 } else {
1447 for (size_t r=0; r<sel.Count(); r++) {
1448 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1451 UpdateSystemCaret();
1454 void Editor::UpdateSystemCaret() {
1457 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1458 docLineStart = Platform::Clamp(docLineStart, 0, pdoc->LinesTotal());
1459 if (wrapStart > docLineStart) {
1460 wrapStart = docLineStart;
1461 llc.Invalidate(LineLayout::llPositions);
1463 if (wrapEnd < docLineEnd) {
1464 wrapEnd = docLineEnd;
1466 wrapEnd = Platform::Clamp(wrapEnd, 0, pdoc->LinesTotal());
1467 // Wrap lines during idle.
1468 if ((wrapState != eWrapNone) && (wrapEnd != wrapStart)) {
1469 SetIdle(true);
1473 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1474 AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
1475 int linesWrapped = 1;
1476 if (ll) {
1477 LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
1478 linesWrapped = ll->lines;
1480 return cs.SetHeight(lineToWrap, linesWrapped +
1481 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1484 // Check if wrapping needed and perform any needed wrapping.
1485 // fullwrap: if true, all lines which need wrapping will be done,
1486 // in this single call.
1487 // priorityWrapLineStart: If greater than or equal to zero, all lines starting from
1488 // here to 1 page + 100 lines past will be wrapped (even if there are
1489 // more lines under wrapping process in idle).
1490 // If it is neither fullwrap, nor priorityWrap, then 1 page + 100 lines will be
1491 // wrapped, if there are any wrapping going on in idle. (Generally this
1492 // condition is called only from idler).
1493 // Return true if wrapping occurred.
1494 bool Editor::WrapLines(bool fullWrap, int priorityWrapLineStart) {
1495 // If there are any pending wraps, do them during idle if possible.
1496 int linesInOneCall = LinesOnScreen() + 100;
1497 if (priorityWrapLineStart >= 0) {
1498 // Using DocFromDisplay() here may result in chicken and egg problem in certain corner cases,
1499 // which will hopefully be handled by added 100 lines. If some lines are still missed, idle wrapping will catch on.
1500 int docLinesInOneCall = cs.DocFromDisplay(topLine + LinesOnScreen() + 100) - cs.DocFromDisplay(topLine);
1501 linesInOneCall = Platform::Maximum(linesInOneCall, docLinesInOneCall);
1503 if (wrapState != eWrapNone) {
1504 if (wrapStart < wrapEnd) {
1505 if (!SetIdle(true)) {
1506 // Idle processing not supported so full wrap required.
1507 fullWrap = true;
1510 if (!fullWrap && priorityWrapLineStart >= 0 &&
1511 // .. and if the paint window is outside pending wraps
1512 (((priorityWrapLineStart + linesInOneCall) < wrapStart) ||
1513 (priorityWrapLineStart > wrapEnd))) {
1514 // No priority wrap pending
1515 return false;
1518 int goodTopLine = topLine;
1519 bool wrapOccurred = false;
1520 if (wrapStart <= pdoc->LinesTotal()) {
1521 if (wrapState == eWrapNone) {
1522 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1523 wrapWidth = LineLayout::wrapWidthInfinite;
1524 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1525 cs.SetHeight(lineDoc, 1 +
1526 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1528 wrapOccurred = true;
1530 wrapStart = wrapLineLarge;
1531 wrapEnd = wrapLineLarge;
1532 } else {
1533 if (wrapEnd >= pdoc->LinesTotal())
1534 wrapEnd = pdoc->LinesTotal();
1535 //ElapsedTime et;
1536 int lineDocTop = cs.DocFromDisplay(topLine);
1537 int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1538 PRectangle rcTextArea = GetClientRectangle();
1539 rcTextArea.left = vs.fixedColumnWidth;
1540 rcTextArea.right -= vs.rightMarginWidth;
1541 wrapWidth = rcTextArea.Width();
1542 RefreshStyleData();
1543 AutoSurface surface(this);
1544 if (surface) {
1545 bool priorityWrap = false;
1546 int lastLineToWrap = wrapEnd;
1547 int lineToWrap = wrapStart;
1548 if (!fullWrap) {
1549 if (priorityWrapLineStart >= 0) {
1550 // This is a priority wrap.
1551 lineToWrap = priorityWrapLineStart;
1552 lastLineToWrap = priorityWrapLineStart + linesInOneCall;
1553 priorityWrap = true;
1554 } else {
1555 // This is idle wrap.
1556 lastLineToWrap = wrapStart + linesInOneCall;
1558 if (lastLineToWrap >= wrapEnd)
1559 lastLineToWrap = wrapEnd;
1560 } // else do a fullWrap.
1562 // Ensure all lines being wrapped are styled.
1563 pdoc->EnsureStyledTo(pdoc->LineEnd(lastLineToWrap));
1565 // Platform::DebugPrintf("Wraplines: full = %d, priorityStart = %d (wrapping: %d to %d)\n", fullWrap, priorityWrapLineStart, lineToWrap, lastLineToWrap);
1566 // Platform::DebugPrintf("Pending wraps: %d to %d\n", wrapStart, wrapEnd);
1567 while (lineToWrap < lastLineToWrap) {
1568 if (WrapOneLine(surface, lineToWrap)) {
1569 wrapOccurred = true;
1571 lineToWrap++;
1573 if (!priorityWrap)
1574 wrapStart = lineToWrap;
1575 // If wrapping is done, bring it to resting position
1576 if (wrapStart >= wrapEnd) {
1577 wrapStart = wrapLineLarge;
1578 wrapEnd = wrapLineLarge;
1581 goodTopLine = cs.DisplayFromDoc(lineDocTop);
1582 if (subLineTop < cs.GetHeight(lineDocTop))
1583 goodTopLine += subLineTop;
1584 else
1585 goodTopLine += cs.GetHeight(lineDocTop);
1586 //double durWrap = et.Duration(true);
1587 //Platform::DebugPrintf("Wrap:%9.6g \n", durWrap);
1590 if (wrapOccurred) {
1591 SetScrollBars();
1592 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1593 SetVerticalScrollPos();
1595 return wrapOccurred;
1598 void Editor::LinesJoin() {
1599 if (!RangeContainsProtected(targetStart, targetEnd)) {
1600 UndoGroup ug(pdoc);
1601 bool prevNonWS = true;
1602 for (int pos = targetStart; pos < targetEnd; pos++) {
1603 if (IsEOLChar(pdoc->CharAt(pos))) {
1604 targetEnd -= pdoc->LenChar(pos);
1605 pdoc->DelChar(pos);
1606 if (prevNonWS) {
1607 // Ensure at least one space separating previous lines
1608 pdoc->InsertChar(pos, ' ');
1609 targetEnd++;
1611 } else {
1612 prevNonWS = pdoc->CharAt(pos) != ' ';
1618 const char *Editor::StringFromEOLMode(int eolMode) {
1619 if (eolMode == SC_EOL_CRLF) {
1620 return "\r\n";
1621 } else if (eolMode == SC_EOL_CR) {
1622 return "\r";
1623 } else {
1624 return "\n";
1628 void Editor::LinesSplit(int pixelWidth) {
1629 if (!RangeContainsProtected(targetStart, targetEnd)) {
1630 if (pixelWidth == 0) {
1631 PRectangle rcText = GetTextRectangle();
1632 pixelWidth = rcText.Width();
1634 int lineStart = pdoc->LineFromPosition(targetStart);
1635 int lineEnd = pdoc->LineFromPosition(targetEnd);
1636 const char *eol = StringFromEOLMode(pdoc->eolMode);
1637 UndoGroup ug(pdoc);
1638 for (int line = lineStart; line <= lineEnd; line++) {
1639 AutoSurface surface(this);
1640 AutoLineLayout ll(llc, RetrieveLineLayout(line));
1641 if (surface && ll) {
1642 unsigned int posLineStart = pdoc->LineStart(line);
1643 LayoutLine(line, surface, vs, ll, pixelWidth);
1644 for (int subLine = 1; subLine < ll->lines; subLine++) {
1645 pdoc->InsertCString(
1646 static_cast<int>(posLineStart + (subLine - 1) * strlen(eol) +
1647 ll->LineStart(subLine)),
1648 eol);
1649 targetEnd += static_cast<int>(strlen(eol));
1652 lineEnd = pdoc->LineFromPosition(targetEnd);
1657 int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) {
1658 if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
1659 return markerDefault;
1660 return markerCheck;
1663 bool ValidStyledText(ViewStyle &vs, size_t styleOffset, const StyledText &st) {
1664 if (st.multipleStyles) {
1665 for (size_t iStyle=0; iStyle<st.length; iStyle++) {
1666 if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
1667 return false;
1669 } else {
1670 if (!vs.ValidStyle(styleOffset + st.style))
1671 return false;
1673 return true;
1676 static int WidthStyledText(Surface *surface, ViewStyle &vs, int styleOffset,
1677 const char *text, const unsigned char *styles, size_t len) {
1678 int width = 0;
1679 size_t start = 0;
1680 while (start < len) {
1681 size_t style = styles[start];
1682 size_t endSegment = start;
1683 while ((endSegment+1 < len) && (static_cast<size_t>(styles[endSegment+1]) == style))
1684 endSegment++;
1685 width += surface->WidthText(vs.styles[style+styleOffset].font, text + start,
1686 static_cast<int>(endSegment - start + 1));
1687 start = endSegment + 1;
1689 return width;
1692 static int WidestLineWidth(Surface *surface, ViewStyle &vs, int styleOffset, const StyledText &st) {
1693 int widthMax = 0;
1694 size_t start = 0;
1695 while (start < st.length) {
1696 size_t lenLine = st.LineLength(start);
1697 int widthSubLine;
1698 if (st.multipleStyles) {
1699 widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
1700 } else {
1701 widthSubLine = surface->WidthText(vs.styles[styleOffset + st.style].font,
1702 st.text + start, static_cast<int>(lenLine));
1704 if (widthSubLine > widthMax)
1705 widthMax = widthSubLine;
1706 start += lenLine + 1;
1708 return widthMax;
1711 void DrawStyledText(Surface *surface, ViewStyle &vs, int styleOffset, PRectangle rcText, int ascent,
1712 const StyledText &st, size_t start, size_t length) {
1714 if (st.multipleStyles) {
1715 int x = rcText.left;
1716 size_t i = 0;
1717 while (i < length) {
1718 size_t end = i;
1719 int style = st.styles[i + start];
1720 while (end < length-1 && st.styles[start+end+1] == style)
1721 end++;
1722 style += styleOffset;
1723 int width = surface->WidthText(vs.styles[style].font,
1724 st.text + start + i, static_cast<int>(end - i + 1));
1725 PRectangle rcSegment = rcText;
1726 rcSegment.left = x;
1727 rcSegment.right = x + width + 1;
1728 surface->DrawTextNoClip(rcSegment, vs.styles[style].font,
1729 ascent, st.text + start + i,
1730 static_cast<int>(end - i + 1),
1731 vs.styles[style].fore,
1732 vs.styles[style].back);
1733 x += width;
1734 i = end + 1;
1736 } else {
1737 size_t style = st.style + styleOffset;
1738 surface->DrawTextNoClip(rcText, vs.styles[style].font,
1739 rcText.top + vs.maxAscent, st.text + start,
1740 static_cast<int>(length),
1741 vs.styles[style].fore,
1742 vs.styles[style].back);
1746 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1747 if (vs.fixedColumnWidth == 0)
1748 return;
1750 PRectangle rcMargin = GetClientRectangle();
1751 rcMargin.right = vs.fixedColumnWidth;
1753 if (!rc.Intersects(rcMargin))
1754 return;
1756 Surface *surface;
1757 if (bufferedDraw) {
1758 surface = pixmapSelMargin;
1759 } else {
1760 surface = surfWindow;
1763 // Clip vertically to paint area to avoid drawing line numbers
1764 if (rcMargin.bottom > rc.bottom)
1765 rcMargin.bottom = rc.bottom;
1766 if (rcMargin.top < rc.top)
1767 rcMargin.top = rc.top;
1769 PRectangle rcSelMargin = rcMargin;
1770 rcSelMargin.right = rcMargin.left;
1772 for (int margin = 0; margin < vs.margins; margin++) {
1773 if (vs.ms[margin].width > 0) {
1775 rcSelMargin.left = rcSelMargin.right;
1776 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
1778 if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
1779 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
1780 // Required because of special way brush is created for selection margin
1781 surface->FillRectangle(rcSelMargin, *pixmapSelPattern);
1782 else {
1783 ColourDesired colour;
1784 switch (vs.ms[margin].style) {
1785 case SC_MARGIN_BACK:
1786 colour = vs.styles[STYLE_DEFAULT].back;
1787 break;
1788 case SC_MARGIN_FORE:
1789 colour = vs.styles[STYLE_DEFAULT].fore;
1790 break;
1791 default:
1792 colour = vs.styles[STYLE_LINENUMBER].back;
1793 break;
1795 surface->FillRectangle(rcSelMargin, colour);
1797 } else {
1798 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back);
1801 const int lineStartPaint = rcMargin.top / vs.lineHeight;
1802 int visibleLine = topLine + lineStartPaint;
1803 int yposScreen = lineStartPaint * vs.lineHeight;
1804 // Work out whether the top line is whitespace located after a
1805 // lessening of fold level which implies a 'fold tail' but which should not
1806 // be displayed until the last of a sequence of whitespace.
1807 bool needWhiteClosure = false;
1808 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1809 int level = pdoc->GetLevel(cs.DocFromDisplay(visibleLine));
1810 if (level & SC_FOLDLEVELWHITEFLAG) {
1811 int lineBack = cs.DocFromDisplay(visibleLine);
1812 int levelPrev = level;
1813 while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
1814 lineBack--;
1815 levelPrev = pdoc->GetLevel(lineBack);
1817 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
1818 if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK))
1819 needWhiteClosure = true;
1822 if (highlightDelimiter.isEnabled) {
1823 int lastLine = cs.DocFromDisplay(topLine + LinesOnScreen()) + 1;
1824 pdoc->GetHighlightDelimiters(highlightDelimiter, pdoc->LineFromPosition(CurrentPosition()), lastLine);
1828 // Old code does not know about new markers needed to distinguish all cases
1829 int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
1830 SC_MARKNUM_FOLDEROPEN);
1831 int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
1832 SC_MARKNUM_FOLDER);
1834 while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rcMargin.bottom) {
1836 PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed());
1837 int lineDoc = cs.DocFromDisplay(visibleLine);
1838 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
1839 bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
1840 bool lastSubLine = visibleLine == (cs.DisplayFromDoc(lineDoc + 1) - 1);
1842 int marks = pdoc->GetMark(lineDoc);
1843 if (!firstSubLine)
1844 marks = 0;
1846 bool headWithTail = false;
1848 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1849 // Decide which fold indicator should be displayed
1850 int level = pdoc->GetLevel(lineDoc);
1851 int levelNext = pdoc->GetLevel(lineDoc + 1);
1852 int levelNum = level & SC_FOLDLEVELNUMBERMASK;
1853 int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
1854 if (level & SC_FOLDLEVELHEADERFLAG) {
1855 if (firstSubLine) {
1856 if (levelNum < levelNextNum) {
1857 if (cs.GetExpanded(lineDoc)) {
1858 if (levelNum == SC_FOLDLEVELBASE)
1859 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
1860 else
1861 marks |= 1 << folderOpenMid;
1862 } else {
1863 if (levelNum == SC_FOLDLEVELBASE)
1864 marks |= 1 << SC_MARKNUM_FOLDER;
1865 else
1866 marks |= 1 << folderEnd;
1868 } else if (levelNum > SC_FOLDLEVELBASE) {
1869 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1871 } else {
1872 if (levelNum < levelNextNum) {
1873 if (cs.GetExpanded(lineDoc)) {
1874 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1875 } else if (levelNum > SC_FOLDLEVELBASE) {
1876 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1878 } else if (levelNum > SC_FOLDLEVELBASE) {
1879 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1882 needWhiteClosure = false;
1883 int firstFollowupLine = cs.DocFromDisplay(cs.DisplayFromDoc(lineDoc + 1));
1884 int firstFollowupLineLevel = pdoc->GetLevel(firstFollowupLine);
1885 int secondFollowupLineLevelNum = pdoc->GetLevel(firstFollowupLine + 1) & SC_FOLDLEVELNUMBERMASK;
1886 if (!cs.GetExpanded(lineDoc)) {
1887 if ((firstFollowupLineLevel & SC_FOLDLEVELWHITEFLAG) &&
1888 (levelNum > secondFollowupLineLevelNum))
1889 needWhiteClosure = true;
1891 if (highlightDelimiter.IsFoldBlockHighlighted(firstFollowupLine))
1892 headWithTail = true;
1894 } else if (level & SC_FOLDLEVELWHITEFLAG) {
1895 if (needWhiteClosure) {
1896 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1897 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1898 } else if (levelNextNum > SC_FOLDLEVELBASE) {
1899 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1900 needWhiteClosure = false;
1901 } else {
1902 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1903 needWhiteClosure = false;
1905 } else if (levelNum > SC_FOLDLEVELBASE) {
1906 if (levelNextNum < levelNum) {
1907 if (levelNextNum > SC_FOLDLEVELBASE) {
1908 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1909 } else {
1910 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1912 } else {
1913 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1916 } else if (levelNum > SC_FOLDLEVELBASE) {
1917 if (levelNextNum < levelNum) {
1918 needWhiteClosure = false;
1919 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1920 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1921 needWhiteClosure = true;
1922 } else if (lastSubLine) {
1923 if (levelNextNum > SC_FOLDLEVELBASE) {
1924 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1925 } else {
1926 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1928 } else {
1929 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1931 } else {
1932 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1937 marks &= vs.ms[margin].mask;
1939 PRectangle rcMarker = rcSelMargin;
1940 rcMarker.top = yposScreen;
1941 rcMarker.bottom = yposScreen + vs.lineHeight;
1942 if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
1943 if (firstSubLine) {
1944 char number[100];
1945 sprintf(number, "%d", lineDoc + 1);
1946 if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
1947 int lev = pdoc->GetLevel(lineDoc);
1948 sprintf(number, "%c%c %03X %03X",
1949 (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
1950 (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
1951 lev & SC_FOLDLEVELNUMBERMASK,
1952 lev >> 16
1955 PRectangle rcNumber = rcMarker;
1956 // Right justify
1957 XYPOSITION width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
1958 XYPOSITION xpos = rcNumber.right - width - 3;
1959 rcNumber.left = xpos;
1960 surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font,
1961 rcNumber.top + vs.maxAscent, number, istrlen(number),
1962 vs.styles[STYLE_LINENUMBER].fore,
1963 vs.styles[STYLE_LINENUMBER].back);
1964 } else if (wrapVisualFlags & SC_WRAPVISUALFLAG_MARGIN) {
1965 PRectangle rcWrapMarker = rcMarker;
1966 rcWrapMarker.right -= 3;
1967 rcWrapMarker.left = rcWrapMarker.right - vs.styles[STYLE_LINENUMBER].aveCharWidth;
1968 DrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
1970 } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
1971 if (firstSubLine) {
1972 const StyledText stMargin = pdoc->MarginStyledText(lineDoc);
1973 if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
1974 surface->FillRectangle(rcMarker,
1975 vs.styles[stMargin.StyleAt(0)+vs.marginStyleOffset].back);
1976 if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
1977 int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
1978 rcMarker.left = rcMarker.right - width - 3;
1980 DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, rcMarker.top + vs.maxAscent,
1981 stMargin, 0, stMargin.length);
1986 if (marks) {
1987 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1988 if (marks & 1) {
1989 LineMarker::typeOfFold tFold = LineMarker::undefined;
1990 if ((vs.ms[margin].mask & SC_MASK_FOLDERS) && highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) {
1991 if (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) {
1992 tFold = LineMarker::body;
1993 } else if (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) {
1994 if (firstSubLine) {
1995 tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head;
1996 } else {
1997 if (cs.GetExpanded(lineDoc) || headWithTail) {
1998 tFold = LineMarker::body;
1999 } else {
2000 tFold = LineMarker::undefined;
2003 } else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) {
2004 tFold = LineMarker::tail;
2007 vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font, tFold, vs.ms[margin].style);
2009 marks >>= 1;
2013 visibleLine++;
2014 yposScreen += vs.lineHeight;
2019 PRectangle rcBlankMargin = rcMargin;
2020 rcBlankMargin.left = rcSelMargin.right;
2021 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back);
2023 if (bufferedDraw) {
2024 surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *pixmapSelMargin);
2028 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
2029 int ydiff = (rcTab.bottom - rcTab.top) / 2;
2030 int xhead = rcTab.right - 1 - ydiff;
2031 if (xhead <= rcTab.left) {
2032 ydiff -= rcTab.left - xhead - 1;
2033 xhead = rcTab.left - 1;
2035 if ((rcTab.left + 2) < (rcTab.right - 1))
2036 surface->MoveTo(rcTab.left + 2, ymid);
2037 else
2038 surface->MoveTo(rcTab.right - 1, ymid);
2039 surface->LineTo(rcTab.right - 1, ymid);
2040 surface->LineTo(xhead, ymid - ydiff);
2041 surface->MoveTo(rcTab.right - 1, ymid);
2042 surface->LineTo(xhead, ymid + ydiff);
2045 LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
2046 int posLineStart = pdoc->LineStart(lineNumber);
2047 int posLineEnd = pdoc->LineStart(lineNumber + 1);
2048 PLATFORM_ASSERT(posLineEnd >= posLineStart);
2049 int lineCaret = pdoc->LineFromPosition(sel.MainCaret());
2050 return llc.Retrieve(lineNumber, lineCaret,
2051 posLineEnd - posLineStart, pdoc->GetStyleClock(),
2052 LinesOnScreen() + 1, pdoc->LinesTotal());
2055 bool BadUTF(const char *s, int len, int &trailBytes) {
2056 // For the rules: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
2057 if (trailBytes) {
2058 trailBytes--;
2059 return false;
2061 int utf8status = UTF8Classify(reinterpret_cast<const unsigned char *>(s), len);
2062 if (utf8status & UTF8MaskInvalid) {
2063 return true;
2064 } else {
2065 trailBytes = (utf8status & UTF8MaskWidth) - 1;
2066 return false;
2071 * Fill in the LineLayout data for the given line.
2072 * Copy the given @a line and its styles from the document into local arrays.
2073 * Also determine the x position at which each character starts.
2075 void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) {
2076 if (!ll)
2077 return;
2079 PLATFORM_ASSERT(line < pdoc->LinesTotal());
2080 PLATFORM_ASSERT(ll->chars != NULL);
2081 int posLineStart = pdoc->LineStart(line);
2082 int posLineEnd = pdoc->LineStart(line + 1);
2083 // If the line is very long, limit the treatment to a length that should fit in the viewport
2084 if (posLineEnd > (posLineStart + ll->maxLineLength)) {
2085 posLineEnd = posLineStart + ll->maxLineLength;
2087 if (ll->validity == LineLayout::llCheckTextAndStyle) {
2088 int lineLength = posLineEnd - posLineStart;
2089 if (!vstyle.viewEOL) {
2090 lineLength = pdoc->LineEnd(line) - posLineStart;
2092 if (lineLength == ll->numCharsInLine) {
2093 // See if chars, styles, indicators, are all the same
2094 bool allSame = true;
2095 const int styleMask = pdoc->stylingBitsMask;
2096 // Check base line layout
2097 char styleByte = 0;
2098 int numCharsInLine = 0;
2099 while (numCharsInLine < lineLength) {
2100 int charInDoc = numCharsInLine + posLineStart;
2101 char chDoc = pdoc->CharAt(charInDoc);
2102 styleByte = pdoc->StyleAt(charInDoc);
2103 allSame = allSame &&
2104 (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte & styleMask));
2105 allSame = allSame &&
2106 (ll->indicators[numCharsInLine] == static_cast<char>(styleByte & ~styleMask));
2107 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
2108 allSame = allSame &&
2109 (ll->chars[numCharsInLine] == chDoc);
2110 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
2111 allSame = allSame &&
2112 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
2113 else // Style::caseUpper
2114 allSame = allSame &&
2115 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
2116 numCharsInLine++;
2118 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
2119 if (allSame) {
2120 ll->validity = LineLayout::llPositions;
2121 } else {
2122 ll->validity = LineLayout::llInvalid;
2124 } else {
2125 ll->validity = LineLayout::llInvalid;
2128 if (ll->validity == LineLayout::llInvalid) {
2129 ll->widthLine = LineLayout::wrapWidthInfinite;
2130 ll->lines = 1;
2131 if (vstyle.edgeState == EDGE_BACKGROUND) {
2132 ll->edgeColumn = pdoc->FindColumn(line, theEdge);
2133 if (ll->edgeColumn >= posLineStart) {
2134 ll->edgeColumn -= posLineStart;
2136 } else {
2137 ll->edgeColumn = -1;
2140 char styleByte;
2141 const int styleMask = pdoc->stylingBitsMask;
2142 ll->styleBitsSet = 0;
2143 // Fill base line layout
2144 const int lineLength = posLineEnd - posLineStart;
2145 pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
2146 pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
2147 int numCharsBeforeEOL = pdoc->LineEnd(line) - posLineStart;
2148 const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
2149 for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
2150 styleByte = ll->styles[styleInLine];
2151 ll->styleBitsSet |= styleByte;
2152 ll->styles[styleInLine] = static_cast<char>(styleByte & styleMask);
2153 ll->indicators[styleInLine] = static_cast<char>(styleByte & ~styleMask);
2155 styleByte = static_cast<char>(((lineLength > 0) ? ll->styles[lineLength-1] : 0) & styleMask);
2156 if (vstyle.someStylesForceCase) {
2157 for (int charInLine = 0; charInLine<lineLength; charInLine++) {
2158 char chDoc = ll->chars[charInLine];
2159 if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
2160 ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
2161 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
2162 ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
2165 ll->xHighlightGuide = 0;
2166 // Extra element at the end of the line to hold end x position and act as
2167 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
2168 ll->styles[numCharsInLine] = styleByte; // For eolFilled
2169 ll->indicators[numCharsInLine] = 0;
2171 // Layout the line, determining the position of each character,
2172 // with an extra element at the end for the end of the line.
2173 int startseg = 0; // Start of the current segment, in char. number
2174 XYACCUMULATOR startsegx = 0; // Start of the current segment, in pixels
2175 ll->positions[0] = 0;
2176 XYPOSITION tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
2177 bool lastSegItalics = false;
2178 Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
2180 XYPOSITION ctrlCharWidth[32] = {0};
2181 bool isControlNext = IsControlCharacter(ll->chars[0]);
2182 int trailBytes = 0;
2183 bool isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars, numCharsInLine, trailBytes);
2184 for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
2185 bool isControl = isControlNext;
2186 isControlNext = IsControlCharacter(ll->chars[charInLine + 1]);
2187 bool isBadUTF = isBadUTFNext;
2188 isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars + charInLine + 1, numCharsInLine - charInLine - 1, trailBytes);
2189 if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) ||
2190 isControl || isControlNext || isBadUTF || isBadUTFNext) {
2191 ll->positions[startseg] = 0;
2192 if (vstyle.styles[ll->styles[charInLine]].visible) {
2193 if (isControl) {
2194 if (ll->chars[charInLine] == '\t') {
2195 ll->positions[charInLine + 1] =
2196 ((static_cast<int>((startsegx + 2) / tabWidth) + 1) * tabWidth) - startsegx;
2197 } else if (controlCharSymbol < 32) {
2198 if (ctrlCharWidth[ll->chars[charInLine]] == 0) {
2199 const char *ctrlChar = ControlCharacterString(ll->chars[charInLine]);
2200 // +3 For a blank on front and rounded edge each side:
2201 ctrlCharWidth[ll->chars[charInLine]] =
2202 surface->WidthText(ctrlCharsFont, ctrlChar, istrlen(ctrlChar)) + 3;
2204 ll->positions[charInLine + 1] = ctrlCharWidth[ll->chars[charInLine]];
2205 } else {
2206 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
2207 surface->MeasureWidths(ctrlCharsFont, cc, 1,
2208 ll->positions + startseg + 1);
2210 lastSegItalics = false;
2211 } else if (isBadUTF) {
2212 char hexits[4];
2213 sprintf(hexits, "x%2X", ll->chars[charInLine] & 0xff);
2214 ll->positions[charInLine + 1] =
2215 surface->WidthText(ctrlCharsFont, hexits, istrlen(hexits)) + 3;
2216 } else { // Regular character
2217 int lenSeg = charInLine - startseg + 1;
2218 if ((lenSeg == 1) && (' ' == ll->chars[startseg])) {
2219 lastSegItalics = false;
2220 // Over half the segments are single characters and of these about half are space characters.
2221 ll->positions[charInLine + 1] = vstyle.styles[ll->styles[charInLine]].spaceWidth;
2222 } else {
2223 lastSegItalics = vstyle.styles[ll->styles[charInLine]].italic;
2224 posCache.MeasureWidths(surface, vstyle, ll->styles[charInLine], ll->chars + startseg,
2225 lenSeg, ll->positions + startseg + 1, pdoc);
2228 } else { // invisible
2229 for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
2230 ll->positions[posToZero] = 0;
2233 for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
2234 ll->positions[posToIncrease] += startsegx;
2236 startsegx = ll->positions[charInLine + 1];
2237 startseg = charInLine + 1;
2240 // Small hack to make lines that end with italics not cut off the edge of the last character
2241 if ((startseg > 0) && lastSegItalics) {
2242 ll->positions[startseg] += 2;
2244 ll->numCharsInLine = numCharsInLine;
2245 ll->numCharsBeforeEOL = numCharsBeforeEOL;
2246 ll->validity = LineLayout::llPositions;
2248 // Hard to cope when too narrow, so just assume there is space
2249 if (width < 20) {
2250 width = 20;
2252 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
2253 ll->widthLine = width;
2254 if (width == LineLayout::wrapWidthInfinite) {
2255 ll->lines = 1;
2256 } else if (width > ll->positions[ll->numCharsInLine]) {
2257 // Simple common case where line does not need wrapping.
2258 ll->lines = 1;
2259 } else {
2260 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2261 width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
2263 XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
2264 if (wrapIndentMode == SC_WRAPINDENT_INDENT) {
2265 wrapAddIndent = pdoc->IndentSize() * vstyle.spaceWidth;
2266 } else if (wrapIndentMode == SC_WRAPINDENT_FIXED) {
2267 wrapAddIndent = wrapVisualStartIndent * vstyle.aveCharWidth;
2269 ll->wrapIndent = wrapAddIndent;
2270 if (wrapIndentMode != SC_WRAPINDENT_FIXED)
2271 for (int i = 0; i < ll->numCharsInLine; i++) {
2272 if (!IsSpaceOrTab(ll->chars[i])) {
2273 ll->wrapIndent += ll->positions[i]; // Add line indent
2274 break;
2277 // Check for text width minimum
2278 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
2279 ll->wrapIndent = wrapAddIndent;
2280 // Check for wrapIndent minimum
2281 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
2282 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
2283 ll->lines = 0;
2284 // Calculate line start positions based upon width.
2285 int lastGoodBreak = 0;
2286 int lastLineStart = 0;
2287 XYACCUMULATOR startOffset = 0;
2288 int p = 0;
2289 while (p < ll->numCharsInLine) {
2290 if ((ll->positions[p + 1] - startOffset) >= width) {
2291 if (lastGoodBreak == lastLineStart) {
2292 // Try moving to start of last character
2293 if (p > 0) {
2294 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2295 - posLineStart;
2297 if (lastGoodBreak == lastLineStart) {
2298 // Ensure at least one character on line.
2299 lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
2300 - posLineStart;
2303 lastLineStart = lastGoodBreak;
2304 ll->lines++;
2305 ll->SetLineStart(ll->lines, lastGoodBreak);
2306 startOffset = ll->positions[lastGoodBreak];
2307 // take into account the space for start wrap mark and indent
2308 startOffset -= ll->wrapIndent;
2309 p = lastGoodBreak + 1;
2310 continue;
2312 if (p > 0) {
2313 if (wrapState == eWrapChar) {
2314 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2315 - posLineStart;
2316 p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
2317 continue;
2318 } else if (ll->styles[p] != ll->styles[p - 1]) {
2319 lastGoodBreak = p;
2320 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
2321 lastGoodBreak = p;
2324 p++;
2326 ll->lines++;
2328 ll->validity = LineLayout::llLines;
2332 ColourDesired Editor::SelectionBackground(ViewStyle &vsDraw, bool main) {
2333 return main ?
2334 (primarySelection ? vsDraw.selbackground : vsDraw.selbackground2) :
2335 vsDraw.selAdditionalBackground;
2338 ColourDesired Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
2339 ColourDesired background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) {
2340 if (inSelection == 1) {
2341 if (vsDraw.selbackset && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
2342 return SelectionBackground(vsDraw, true);
2344 } else if (inSelection == 2) {
2345 if (vsDraw.selbackset && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
2346 return SelectionBackground(vsDraw, false);
2348 } else {
2349 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
2350 (i >= ll->edgeColumn) &&
2351 (i < ll->numCharsBeforeEOL))
2352 return vsDraw.edgecolour;
2353 if (inHotspot && vsDraw.hotspotBackgroundSet)
2354 return vsDraw.hotspotBackground;
2356 if (overrideBackground && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
2357 return background;
2358 } else {
2359 return vsDraw.styles[styleMain].back;
2363 void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
2364 Point from(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
2365 PRectangle rcCopyArea(start + 1, rcSegment.top, start + 2, rcSegment.bottom);
2366 surface->Copy(rcCopyArea, from,
2367 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
2370 void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
2371 bool isEndMarker, ColourDesired wrapColour) {
2372 surface->PenColour(wrapColour);
2374 enum { xa = 1 }; // gap before start
2375 int w = rcPlace.right - rcPlace.left - xa - 1;
2377 bool xStraight = isEndMarker; // x-mirrored symbol for start marker
2378 bool yStraight = true;
2379 //bool yStraight= isEndMarker; // comment in for start marker y-mirrowed
2381 int x0 = xStraight ? rcPlace.left : rcPlace.right - 1;
2382 int y0 = yStraight ? rcPlace.top : rcPlace.bottom - 1;
2384 int dy = (rcPlace.bottom - rcPlace.top) / 5;
2385 int y = (rcPlace.bottom - rcPlace.top) / 2 + dy;
2387 struct Relative {
2388 Surface *surface;
2389 int xBase;
2390 int xDir;
2391 int yBase;
2392 int yDir;
2393 void MoveTo(int xRelative, int yRelative) {
2394 surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2396 void LineTo(int xRelative, int yRelative) {
2397 surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2400 Relative rel = {surface, x0, xStraight ? 1 : -1, y0, yStraight ? 1 : -1};
2402 // arrow head
2403 rel.MoveTo(xa, y);
2404 rel.LineTo(xa + 2*w / 3, y - dy);
2405 rel.MoveTo(xa, y);
2406 rel.LineTo(xa + 2*w / 3, y + dy);
2408 // arrow body
2409 rel.MoveTo(xa, y);
2410 rel.LineTo(xa + w, y);
2411 rel.LineTo(xa + w, y - 2 * dy);
2412 rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
2413 y - 2 * dy);
2416 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
2417 if (alpha != SC_ALPHA_NOALPHA) {
2418 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
2422 void DrawTextBlob(Surface *surface, ViewStyle &vsDraw, PRectangle rcSegment,
2423 const char *s, ColourDesired textBack, ColourDesired textFore, bool twoPhaseDraw) {
2424 if (!twoPhaseDraw) {
2425 surface->FillRectangle(rcSegment, textBack);
2427 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2428 int normalCharHeight = surface->Ascent(ctrlCharsFont) -
2429 surface->InternalLeading(ctrlCharsFont);
2430 PRectangle rcCChar = rcSegment;
2431 rcCChar.left = rcCChar.left + 1;
2432 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
2433 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
2434 PRectangle rcCentral = rcCChar;
2435 rcCentral.top++;
2436 rcCentral.bottom--;
2437 surface->FillRectangle(rcCentral, textFore);
2438 PRectangle rcChar = rcCChar;
2439 rcChar.left++;
2440 rcChar.right--;
2441 surface->DrawTextClipped(rcChar, ctrlCharsFont,
2442 rcSegment.top + vsDraw.maxAscent, s, istrlen(s),
2443 textBack, textFore);
2446 void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
2447 int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
2448 bool overrideBackground, ColourDesired background,
2449 bool drawWrapMarkEnd, ColourDesired wrapColour) {
2451 const int posLineStart = pdoc->LineStart(line);
2452 const int styleMask = pdoc->stylingBitsMask;
2453 PRectangle rcSegment = rcLine;
2455 const bool lastSubLine = subLine == (ll->lines - 1);
2456 XYPOSITION virtualSpace = 0;
2457 if (lastSubLine) {
2458 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
2459 virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * spaceWidth;
2461 XYPOSITION xEol = ll->positions[lineEnd] - subLineStart;
2463 // Fill the virtual space and show selections within it
2464 if (virtualSpace) {
2465 rcSegment.left = xEol + xStart;
2466 rcSegment.right = xEol + xStart + virtualSpace;
2467 surface->FillRectangle(rcSegment, overrideBackground ? background : vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2468 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
2469 SelectionSegment virtualSpaceRange(SelectionPosition(pdoc->LineEnd(line)), SelectionPosition(pdoc->LineEnd(line), sel.VirtualSpaceFor(pdoc->LineEnd(line))));
2470 for (size_t r=0; r<sel.Count(); r++) {
2471 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2472 if (alpha == SC_ALPHA_NOALPHA) {
2473 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
2474 if (!portion.Empty()) {
2475 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
2476 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
2477 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
2478 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
2479 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
2480 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == sel.Main()));
2487 int eolInSelection = 0;
2488 int alpha = SC_ALPHA_NOALPHA;
2489 if (!hideSelection) {
2490 int posAfterLineEnd = pdoc->LineStart(line + 1);
2491 eolInSelection = (subLine == (ll->lines - 1)) ? sel.InSelectionForEOL(posAfterLineEnd) : 0;
2492 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2495 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
2496 XYPOSITION blobsWidth = 0;
2497 if (lastSubLine) {
2498 for (int eolPos=ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
2499 rcSegment.left = xStart + ll->positions[eolPos] - subLineStart + virtualSpace;
2500 rcSegment.right = xStart + ll->positions[eolPos+1] - subLineStart + virtualSpace;
2501 blobsWidth += rcSegment.Width();
2502 const char *ctrlChar = ControlCharacterString(ll->chars[eolPos]);
2503 int styleMain = ll->styles[eolPos];
2504 ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, eolInSelection, false, styleMain, eolPos, ll);
2505 ColourDesired textFore = vsDraw.styles[styleMain].fore;
2506 if (eolInSelection && vsDraw.selforeset) {
2507 textFore = (eolInSelection == 1) ? vsDraw.selforeground : vsDraw.selAdditionalForeground;
2509 if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1)) {
2510 if (alpha == SC_ALPHA_NOALPHA) {
2511 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2512 } else {
2513 surface->FillRectangle(rcSegment, textBack);
2515 } else {
2516 surface->FillRectangle(rcSegment, textBack);
2518 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
2519 if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2520 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2525 // Draw the eol-is-selected rectangle
2526 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
2527 rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
2529 if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2530 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2531 } else {
2532 if (overrideBackground) {
2533 surface->FillRectangle(rcSegment, background);
2534 } else if (line < pdoc->LinesTotal() - 1) {
2535 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2536 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2537 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2538 } else {
2539 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
2541 if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2542 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2546 // Fill the remainder of the line
2547 rcSegment.left = rcSegment.right;
2548 if (rcSegment.left < rcLine.left)
2549 rcSegment.left = rcLine.left;
2550 rcSegment.right = rcLine.right;
2552 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2553 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2554 } else {
2555 if (overrideBackground) {
2556 surface->FillRectangle(rcSegment, background);
2557 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2558 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2559 } else {
2560 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
2562 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2563 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2567 if (drawWrapMarkEnd) {
2568 PRectangle rcPlace = rcSegment;
2570 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
2571 rcPlace.left = xEol + xStart + virtualSpace;
2572 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2573 } else {
2574 // rcLine is clipped to text area
2575 rcPlace.right = rcLine.right;
2576 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2578 DrawWrapMarker(surface, rcPlace, true, wrapColour);
2582 void Editor::DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, ViewStyle &vsDraw,
2583 int xStart, PRectangle rcLine, LineLayout *ll, int subLine) {
2584 const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
2585 PRectangle rcIndic(
2586 ll->positions[startPos] + xStart - subLineStart,
2587 rcLine.top + vsDraw.maxAscent,
2588 ll->positions[endPos] + xStart - subLineStart,
2589 rcLine.top + vsDraw.maxAscent + 3);
2590 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine);
2593 void Editor::DrawIndicators(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2594 PRectangle rcLine, LineLayout *ll, int subLine, int lineEnd, bool under) {
2595 // Draw decorators
2596 const int posLineStart = pdoc->LineStart(line);
2597 const int lineStart = ll->LineStart(subLine);
2598 const int posLineEnd = posLineStart + lineEnd;
2600 if (!under) {
2601 // Draw indicators
2602 // foreach indicator...
2603 for (int indicnum = 0, mask = 1 << pdoc->stylingBits; mask < 0x100; indicnum++) {
2604 if (!(mask & ll->styleBitsSet)) {
2605 mask <<= 1;
2606 continue;
2608 int startPos = -1;
2609 // foreach style pos in line...
2610 for (int indicPos = lineStart; indicPos <= lineEnd; indicPos++) {
2611 // look for starts...
2612 if (startPos < 0) {
2613 // NOT in indicator run, looking for START
2614 if (indicPos < lineEnd && (ll->indicators[indicPos] & mask))
2615 startPos = indicPos;
2617 // ... or ends
2618 if (startPos >= 0) {
2619 // IN indicator run, looking for END
2620 if (indicPos >= lineEnd || !(ll->indicators[indicPos] & mask)) {
2621 // AT end of indicator run, DRAW it!
2622 DrawIndicator(indicnum, startPos, indicPos, surface, vsDraw, xStart, rcLine, ll, subLine);
2623 // RESET control var
2624 startPos = -1;
2628 mask <<= 1;
2632 for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
2633 if (under == vsDraw.indicators[deco->indicator].under) {
2634 int startPos = posLineStart + lineStart;
2635 if (!deco->rs.ValueAt(startPos)) {
2636 startPos = deco->rs.EndRun(startPos);
2638 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
2639 int endPos = deco->rs.EndRun(startPos);
2640 if (endPos > posLineEnd)
2641 endPos = posLineEnd;
2642 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
2643 surface, vsDraw, xStart, rcLine, ll, subLine);
2644 startPos = deco->rs.EndRun(endPos);
2649 // Use indicators to highlight matching braces
2650 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
2651 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
2652 int braceIndicator = (bracesMatchStyle == STYLE_BRACELIGHT) ? vs.braceHighlightIndicator : vs.braceBadLightIndicator;
2653 if (under == vsDraw.indicators[braceIndicator].under) {
2654 Range rangeLine(posLineStart + lineStart, posLineEnd);
2655 if (rangeLine.ContainsCharacter(braces[0])) {
2656 int braceOffset = braces[0] - posLineStart;
2657 if (braceOffset < ll->numCharsInLine) {
2658 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2661 if (rangeLine.ContainsCharacter(braces[1])) {
2662 int braceOffset = braces[1] - posLineStart;
2663 if (braceOffset < ll->numCharsInLine) {
2664 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2671 void Editor::DrawAnnotation(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2672 PRectangle rcLine, LineLayout *ll, int subLine) {
2673 int indent = pdoc->GetLineIndentation(line) * vsDraw.spaceWidth;
2674 PRectangle rcSegment = rcLine;
2675 int annotationLine = subLine - ll->lines;
2676 const StyledText stAnnotation = pdoc->AnnotationStyledText(line);
2677 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
2678 surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
2679 if (vs.annotationVisible == ANNOTATION_BOXED) {
2680 // Only care about calculating width if need to draw box
2681 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
2682 widthAnnotation += vsDraw.spaceWidth * 2; // Margins
2683 rcSegment.left = xStart + indent;
2684 rcSegment.right = rcSegment.left + widthAnnotation;
2685 } else {
2686 rcSegment.left = xStart;
2688 const int annotationLines = pdoc->AnnotationLines(line);
2689 size_t start = 0;
2690 size_t lengthAnnotation = stAnnotation.LineLength(start);
2691 int lineInAnnotation = 0;
2692 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
2693 start += lengthAnnotation + 1;
2694 lengthAnnotation = stAnnotation.LineLength(start);
2695 lineInAnnotation++;
2697 PRectangle rcText = rcSegment;
2698 if (vs.annotationVisible == ANNOTATION_BOXED) {
2699 surface->FillRectangle(rcText,
2700 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
2701 rcText.left += vsDraw.spaceWidth;
2703 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText, rcText.top + vsDraw.maxAscent,
2704 stAnnotation, start, lengthAnnotation);
2705 if (vs.annotationVisible == ANNOTATION_BOXED) {
2706 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
2707 surface->MoveTo(rcSegment.left, rcSegment.top);
2708 surface->LineTo(rcSegment.left, rcSegment.bottom);
2709 surface->MoveTo(rcSegment.right, rcSegment.top);
2710 surface->LineTo(rcSegment.right, rcSegment.bottom);
2711 if (subLine == ll->lines) {
2712 surface->MoveTo(rcSegment.left, rcSegment.top);
2713 surface->LineTo(rcSegment.right, rcSegment.top);
2715 if (subLine == ll->lines+annotationLines-1) {
2716 surface->MoveTo(rcSegment.left, rcSegment.bottom - 1);
2717 surface->LineTo(rcSegment.right, rcSegment.bottom - 1);
2723 void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
2724 PRectangle rcLine, LineLayout *ll, int subLine) {
2726 PRectangle rcSegment = rcLine;
2728 // Using one font for all control characters so it can be controlled independently to ensure
2729 // the box goes around the characters tightly. Seems to be no way to work out what height
2730 // is taken by an individual character - internal leading gives varying results.
2731 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2733 // See if something overrides the line background color: Either if caret is on the line
2734 // and background color is set for that, or if a marker is defined that forces its background
2735 // color onto the line, or if a marker is defined but has no selection margin in which to
2736 // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
2737 // with the earlier taking precedence. When multiple markers cause background override,
2738 // the color for the highest numbered one is used.
2739 bool overrideBackground = false;
2740 ColourDesired background;
2741 if (caret.active && vsDraw.showCaretLineBackground && (vsDraw.caretLineAlpha == SC_ALPHA_NOALPHA) && ll->containsCaret) {
2742 overrideBackground = true;
2743 background = vsDraw.caretLineBackground;
2745 if (!overrideBackground) {
2746 int marks = pdoc->GetMark(line);
2747 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
2748 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) &&
2749 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2750 background = vsDraw.markers[markBit].back;
2751 overrideBackground = true;
2753 marks >>= 1;
2756 if (!overrideBackground) {
2757 if (vsDraw.maskInLine) {
2758 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
2759 if (marksMasked) {
2760 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
2761 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY) &&
2762 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2763 overrideBackground = true;
2764 background = vsDraw.markers[markBit].back;
2766 marksMasked >>= 1;
2772 bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
2773 (!overrideBackground) && (vsDraw.whitespaceBackgroundSet);
2775 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2776 const XYPOSITION indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
2777 const XYPOSITION epsilon = 0.0001f; // A small nudge to avoid floating point precision issues
2779 int posLineStart = pdoc->LineStart(line);
2781 int startseg = ll->LineStart(subLine);
2782 XYACCUMULATOR subLineStart = ll->positions[startseg];
2783 if (subLine >= ll->lines) {
2784 DrawAnnotation(surface, vsDraw, line, xStart, rcLine, ll, subLine);
2785 return; // No further drawing
2787 int lineStart = 0;
2788 int lineEnd = 0;
2789 if (subLine < ll->lines) {
2790 lineStart = ll->LineStart(subLine);
2791 lineEnd = ll->LineStart(subLine + 1);
2792 if (subLine == ll->lines - 1) {
2793 lineEnd = ll->numCharsBeforeEOL;
2797 ColourDesired wrapColour = vsDraw.styles[STYLE_DEFAULT].fore;
2798 if (vsDraw.whitespaceForegroundSet)
2799 wrapColour = vsDraw.whitespaceForeground;
2801 bool drawWrapMarkEnd = false;
2803 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2804 if (subLine + 1 < ll->lines) {
2805 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
2809 if (ll->wrapIndent != 0) {
2811 bool continuedWrapLine = false;
2812 if (subLine < ll->lines) {
2813 continuedWrapLine = ll->LineStart(subLine) != 0;
2816 if (continuedWrapLine) {
2817 // draw continuation rect
2818 PRectangle rcPlace = rcSegment;
2820 rcPlace.left = ll->positions[startseg] + xStart - subLineStart;
2821 rcPlace.right = rcPlace.left + ll->wrapIndent;
2823 // default bgnd here..
2824 surface->FillRectangle(rcSegment, overrideBackground ? background :
2825 vsDraw.styles[STYLE_DEFAULT].back);
2827 // main line style would be below but this would be inconsistent with end markers
2828 // also would possibly not be the style at wrap point
2829 //int styleMain = ll->styles[lineStart];
2830 //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back);
2832 if (wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
2834 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
2835 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2836 else
2837 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2839 DrawWrapMarker(surface, rcPlace, false, wrapColour);
2842 xStart += static_cast<int>(ll->wrapIndent);
2846 bool selBackDrawn = vsDraw.selbackset &&
2847 ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA));
2849 // Does not take margin into account but not significant
2850 int xStartVisible = static_cast<int>(subLineStart) - xStart;
2852 ll->psel = &sel;
2854 BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, xStartVisible, selBackDrawn, pdoc);
2855 int next = bfBack.First();
2857 // Background drawing loop
2858 while (twoPhaseDraw && (next < lineEnd)) {
2860 startseg = next;
2861 next = bfBack.Next();
2862 int i = next - 1;
2863 int iDoc = i + posLineStart;
2865 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2866 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2867 // Only try to draw if really visible - enhances performance by not calling environment to
2868 // draw strings that are completely past the right side of the window.
2869 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2870 // Clip to line rectangle, since may have a huge position which will not work with some platforms
2871 if (rcSegment.left < rcLine.left)
2872 rcSegment.left = rcLine.left;
2873 if (rcSegment.right > rcLine.right)
2874 rcSegment.right = rcLine.right;
2876 int styleMain = ll->styles[i];
2877 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2878 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2879 ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2880 if (ll->chars[i] == '\t') {
2881 // Tab display
2882 if (drawWhitespaceBackground &&
2883 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2884 textBack = vsDraw.whitespaceBackground;
2885 surface->FillRectangle(rcSegment, textBack);
2886 } else if (IsControlCharacter(ll->chars[i])) {
2887 // Control character display
2888 inIndentation = false;
2889 surface->FillRectangle(rcSegment, textBack);
2890 } else {
2891 // Normal text display
2892 surface->FillRectangle(rcSegment, textBack);
2893 if (vsDraw.viewWhitespace != wsInvisible ||
2894 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
2895 for (int cpos = 0; cpos <= i - startseg; cpos++) {
2896 if (ll->chars[cpos + startseg] == ' ') {
2897 if (drawWhitespaceBackground &&
2898 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
2899 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
2900 rcSegment.top,
2901 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
2902 rcSegment.bottom);
2903 surface->FillRectangle(rcSpace, vsDraw.whitespaceBackground);
2905 } else {
2906 inIndentation = false;
2911 } else if (rcSegment.left > rcLine.right) {
2912 break;
2916 if (twoPhaseDraw) {
2917 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
2918 xStart, subLine, subLineStart, overrideBackground, background,
2919 drawWrapMarkEnd, wrapColour);
2922 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, true);
2924 if (vsDraw.edgeState == EDGE_LINE) {
2925 int edgeX = theEdge * vsDraw.spaceWidth;
2926 rcSegment.left = edgeX + xStart;
2927 if ((ll->wrapIndent != 0) && (lineStart != 0))
2928 rcSegment.left -= ll->wrapIndent;
2929 rcSegment.right = rcSegment.left + 1;
2930 surface->FillRectangle(rcSegment, vsDraw.edgecolour);
2933 // Draw underline mark as part of background if not transparent
2934 int marks = pdoc->GetMark(line);
2935 int markBit;
2936 for (markBit = 0; (markBit < 32) && marks; markBit++) {
2937 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
2938 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2939 PRectangle rcUnderline = rcLine;
2940 rcUnderline.top = rcUnderline.bottom - 2;
2941 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
2943 marks >>= 1;
2946 inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2947 // Foreground drawing loop
2948 BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, xStartVisible,
2949 ((!twoPhaseDraw && selBackDrawn) || vsDraw.selforeset), pdoc);
2950 next = bfFore.First();
2952 while (next < lineEnd) {
2954 startseg = next;
2955 next = bfFore.Next();
2956 int i = next - 1;
2958 int iDoc = i + posLineStart;
2960 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2961 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2962 // Only try to draw if really visible - enhances performance by not calling environment to
2963 // draw strings that are completely past the right side of the window.
2964 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2965 int styleMain = ll->styles[i];
2966 ColourDesired textFore = vsDraw.styles[styleMain].fore;
2967 Font &textFont = vsDraw.styles[styleMain].font;
2968 //hotspot foreground
2969 if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) {
2970 if (vsDraw.hotspotForegroundSet)
2971 textFore = vsDraw.hotspotForeground;
2973 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2974 if (inSelection && (vsDraw.selforeset)) {
2975 textFore = (inSelection == 1) ? vsDraw.selforeground : vsDraw.selAdditionalForeground;
2977 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2978 ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2979 if (ll->chars[i] == '\t') {
2980 // Tab display
2981 if (!twoPhaseDraw) {
2982 if (drawWhitespaceBackground &&
2983 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2984 textBack = vsDraw.whitespaceBackground;
2985 surface->FillRectangle(rcSegment, textBack);
2987 if ((vsDraw.viewWhitespace != wsInvisible) ||
2988 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
2989 if (vsDraw.whitespaceForegroundSet)
2990 textFore = vsDraw.whitespaceForeground;
2991 surface->PenColour(textFore);
2993 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
2994 for (int indentCount = (ll->positions[i] + epsilon) / indentWidth;
2995 indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
2996 indentCount++) {
2997 if (indentCount > 0) {
2998 int xIndent = indentCount * indentWidth;
2999 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3000 (ll->xHighlightGuide == xIndent));
3004 if (vsDraw.viewWhitespace != wsInvisible) {
3005 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3006 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
3007 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
3008 DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
3011 } else if (IsControlCharacter(ll->chars[i])) {
3012 // Control character display
3013 inIndentation = false;
3014 if (controlCharSymbol < 32) {
3015 // Draw the character
3016 const char *ctrlChar = ControlCharacterString(ll->chars[i]);
3017 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
3018 } else {
3019 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
3020 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
3021 rcSegment.top + vsDraw.maxAscent,
3022 cc, 1, textBack, textFore);
3024 } else if ((i == startseg) && (static_cast<unsigned char>(ll->chars[i]) >= 0x80) && IsUnicodeMode()) {
3025 // A single byte >= 0x80 in UTF-8 is a bad byte and is displayed as its hex value
3026 char hexits[4];
3027 sprintf(hexits, "x%2X", ll->chars[i] & 0xff);
3028 DrawTextBlob(surface, vsDraw, rcSegment, hexits, textBack, textFore, twoPhaseDraw);
3029 } else {
3030 // Normal text display
3031 if (vsDraw.styles[styleMain].visible) {
3032 if (twoPhaseDraw) {
3033 surface->DrawTextTransparent(rcSegment, textFont,
3034 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
3035 i - startseg + 1, textFore);
3036 } else {
3037 surface->DrawTextNoClip(rcSegment, textFont,
3038 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
3039 i - startseg + 1, textFore, textBack);
3042 if (vsDraw.viewWhitespace != wsInvisible ||
3043 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
3044 for (int cpos = 0; cpos <= i - startseg; cpos++) {
3045 if (ll->chars[cpos + startseg] == ' ') {
3046 if (vsDraw.viewWhitespace != wsInvisible) {
3047 if (vsDraw.whitespaceForegroundSet)
3048 textFore = vsDraw.whitespaceForeground;
3049 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3050 XYPOSITION xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2;
3051 if (!twoPhaseDraw && drawWhitespaceBackground &&
3052 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
3053 textBack = vsDraw.whitespaceBackground;
3054 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
3055 rcSegment.top,
3056 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
3057 rcSegment.bottom);
3058 surface->FillRectangle(rcSpace, textBack);
3060 PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
3061 rcDot.right = rcDot.left + vs.whitespaceSize;
3062 rcDot.bottom = rcDot.top + vs.whitespaceSize;
3063 surface->FillRectangle(rcDot, textFore);
3066 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
3067 for (int indentCount = (ll->positions[cpos + startseg] + epsilon) / indentWidth;
3068 indentCount <= (ll->positions[cpos + startseg + 1] - epsilon) / indentWidth;
3069 indentCount++) {
3070 if (indentCount > 0) {
3071 int xIndent = indentCount * indentWidth;
3072 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3073 (ll->xHighlightGuide == xIndent));
3077 } else {
3078 inIndentation = false;
3083 if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd) {
3084 PRectangle rcUL = rcSegment;
3085 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3086 rcUL.bottom = rcUL.top + 1;
3087 if (vsDraw.hotspotForegroundSet)
3088 surface->FillRectangle(rcUL, vsDraw.hotspotForeground);
3089 else
3090 surface->FillRectangle(rcUL, textFore);
3091 } else if (vsDraw.styles[styleMain].underline) {
3092 PRectangle rcUL = rcSegment;
3093 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3094 rcUL.bottom = rcUL.top + 1;
3095 surface->FillRectangle(rcUL, textFore);
3097 } else if (rcSegment.left > rcLine.right) {
3098 break;
3101 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
3102 && (subLine == 0)) {
3103 int indentSpace = pdoc->GetLineIndentation(line);
3104 int xStartText = ll->positions[pdoc->GetLineIndentPosition(line) - posLineStart];
3106 // Find the most recent line with some text
3108 int lineLastWithText = line;
3109 while (lineLastWithText > Platform::Maximum(line-20, 0) && pdoc->IsWhiteLine(lineLastWithText)) {
3110 lineLastWithText--;
3112 if (lineLastWithText < line) {
3113 xStartText = 100000; // Don't limit to visible indentation on empty line
3114 // This line is empty, so use indentation of last line with text
3115 int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText);
3116 int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
3117 if (isFoldHeader) {
3118 // Level is one more level than parent
3119 indentLastWithText += pdoc->IndentSize();
3121 if (vsDraw.viewIndentationGuides == ivLookForward) {
3122 // In viLookForward mode, previous line only used if it is a fold header
3123 if (isFoldHeader) {
3124 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3126 } else { // viLookBoth
3127 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3131 int lineNextWithText = line;
3132 while (lineNextWithText < Platform::Minimum(line+20, pdoc->LinesTotal()) && pdoc->IsWhiteLine(lineNextWithText)) {
3133 lineNextWithText++;
3135 if (lineNextWithText > line) {
3136 xStartText = 100000; // Don't limit to visible indentation on empty line
3137 // This line is empty, so use indentation of first next line with text
3138 indentSpace = Platform::Maximum(indentSpace,
3139 pdoc->GetLineIndentation(lineNextWithText));
3142 for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) {
3143 int xIndent = indentPos * vsDraw.spaceWidth;
3144 if (xIndent < xStartText) {
3145 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3146 (ll->xHighlightGuide == xIndent));
3151 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, false);
3153 // End of the drawing of the current line
3154 if (!twoPhaseDraw) {
3155 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
3156 xStart, subLine, subLineStart, overrideBackground, background,
3157 drawWrapMarkEnd, wrapColour);
3159 if (!hideSelection && ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA))) {
3160 // For each selection draw
3161 int virtualSpaces = 0;
3162 if (subLine == (ll->lines - 1)) {
3163 virtualSpaces = sel.VirtualSpaceFor(pdoc->LineEnd(line));
3165 SelectionPosition posStart(posLineStart + lineStart);
3166 SelectionPosition posEnd(posLineStart + lineEnd, virtualSpaces);
3167 SelectionSegment virtualSpaceRange(posStart, posEnd);
3168 for (size_t r=0; r<sel.Count(); r++) {
3169 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
3170 if (alpha != SC_ALPHA_NOALPHA) {
3171 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
3172 if (!portion.Empty()) {
3173 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
3174 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
3175 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
3176 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3177 if ((portion.start.Position() - posLineStart) == lineStart && sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
3178 rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
3180 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
3181 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
3182 if (rcSegment.right > rcLine.left)
3183 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == sel.Main()), alpha);
3189 // Draw any translucent whole line states
3190 rcSegment = rcLine;
3191 if (caret.active && vsDraw.showCaretLineBackground && ll->containsCaret) {
3192 SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
3194 marks = pdoc->GetMark(line);
3195 for (markBit = 0; (markBit < 32) && marks; markBit++) {
3196 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
3197 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3198 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
3199 PRectangle rcUnderline = rcSegment;
3200 rcUnderline.top = rcUnderline.bottom - 2;
3201 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3203 marks >>= 1;
3205 if (vsDraw.maskInLine) {
3206 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
3207 if (marksMasked) {
3208 for (markBit = 0; (markBit < 32) && marksMasked; markBit++) {
3209 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
3210 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3212 marksMasked >>= 1;
3218 void Editor::DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine,
3219 int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) {
3221 int lineStart = ll->LineStart(subLine);
3222 int posBefore = posCaret;
3223 int posAfter = MovePositionOutsideChar(posCaret + 1, 1);
3224 int numCharsToDraw = posAfter - posCaret;
3226 // Work out where the starting and ending offsets are. We need to
3227 // see if the previous character shares horizontal space, such as a
3228 // glyph / combining character. If so we'll need to draw that too.
3229 int offsetFirstChar = offset;
3230 int offsetLastChar = offset + (posAfter - posCaret);
3231 while ((offsetLastChar - numCharsToDraw) >= lineStart) {
3232 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
3233 // The char does not share horizontal space
3234 break;
3236 // Char shares horizontal space, update the numChars to draw
3237 // Update posBefore to point to the prev char
3238 posBefore = MovePositionOutsideChar(posBefore - 1, -1);
3239 numCharsToDraw = posAfter - posBefore;
3240 offsetFirstChar = offset - (posCaret - posBefore);
3243 // See if the next character shares horizontal space, if so we'll
3244 // need to draw that too.
3245 numCharsToDraw = offsetLastChar - offsetFirstChar;
3246 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
3247 // Update posAfter to point to the 2nd next char, this is where
3248 // the next character ends, and 2nd next begins. We'll need
3249 // to compare these two
3250 posBefore = posAfter;
3251 posAfter = MovePositionOutsideChar(posAfter + 1, 1);
3252 offsetLastChar = offset + (posAfter - posCaret);
3253 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
3254 // The char does not share horizontal space
3255 break;
3257 // Char shares horizontal space, update the numChars to draw
3258 numCharsToDraw = offsetLastChar - offsetFirstChar;
3261 // We now know what to draw, update the caret drawing rectangle
3262 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
3263 rcCaret.right = ll->positions[offsetFirstChar+numCharsToDraw] - ll->positions[lineStart] + xStart;
3265 // Adjust caret position to take into account any word wrapping symbols.
3266 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3267 XYPOSITION wordWrapCharWidth = ll->wrapIndent;
3268 rcCaret.left += wordWrapCharWidth;
3269 rcCaret.right += wordWrapCharWidth;
3272 // This character is where the caret block is, we override the colours
3273 // (inversed) for drawing the caret here.
3274 int styleMain = ll->styles[offsetFirstChar];
3275 surface->DrawTextClipped(rcCaret, vsDraw.styles[styleMain].font,
3276 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
3277 numCharsToDraw, vsDraw.styles[styleMain].back,
3278 caretColour);
3281 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
3282 if (!pixmapSelPattern->Initialised()) {
3283 const int patternSize = 8;
3284 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3285 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
3286 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
3287 // way between the chrome colour and the chrome highlight colour making a nice transition
3288 // between the window chrome and the content area. And it works in low colour depths.
3289 PRectangle rcPattern(0, 0, patternSize, patternSize);
3291 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
3292 ColourDesired colourFMFill = vs.selbar;
3293 ColourDesired colourFMStripes = vs.selbarlight;
3295 if (!(vs.selbarlight == ColourDesired(0xff, 0xff, 0xff))) {
3296 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
3297 // (Typically, the highlight colour is white.)
3298 colourFMFill = vs.selbarlight;
3301 if (vs.foldmarginColourSet) {
3302 // override default fold margin colour
3303 colourFMFill = vs.foldmarginColour;
3305 if (vs.foldmarginHighlightColourSet) {
3306 // override default fold margin highlight colour
3307 colourFMStripes = vs.foldmarginHighlightColour;
3310 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
3311 for (int y = 0; y < patternSize; y++) {
3312 for (int x = y % 2; x < patternSize; x+=2) {
3313 PRectangle rcPixel(x, y, x+1, y+1);
3314 pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);
3319 if (!pixmapIndentGuide->Initialised()) {
3320 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
3321 pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3322 pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3323 PRectangle rcIG(0, 0, 1, vs.lineHeight);
3324 pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back);
3325 pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore);
3326 pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back);
3327 pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore);
3328 for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
3329 PRectangle rcPixel(0, stripe, 1, stripe+1);
3330 pixmapIndentGuide->FillRectangle(rcPixel, vs.styles[STYLE_INDENTGUIDE].fore);
3331 pixmapIndentGuideHighlight->FillRectangle(rcPixel, vs.styles[STYLE_BRACELIGHT].fore);
3335 if (bufferedDraw) {
3336 if (!pixmapLine->Initialised()) {
3337 PRectangle rcClient = GetClientRectangle();
3338 pixmapLine->InitPixMap(rcClient.Width(), vs.lineHeight,
3339 surfaceWindow, wMain.GetID());
3340 pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
3341 rcClient.Height(), surfaceWindow, wMain.GetID());
3346 void Editor::DrawCarets(Surface *surface, ViewStyle &vsDraw, int lineDoc, int xStart,
3347 PRectangle rcLine, LineLayout *ll, int subLine) {
3348 // When drag is active it is the only caret drawn
3349 bool drawDrag = posDrag.IsValid();
3350 if (hideSelection && !drawDrag)
3351 return;
3352 const int posLineStart = pdoc->LineStart(lineDoc);
3353 // For each selection draw
3354 for (size_t r=0; (r<sel.Count()) || drawDrag; r++) {
3355 const bool mainCaret = r == sel.Main();
3356 const SelectionPosition posCaret = (drawDrag ? posDrag : sel.Range(r).caret);
3357 const int offset = posCaret.Position() - posLineStart;
3358 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
3359 const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
3360 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
3361 XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
3362 if (ll->wrapIndent != 0) {
3363 int lineStart = ll->LineStart(subLine);
3364 if (lineStart != 0) // Wrapped
3365 xposCaret += ll->wrapIndent;
3367 bool caretBlinkState = (caret.active && caret.on) || (!additionalCaretsBlink && !mainCaret);
3368 bool caretVisibleState = additionalCaretsVisible || mainCaret;
3369 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
3370 ((posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
3371 bool caretAtEOF = false;
3372 bool caretAtEOL = false;
3373 bool drawBlockCaret = false;
3374 XYPOSITION widthOverstrikeCaret;
3375 int caretWidthOffset = 0;
3376 PRectangle rcCaret = rcLine;
3378 if (posCaret.Position() == pdoc->Length()) { // At end of document
3379 caretAtEOF = true;
3380 widthOverstrikeCaret = vsDraw.aveCharWidth;
3381 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
3382 caretAtEOL = true;
3383 widthOverstrikeCaret = vsDraw.aveCharWidth;
3384 } else {
3385 widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
3387 if (widthOverstrikeCaret < 3) // Make sure its visible
3388 widthOverstrikeCaret = 3;
3390 if (xposCaret > 0)
3391 caretWidthOffset = 1; // Move back so overlaps both character cells.
3392 xposCaret += xStart;
3393 if (posDrag.IsValid()) {
3394 /* Dragging text, use a line caret */
3395 rcCaret.left = xposCaret - caretWidthOffset;
3396 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3397 } else if (inOverstrike) {
3398 /* Overstrike (insert mode), use a modified bar caret */
3399 rcCaret.top = rcCaret.bottom - 2;
3400 rcCaret.left = xposCaret + 1;
3401 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
3402 } else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) {
3403 /* Block caret */
3404 rcCaret.left = xposCaret;
3405 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
3406 drawBlockCaret = true;
3407 rcCaret.right = xposCaret + widthOverstrikeCaret;
3408 } else {
3409 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
3411 } else {
3412 /* Line caret */
3413 rcCaret.left = xposCaret - caretWidthOffset;
3414 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3416 ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
3417 if (drawBlockCaret) {
3418 DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
3419 } else {
3420 surface->FillRectangle(rcCaret, caretColour);
3424 if (drawDrag)
3425 break;
3429 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
3430 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
3431 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
3432 AllocateGraphics();
3434 RefreshStyleData();
3435 RefreshPixMaps(surfaceWindow);
3437 StyleToPositionInView(PositionAfterArea(rcArea));
3439 PRectangle rcClient = GetClientRectangle();
3440 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
3441 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3443 int screenLinePaintFirst = rcArea.top / vs.lineHeight;
3445 int xStart = vs.fixedColumnWidth - xOffset;
3446 int ypos = 0;
3447 if (!bufferedDraw)
3448 ypos += screenLinePaintFirst * vs.lineHeight;
3449 int yposScreen = screenLinePaintFirst * vs.lineHeight;
3451 bool paintAbandonedByStyling = paintState == paintAbandoned;
3452 if (needUpdateUI) {
3453 NotifyUpdateUI();
3454 needUpdateUI = 0;
3456 RefreshStyleData();
3457 RefreshPixMaps(surfaceWindow);
3460 // Call priority lines wrap on a window of lines which are likely
3461 // to rendered with the following paint (that is wrap the visible
3462 // lines first).
3463 int startLineToWrap = cs.DocFromDisplay(topLine) - 5;
3464 if (startLineToWrap < 0)
3465 startLineToWrap = 0;
3466 if (WrapLines(false, startLineToWrap)) {
3467 // The wrapping process has changed the height of some lines so
3468 // abandon this paint for a complete repaint.
3469 if (AbandonPaint()) {
3470 return;
3472 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
3474 PLATFORM_ASSERT(pixmapSelPattern->Initialised());
3476 if (!bufferedDraw)
3477 surfaceWindow->SetClip(rcArea);
3479 if (paintState != paintAbandoned) {
3480 PaintSelMargin(surfaceWindow, rcArea);
3482 PRectangle rcRightMargin = rcClient;
3483 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
3484 if (rcArea.Intersects(rcRightMargin)) {
3485 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
3489 if (paintState == paintAbandoned) {
3490 // Either styling or NotifyUpdateUI noticed that painting is needed
3491 // outside the current painting rectangle
3492 //Platform::DebugPrintf("Abandoning paint\n");
3493 if (wrapState != eWrapNone) {
3494 if (paintAbandonedByStyling) {
3495 // Styling has spilled over a line end, such as occurs by starting a multiline
3496 // comment. The width of subsequent text may have changed, so rewrap.
3497 NeedWrapping(cs.DocFromDisplay(topLine));
3500 return;
3502 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
3504 // Do the painting
3505 if (rcArea.right > vs.fixedColumnWidth) {
3507 Surface *surface = surfaceWindow;
3508 if (bufferedDraw) {
3509 surface = pixmapLine;
3510 PLATFORM_ASSERT(pixmapLine->Initialised());
3512 surface->SetUnicodeMode(IsUnicodeMode());
3513 surface->SetDBCSMode(CodePage());
3515 int visibleLine = topLine + screenLinePaintFirst;
3517 SelectionPosition posCaret = sel.RangeMain().caret;
3518 if (posDrag.IsValid())
3519 posCaret = posDrag;
3520 int lineCaret = pdoc->LineFromPosition(posCaret.Position());
3522 PRectangle rcTextArea = rcClient;
3523 rcTextArea.left = vs.fixedColumnWidth;
3524 rcTextArea.right -= vs.rightMarginWidth;
3526 // Remove selection margin from drawing area so text will not be drawn
3527 // on it in unbuffered mode.
3528 if (!bufferedDraw) {
3529 surfaceWindow->SetClip(rcTextArea);
3532 // Loop on visible lines
3533 //double durLayout = 0.0;
3534 //double durPaint = 0.0;
3535 //double durCopy = 0.0;
3536 //ElapsedTime etWhole;
3537 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
3538 AutoLineLayout ll(llc, 0);
3539 while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
3541 int lineDoc = cs.DocFromDisplay(visibleLine);
3542 // Only visible lines should be handled by the code within the loop
3543 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
3544 int lineStartSet = cs.DisplayFromDoc(lineDoc);
3545 int subLine = visibleLine - lineStartSet;
3547 // Copy this line and its styles from the document into local arrays
3548 // and determine the x position at which each character starts.
3549 //ElapsedTime et;
3550 if (lineDoc != lineDocPrevious) {
3551 ll.Set(0);
3552 ll.Set(RetrieveLineLayout(lineDoc));
3553 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
3554 lineDocPrevious = lineDoc;
3556 //durLayout += et.Duration(true);
3558 if (ll) {
3559 ll->containsCaret = lineDoc == lineCaret;
3560 if (hideSelection) {
3561 ll->containsCaret = false;
3564 GetHotSpotRange(ll->hsStart, ll->hsEnd);
3566 PRectangle rcLine = rcTextArea;
3567 rcLine.top = ypos;
3568 rcLine.bottom = ypos + vs.lineHeight;
3570 bool bracesIgnoreStyle = false;
3571 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
3572 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
3573 bracesIgnoreStyle = true;
3575 Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
3576 // Highlight the current braces if any
3577 ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
3578 highlightGuideColumn * vs.spaceWidth, bracesIgnoreStyle);
3580 // Draw the line
3581 DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
3582 //durPaint += et.Duration(true);
3584 // Restore the previous styles for the brace highlights in case layout is in cache.
3585 ll->RestoreBracesHighlight(rangeLine, braces, bracesIgnoreStyle);
3587 bool expanded = cs.GetExpanded(lineDoc);
3588 const int level = pdoc->GetLevel(lineDoc);
3589 const int levelNext = pdoc->GetLevel(lineDoc + 1);
3590 if ((level & SC_FOLDLEVELHEADERFLAG) &&
3591 ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
3592 // Paint the line above the fold
3593 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
3595 (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
3596 PRectangle rcFoldLine = rcLine;
3597 rcFoldLine.bottom = rcFoldLine.top + 1;
3598 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore);
3600 // Paint the line below the fold
3601 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
3603 (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
3604 PRectangle rcFoldLine = rcLine;
3605 rcFoldLine.top = rcFoldLine.bottom - 1;
3606 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore);
3610 DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine);
3612 if (bufferedDraw) {
3613 Point from(vs.fixedColumnWidth, 0);
3614 PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen,
3615 rcClient.right - vs.rightMarginWidth, yposScreen + vs.lineHeight);
3616 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
3619 lineWidthMaxSeen = Platform::Maximum(
3620 lineWidthMaxSeen, ll->positions[ll->numCharsInLine]);
3621 //durCopy += et.Duration(true);
3624 if (!bufferedDraw) {
3625 ypos += vs.lineHeight;
3628 yposScreen += vs.lineHeight;
3629 visibleLine++;
3631 //gdk_flush();
3633 ll.Set(0);
3634 //if (durPaint < 0.00000001)
3635 // durPaint = 0.00000001;
3637 // Right column limit indicator
3638 PRectangle rcBeyondEOF = rcClient;
3639 rcBeyondEOF.left = vs.fixedColumnWidth;
3640 rcBeyondEOF.right = rcBeyondEOF.right - vs.rightMarginWidth;
3641 rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight;
3642 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
3643 surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back);
3644 if (vs.edgeState == EDGE_LINE) {
3645 int edgeX = theEdge * vs.spaceWidth;
3646 rcBeyondEOF.left = edgeX + xStart;
3647 rcBeyondEOF.right = rcBeyondEOF.left + 1;
3648 surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour);
3651 //Platform::DebugPrintf(
3652 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
3653 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
3654 NotifyPainted();
3658 // Space (3 space characters) between line numbers and text when printing.
3659 #define lineNumberPrintSpace " "
3661 ColourDesired InvertedLight(ColourDesired orig) {
3662 unsigned int r = orig.GetRed();
3663 unsigned int g = orig.GetGreen();
3664 unsigned int b = orig.GetBlue();
3665 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
3666 unsigned int il = 0xff - l;
3667 if (l == 0)
3668 return ColourDesired(0xff, 0xff, 0xff);
3669 r = r * il / l;
3670 g = g * il / l;
3671 b = b * il / l;
3672 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
3675 // This is mostly copied from the Paint method but with some things omitted
3676 // such as the margin markers, line numbers, selection and caret
3677 // Should be merged back into a combined Draw method.
3678 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
3679 if (!pfr)
3680 return 0;
3682 AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
3683 if (!surface)
3684 return 0;
3685 AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
3686 if (!surfaceMeasure) {
3687 return 0;
3690 // Can't use measurements cached for screen
3691 posCache.Clear();
3693 ViewStyle vsPrint(vs);
3694 vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
3696 // Modify the view style for printing as do not normally want any of the transient features to be printed
3697 // Printing supports only the line number margin.
3698 int lineNumberIndex = -1;
3699 for (int margin = 0; margin < ViewStyle::margins; margin++) {
3700 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
3701 lineNumberIndex = margin;
3702 } else {
3703 vsPrint.ms[margin].width = 0;
3706 vsPrint.fixedColumnWidth = 0;
3707 vsPrint.zoomLevel = printMagnification;
3708 // Don't show indentation guides
3709 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
3710 vsPrint.viewIndentationGuides = ivNone;
3711 // Don't show the selection when printing
3712 vsPrint.selbackset = false;
3713 vsPrint.selforeset = false;
3714 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
3715 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
3716 vsPrint.whitespaceBackgroundSet = false;
3717 vsPrint.whitespaceForegroundSet = false;
3718 vsPrint.showCaretLineBackground = false;
3719 // Don't highlight matching braces using indicators
3720 vsPrint.braceHighlightIndicatorSet = false;
3721 vsPrint.braceBadLightIndicatorSet = false;
3723 // Set colours for printing according to users settings
3724 for (size_t sty = 0; sty < vsPrint.stylesSize; sty++) {
3725 if (printColourMode == SC_PRINT_INVERTLIGHT) {
3726 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
3727 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
3728 } else if (printColourMode == SC_PRINT_BLACKONWHITE) {
3729 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
3730 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3731 } else if (printColourMode == SC_PRINT_COLOURONWHITE) {
3732 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3733 } else if (printColourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
3734 if (sty <= STYLE_DEFAULT) {
3735 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3739 // White background for the line numbers
3740 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
3742 // Printing uses different margins, so reset screen margins
3743 vsPrint.leftMarginWidth = 0;
3744 vsPrint.rightMarginWidth = 0;
3746 vsPrint.Refresh(*surfaceMeasure);
3747 // Determining width must hapen after fonts have been realised in Refresh
3748 int lineNumberWidth = 0;
3749 if (lineNumberIndex >= 0) {
3750 lineNumberWidth = surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
3751 "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace));
3752 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
3753 vsPrint.Refresh(*surfaceMeasure); // Recalculate fixedColumnWidth
3756 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
3757 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
3758 if (linePrintLast < linePrintStart)
3759 linePrintLast = linePrintStart;
3760 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
3761 if (linePrintLast > linePrintMax)
3762 linePrintLast = linePrintMax;
3763 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
3764 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
3765 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
3766 int endPosPrint = pdoc->Length();
3767 if (linePrintLast < pdoc->LinesTotal())
3768 endPosPrint = pdoc->LineStart(linePrintLast + 1);
3770 // Ensure we are styled to where we are formatting.
3771 pdoc->EnsureStyledTo(endPosPrint);
3773 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
3774 int ypos = pfr->rc.top;
3776 int lineDoc = linePrintStart;
3778 int nPrintPos = pfr->chrg.cpMin;
3779 int visibleLine = 0;
3780 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
3781 if (printWrapState == eWrapNone)
3782 widthPrint = LineLayout::wrapWidthInfinite;
3784 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
3786 // When printing, the hdc and hdcTarget may be the same, so
3787 // changing the state of surfaceMeasure may change the underlying
3788 // state of surface. Therefore, any cached state is discarded before
3789 // using each surface.
3790 surfaceMeasure->FlushCachedState();
3792 // Copy this line and its styles from the document into local arrays
3793 // and determine the x position at which each character starts.
3794 LineLayout ll(8000);
3795 LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
3797 ll.containsCaret = false;
3799 PRectangle rcLine;
3800 rcLine.left = pfr->rc.left;
3801 rcLine.top = ypos;
3802 rcLine.right = pfr->rc.right - 1;
3803 rcLine.bottom = ypos + vsPrint.lineHeight;
3805 // When document line is wrapped over multiple display lines, find where
3806 // to start printing from to ensure a particular position is on the first
3807 // line of the page.
3808 if (visibleLine == 0) {
3809 int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
3810 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
3811 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
3812 visibleLine = -iwl;
3816 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
3817 visibleLine = -(ll.lines - 1);
3821 if (draw && lineNumberWidth &&
3822 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
3823 (visibleLine >= 0)) {
3824 char number[100];
3825 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
3826 PRectangle rcNumber = rcLine;
3827 rcNumber.right = rcNumber.left + lineNumberWidth;
3828 // Right justify
3829 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
3830 vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
3831 surface->FlushCachedState();
3832 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
3833 ypos + vsPrint.maxAscent, number, istrlen(number),
3834 vsPrint.styles[STYLE_LINENUMBER].fore,
3835 vsPrint.styles[STYLE_LINENUMBER].back);
3838 // Draw the line
3839 surface->FlushCachedState();
3841 for (int iwl = 0; iwl < ll.lines; iwl++) {
3842 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
3843 if (visibleLine >= 0) {
3844 if (draw) {
3845 rcLine.top = ypos;
3846 rcLine.bottom = ypos + vsPrint.lineHeight;
3847 DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
3849 ypos += vsPrint.lineHeight;
3851 visibleLine++;
3852 if (iwl == ll.lines - 1)
3853 nPrintPos = pdoc->LineStart(lineDoc + 1);
3854 else
3855 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
3859 ++lineDoc;
3862 // Clear cache so measurements are not used for screen
3863 posCache.Clear();
3865 return nPrintPos;
3868 int Editor::TextWidth(int style, const char *text) {
3869 RefreshStyleData();
3870 AutoSurface surface(this);
3871 if (surface) {
3872 return surface->WidthText(vs.styles[style].font, text, istrlen(text));
3873 } else {
3874 return 1;
3878 // Empty method is overridden on GTK+ to show / hide scrollbars
3879 void Editor::ReconfigureScrollBars() {}
3881 void Editor::SetScrollBars() {
3882 RefreshStyleData();
3884 int nMax = MaxScrollPos();
3885 int nPage = LinesOnScreen();
3886 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
3887 if (modified) {
3888 DwellEnd(true);
3891 // TODO: ensure always showing as many lines as possible
3892 // May not be, if, for example, window made larger
3893 if (topLine > MaxScrollPos()) {
3894 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
3895 SetVerticalScrollPos();
3896 Redraw();
3898 if (modified) {
3899 if (!AbandonPaint())
3900 Redraw();
3902 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
3905 void Editor::ChangeSize() {
3906 DropGraphics(false);
3907 SetScrollBars();
3908 if (wrapState != eWrapNone) {
3909 PRectangle rcTextArea = GetClientRectangle();
3910 rcTextArea.left = vs.fixedColumnWidth;
3911 rcTextArea.right -= vs.rightMarginWidth;
3912 if (wrapWidth != rcTextArea.Width()) {
3913 NeedWrapping();
3914 Redraw();
3919 int Editor::InsertSpace(int position, unsigned int spaces) {
3920 if (spaces > 0) {
3921 std::string spaceText(spaces, ' ');
3922 pdoc->InsertString(position, spaceText.c_str(), spaces);
3923 position += spaces;
3925 return position;
3928 void Editor::AddChar(char ch) {
3929 char s[2];
3930 s[0] = ch;
3931 s[1] = '\0';
3932 AddCharUTF(s, 1);
3935 void Editor::FilterSelections() {
3936 if (!additionalSelectionTyping && (sel.Count() > 1)) {
3937 SelectionRange rangeOnly = sel.RangeMain();
3938 InvalidateSelection(rangeOnly, true);
3939 sel.SetSelection(rangeOnly);
3943 static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
3944 return *a < *b;
3947 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
3948 void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
3949 FilterSelections();
3951 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
3953 std::vector<SelectionRange *> selPtrs;
3954 for (size_t r = 0; r < sel.Count(); r++) {
3955 selPtrs.push_back(&sel.Range(r));
3957 std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
3959 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
3960 rit != selPtrs.rend(); ++rit) {
3961 SelectionRange *currentSel = *rit;
3962 if (!RangeContainsProtected(currentSel->Start().Position(),
3963 currentSel->End().Position())) {
3964 int positionInsert = currentSel->Start().Position();
3965 if (!currentSel->Empty()) {
3966 if (currentSel->Length()) {
3967 pdoc->DeleteChars(positionInsert, currentSel->Length());
3968 currentSel->ClearVirtualSpace();
3969 } else {
3970 // Range is all virtual so collapse to start of virtual space
3971 currentSel->MinimizeVirtualSpace();
3973 } else if (inOverstrike) {
3974 if (positionInsert < pdoc->Length()) {
3975 if (!IsEOLChar(pdoc->CharAt(positionInsert))) {
3976 pdoc->DelChar(positionInsert);
3977 currentSel->ClearVirtualSpace();
3981 positionInsert = InsertSpace(positionInsert, currentSel->caret.VirtualSpace());
3982 if (pdoc->InsertString(positionInsert, s, len)) {
3983 currentSel->caret.SetPosition(positionInsert + len);
3984 currentSel->anchor.SetPosition(positionInsert + len);
3986 currentSel->ClearVirtualSpace();
3987 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
3988 if (wrapState != eWrapNone) {
3989 AutoSurface surface(this);
3990 if (surface) {
3991 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
3992 SetScrollBars();
3993 SetVerticalScrollPos();
3994 Redraw();
4001 if (wrapState != eWrapNone) {
4002 SetScrollBars();
4004 ThinRectangularRange();
4005 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4006 EnsureCaretVisible();
4007 // Avoid blinking during rapid typing:
4008 ShowCaretAtCurrentPosition();
4009 if ((caretSticky == SC_CARETSTICKY_OFF) ||
4010 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
4011 SetLastXChosen();
4014 if (treatAsDBCS) {
4015 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
4016 static_cast<unsigned char>(s[1]));
4017 } else {
4018 int byte = static_cast<unsigned char>(s[0]);
4019 if ((byte < 0xC0) || (1 == len)) {
4020 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
4021 // characters when not in UTF-8 mode.
4022 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
4023 // characters representing themselves.
4024 } else {
4025 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
4026 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
4027 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
4028 if (byte < 0xE0) {
4029 int byte2 = static_cast<unsigned char>(s[1]);
4030 if ((byte2 & 0xC0) == 0x80) {
4031 // Two-byte-character lead-byte followed by a trail-byte.
4032 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
4034 // A two-byte-character lead-byte not followed by trail-byte
4035 // represents itself.
4036 } else if (byte < 0xF0) {
4037 int byte2 = static_cast<unsigned char>(s[1]);
4038 int byte3 = static_cast<unsigned char>(s[2]);
4039 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
4040 // Three-byte-character lead byte followed by two trail bytes.
4041 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
4042 (byte3 & 0x3F));
4044 // A three-byte-character lead-byte not followed by two trail-bytes
4045 // represents itself.
4048 NotifyChar(byte);
4051 if (recordingMacro) {
4052 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
4056 void Editor::InsertPaste(SelectionPosition selStart, const char *text, int len) {
4057 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
4058 selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
4059 if (pdoc->InsertString(selStart.Position(), text, len)) {
4060 SetEmptySelection(selStart.Position() + len);
4062 } else {
4063 // SC_MULTIPASTE_EACH
4064 for (size_t r=0; r<sel.Count(); r++) {
4065 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4066 sel.Range(r).End().Position())) {
4067 int positionInsert = sel.Range(r).Start().Position();
4068 if (!sel.Range(r).Empty()) {
4069 if (sel.Range(r).Length()) {
4070 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
4071 sel.Range(r).ClearVirtualSpace();
4072 } else {
4073 // Range is all virtual so collapse to start of virtual space
4074 sel.Range(r).MinimizeVirtualSpace();
4077 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
4078 if (pdoc->InsertString(positionInsert, text, len)) {
4079 sel.Range(r).caret.SetPosition(positionInsert + len);
4080 sel.Range(r).anchor.SetPosition(positionInsert + len);
4082 sel.Range(r).ClearVirtualSpace();
4088 void Editor::ClearSelection(bool retainMultipleSelections) {
4089 if (!sel.IsRectangular() && !retainMultipleSelections)
4090 FilterSelections();
4091 UndoGroup ug(pdoc);
4092 for (size_t r=0; r<sel.Count(); r++) {
4093 if (!sel.Range(r).Empty()) {
4094 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4095 sel.Range(r).End().Position())) {
4096 pdoc->DeleteChars(sel.Range(r).Start().Position(),
4097 sel.Range(r).Length());
4098 sel.Range(r) = sel.Range(r).Start();
4102 ThinRectangularRange();
4103 sel.RemoveDuplicates();
4104 ClaimSelection();
4107 void Editor::ClearAll() {
4109 UndoGroup ug(pdoc);
4110 if (0 != pdoc->Length()) {
4111 pdoc->DeleteChars(0, pdoc->Length());
4113 if (!pdoc->IsReadOnly()) {
4114 cs.Clear();
4115 pdoc->AnnotationClearAll();
4116 pdoc->MarginClearAll();
4119 sel.Clear();
4120 SetTopLine(0);
4121 SetVerticalScrollPos();
4122 InvalidateStyleRedraw();
4125 void Editor::ClearDocumentStyle() {
4126 Decoration *deco = pdoc->decorations.root;
4127 while (deco) {
4128 // Save next in case deco deleted
4129 Decoration *decoNext = deco->next;
4130 if (deco->indicator < INDIC_CONTAINER) {
4131 pdoc->decorations.SetCurrentIndicator(deco->indicator);
4132 pdoc->DecorationFillRange(0, 0, pdoc->Length());
4134 deco = decoNext;
4136 pdoc->StartStyling(0, '\377');
4137 pdoc->SetStyleFor(pdoc->Length(), 0);
4138 cs.ShowAll();
4139 pdoc->ClearLevels();
4142 void Editor::CopyAllowLine() {
4143 SelectionText selectedText;
4144 CopySelectionRange(&selectedText, true);
4145 CopyToClipboard(selectedText);
4148 void Editor::Cut() {
4149 pdoc->CheckReadOnly();
4150 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
4151 Copy();
4152 ClearSelection();
4156 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
4157 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
4158 return;
4160 sel.Clear();
4161 sel.RangeMain() = SelectionRange(pos);
4162 int line = pdoc->LineFromPosition(sel.MainCaret());
4163 UndoGroup ug(pdoc);
4164 sel.RangeMain().caret = SelectionPosition(
4165 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
4166 int xInsert = XFromPosition(sel.RangeMain().caret);
4167 bool prevCr = false;
4168 while ((len > 0) && IsEOLChar(ptr[len-1]))
4169 len--;
4170 for (int i = 0; i < len; i++) {
4171 if (IsEOLChar(ptr[i])) {
4172 if ((ptr[i] == '\r') || (!prevCr))
4173 line++;
4174 if (line >= pdoc->LinesTotal()) {
4175 if (pdoc->eolMode != SC_EOL_LF)
4176 pdoc->InsertChar(pdoc->Length(), '\r');
4177 if (pdoc->eolMode != SC_EOL_CR)
4178 pdoc->InsertChar(pdoc->Length(), '\n');
4180 // Pad the end of lines with spaces if required
4181 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
4182 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
4183 while (XFromPosition(sel.MainCaret()) < xInsert) {
4184 pdoc->InsertChar(sel.MainCaret(), ' ');
4185 sel.RangeMain().caret.Add(1);
4188 prevCr = ptr[i] == '\r';
4189 } else {
4190 pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
4191 sel.RangeMain().caret.Add(1);
4192 prevCr = false;
4195 SetEmptySelection(pos);
4198 bool Editor::CanPaste() {
4199 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
4202 void Editor::Clear() {
4203 // If multiple selections, don't delete EOLS
4204 if (sel.Empty()) {
4205 bool singleVirtual = false;
4206 if ((sel.Count() == 1) &&
4207 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
4208 sel.RangeMain().Start().VirtualSpace()) {
4209 singleVirtual = true;
4211 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
4212 for (size_t r=0; r<sel.Count(); r++) {
4213 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
4214 if (sel.Range(r).Start().VirtualSpace()) {
4215 if (sel.Range(r).anchor < sel.Range(r).caret)
4216 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
4217 else
4218 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
4220 if ((sel.Count() == 1) || !IsEOLChar(pdoc->CharAt(sel.Range(r).caret.Position()))) {
4221 pdoc->DelChar(sel.Range(r).caret.Position());
4222 sel.Range(r).ClearVirtualSpace();
4223 } // else multiple selection so don't eat line ends
4224 } else {
4225 sel.Range(r).ClearVirtualSpace();
4228 } else {
4229 ClearSelection();
4231 sel.RemoveDuplicates();
4234 void Editor::SelectAll() {
4235 sel.Clear();
4236 SetSelection(0, pdoc->Length());
4237 Redraw();
4240 void Editor::Undo() {
4241 if (pdoc->CanUndo()) {
4242 InvalidateCaret();
4243 int newPos = pdoc->Undo();
4244 if (newPos >= 0)
4245 SetEmptySelection(newPos);
4246 EnsureCaretVisible();
4250 void Editor::Redo() {
4251 if (pdoc->CanRedo()) {
4252 int newPos = pdoc->Redo();
4253 if (newPos >= 0)
4254 SetEmptySelection(newPos);
4255 EnsureCaretVisible();
4259 void Editor::DelChar() {
4260 if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) {
4261 pdoc->DelChar(sel.MainCaret());
4263 // Avoid blinking during rapid typing:
4264 ShowCaretAtCurrentPosition();
4267 void Editor::DelCharBack(bool allowLineStartDeletion) {
4268 if (!sel.IsRectangular())
4269 FilterSelections();
4270 if (sel.IsRectangular())
4271 allowLineStartDeletion = false;
4272 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
4273 if (sel.Empty()) {
4274 for (size_t r=0; r<sel.Count(); r++) {
4275 if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
4276 if (sel.Range(r).caret.VirtualSpace()) {
4277 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
4278 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
4279 } else {
4280 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4281 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
4282 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
4283 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
4284 UndoGroup ugInner(pdoc, !ug.Needed());
4285 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4286 int indentationStep = pdoc->IndentSize();
4287 if (indentation % indentationStep == 0) {
4288 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
4289 } else {
4290 pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
4292 // SetEmptySelection
4293 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos),
4294 pdoc->GetLineIndentPosition(lineCurrentPos));
4295 } else {
4296 pdoc->DelCharBack(sel.Range(r).caret.Position());
4300 } else {
4301 sel.Range(r).ClearVirtualSpace();
4304 } else {
4305 ClearSelection();
4307 sel.RemoveDuplicates();
4308 // Avoid blinking during rapid typing:
4309 ShowCaretAtCurrentPosition();
4312 void Editor::NotifyFocus(bool) {}
4314 void Editor::SetCtrlID(int identifier) {
4315 ctrlID = identifier;
4318 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
4319 SCNotification scn = {0};
4320 scn.nmhdr.code = SCN_STYLENEEDED;
4321 scn.position = endStyleNeeded;
4322 NotifyParent(scn);
4325 void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
4326 NotifyStyleToNeeded(endStyleNeeded);
4329 void Editor::NotifyLexerChanged(Document *, void *) {
4332 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
4333 errorStatus = status;
4336 void Editor::NotifyChar(int ch) {
4337 SCNotification scn = {0};
4338 scn.nmhdr.code = SCN_CHARADDED;
4339 scn.ch = ch;
4340 NotifyParent(scn);
4343 void Editor::NotifySavePoint(bool isSavePoint) {
4344 SCNotification scn = {0};
4345 if (isSavePoint) {
4346 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
4347 } else {
4348 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
4350 NotifyParent(scn);
4353 void Editor::NotifyModifyAttempt() {
4354 SCNotification scn = {0};
4355 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
4356 NotifyParent(scn);
4359 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
4360 SCNotification scn = {0};
4361 scn.nmhdr.code = SCN_DOUBLECLICK;
4362 scn.line = LineFromLocation(pt);
4363 scn.position = PositionFromLocation(pt, true);
4364 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4365 (alt ? SCI_ALT : 0);
4366 NotifyParent(scn);
4369 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
4370 SCNotification scn = {0};
4371 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
4372 scn.position = position;
4373 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4374 (alt ? SCI_ALT : 0);
4375 NotifyParent(scn);
4378 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
4379 SCNotification scn = {0};
4380 scn.nmhdr.code = SCN_HOTSPOTCLICK;
4381 scn.position = position;
4382 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4383 (alt ? SCI_ALT : 0);
4384 NotifyParent(scn);
4387 void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
4388 SCNotification scn = {0};
4389 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
4390 scn.position = position;
4391 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4392 (alt ? SCI_ALT : 0);
4393 NotifyParent(scn);
4396 void Editor::NotifyUpdateUI() {
4397 SCNotification scn = {0};
4398 scn.nmhdr.code = SCN_UPDATEUI;
4399 scn.updated = needUpdateUI;
4400 NotifyParent(scn);
4403 void Editor::NotifyPainted() {
4404 SCNotification scn = {0};
4405 scn.nmhdr.code = SCN_PAINTED;
4406 NotifyParent(scn);
4409 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
4410 int mask = pdoc->decorations.AllOnFor(position);
4411 if ((click && mask) || pdoc->decorations.clickNotified) {
4412 SCNotification scn = {0};
4413 pdoc->decorations.clickNotified = click;
4414 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
4415 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | (alt ? SCI_ALT : 0);
4416 scn.position = position;
4417 NotifyParent(scn);
4421 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
4422 int marginClicked = -1;
4423 int x = 0;
4424 for (int margin = 0; margin < ViewStyle::margins; margin++) {
4425 if ((pt.x > x) && (pt.x < x + vs.ms[margin].width))
4426 marginClicked = margin;
4427 x += vs.ms[margin].width;
4429 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
4430 SCNotification scn = {0};
4431 scn.nmhdr.code = SCN_MARGINCLICK;
4432 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4433 (alt ? SCI_ALT : 0);
4434 scn.position = pdoc->LineStart(LineFromLocation(pt));
4435 scn.margin = marginClicked;
4436 NotifyParent(scn);
4437 return true;
4438 } else {
4439 return false;
4443 void Editor::NotifyNeedShown(int pos, int len) {
4444 SCNotification scn = {0};
4445 scn.nmhdr.code = SCN_NEEDSHOWN;
4446 scn.position = pos;
4447 scn.length = len;
4448 NotifyParent(scn);
4451 void Editor::NotifyDwelling(Point pt, bool state) {
4452 SCNotification scn = {0};
4453 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
4454 scn.position = PositionFromLocation(pt, true);
4455 scn.x = pt.x;
4456 scn.y = pt.y;
4457 NotifyParent(scn);
4460 void Editor::NotifyZoom() {
4461 SCNotification scn = {0};
4462 scn.nmhdr.code = SCN_ZOOM;
4463 NotifyParent(scn);
4466 // Notifications from document
4467 void Editor::NotifyModifyAttempt(Document *, void *) {
4468 //Platform::DebugPrintf("** Modify Attempt\n");
4469 NotifyModifyAttempt();
4472 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
4473 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
4474 NotifySavePoint(atSavePoint);
4477 void Editor::CheckModificationForWrap(DocModification mh) {
4478 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
4479 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4480 int lineDoc = pdoc->LineFromPosition(mh.position);
4481 int lines = Platform::Maximum(0, mh.linesAdded);
4482 if (wrapState != eWrapNone) {
4483 NeedWrapping(lineDoc, lineDoc + lines + 1);
4485 RefreshStyleData();
4486 // Fix up annotation heights
4487 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
4491 // Move a position so it is still after the same character as before the insertion.
4492 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
4493 if (position > startInsertion) {
4494 return position + length;
4496 return position;
4499 // Move a position so it is still after the same character as before the deletion if that
4500 // character is still present else after the previous surviving character.
4501 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
4502 if (position > startDeletion) {
4503 int endDeletion = startDeletion + length;
4504 if (position > endDeletion) {
4505 return position - length;
4506 } else {
4507 return startDeletion;
4509 } else {
4510 return position;
4514 void Editor::NotifyModified(Document *, DocModification mh, void *) {
4515 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
4516 if (paintState == painting) {
4517 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
4519 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
4520 if (paintState == painting) {
4521 CheckForChangeOutsidePaint(
4522 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
4523 } else {
4524 // Could check that change is before last visible line.
4525 Redraw();
4528 if (mh.modificationType & SC_MOD_LEXERSTATE) {
4529 if (paintState == painting) {
4530 CheckForChangeOutsidePaint(
4531 Range(mh.position, mh.position + mh.length));
4532 } else {
4533 Redraw();
4536 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
4537 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4538 pdoc->IncrementStyleClock();
4540 if (paintState == notPainting) {
4541 if (mh.position < pdoc->LineStart(topLine)) {
4542 // Styling performed before this view
4543 Redraw();
4544 } else {
4545 InvalidateRange(mh.position, mh.position + mh.length);
4548 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4549 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4551 } else {
4552 // Move selection and brace highlights
4553 if (mh.modificationType & SC_MOD_INSERTTEXT) {
4554 sel.MovePositions(true, mh.position, mh.length);
4555 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
4556 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
4557 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
4558 sel.MovePositions(false, mh.position, mh.length);
4559 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
4560 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
4562 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && cs.HiddenLines()) {
4563 // Some lines are hidden so may need shown.
4564 // TODO: check if the modified area is hidden.
4565 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
4566 int lineOfPos = pdoc->LineFromPosition(mh.position);
4567 bool insertingNewLine = false;
4568 for (int i=0; i < mh.length; i++) {
4569 if ((mh.text[i] == '\n') || (mh.text[i] == '\r'))
4570 insertingNewLine = true;
4572 if (insertingNewLine && (mh.position != pdoc->LineStart(lineOfPos)))
4573 NotifyNeedShown(mh.position, pdoc->LineStart(lineOfPos+1) - mh.position);
4574 else
4575 NotifyNeedShown(mh.position, 0);
4576 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
4577 NotifyNeedShown(mh.position, mh.length);
4580 if (mh.linesAdded != 0) {
4581 // Update contraction state for inserted and removed lines
4582 // lineOfPos should be calculated in context of state before modification, shouldn't it
4583 int lineOfPos = pdoc->LineFromPosition(mh.position);
4584 if (mh.linesAdded > 0) {
4585 cs.InsertLines(lineOfPos, mh.linesAdded);
4586 } else {
4587 cs.DeleteLines(lineOfPos, -mh.linesAdded);
4590 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
4591 int lineDoc = pdoc->LineFromPosition(mh.position);
4592 if (vs.annotationVisible) {
4593 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
4594 Redraw();
4597 CheckModificationForWrap(mh);
4598 if (mh.linesAdded != 0) {
4599 // Avoid scrolling of display if change before current display
4600 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
4601 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
4602 if (newTop != topLine) {
4603 SetTopLine(newTop);
4604 SetVerticalScrollPos();
4608 //Platform::DebugPrintf("** %x Doc Changed\n", this);
4609 // TODO: could invalidate from mh.startModification to end of screen
4610 //InvalidateRange(mh.position, mh.position + mh.length);
4611 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
4612 QueueStyling(pdoc->Length());
4613 Redraw();
4615 } else {
4616 //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
4617 // mh.position, mh.position + mh.length);
4618 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
4619 QueueStyling(mh.position + mh.length);
4620 InvalidateRange(mh.position, mh.position + mh.length);
4625 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
4626 SetScrollBars();
4629 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
4630 if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) {
4631 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
4632 // Fold changes can affect the drawing of following lines so redraw whole margin
4633 RedrawSelMargin(highlightDelimiter.isEnabled ? -1 : mh.line-1, true);
4634 } else {
4635 RedrawSelMargin(mh.line);
4640 // NOW pay the piper WRT "deferred" visual updates
4641 if (IsLastStep(mh)) {
4642 SetScrollBars();
4643 Redraw();
4646 // If client wants to see this modification
4647 if (mh.modificationType & modEventMask) {
4648 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
4649 // Real modification made to text of document.
4650 NotifyChange(); // Send EN_CHANGE
4653 SCNotification scn = {0};
4654 scn.nmhdr.code = SCN_MODIFIED;
4655 scn.position = mh.position;
4656 scn.modificationType = mh.modificationType;
4657 scn.text = mh.text;
4658 scn.length = mh.length;
4659 scn.linesAdded = mh.linesAdded;
4660 scn.line = mh.line;
4661 scn.foldLevelNow = mh.foldLevelNow;
4662 scn.foldLevelPrev = mh.foldLevelPrev;
4663 scn.token = mh.token;
4664 scn.annotationLinesAdded = mh.annotationLinesAdded;
4665 NotifyParent(scn);
4669 void Editor::NotifyDeleted(Document *, void *) {
4670 /* Do nothing */
4673 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
4675 // Enumerates all macroable messages
4676 switch (iMessage) {
4677 case SCI_CUT:
4678 case SCI_COPY:
4679 case SCI_PASTE:
4680 case SCI_CLEAR:
4681 case SCI_REPLACESEL:
4682 case SCI_ADDTEXT:
4683 case SCI_INSERTTEXT:
4684 case SCI_APPENDTEXT:
4685 case SCI_CLEARALL:
4686 case SCI_SELECTALL:
4687 case SCI_GOTOLINE:
4688 case SCI_GOTOPOS:
4689 case SCI_SEARCHANCHOR:
4690 case SCI_SEARCHNEXT:
4691 case SCI_SEARCHPREV:
4692 case SCI_LINEDOWN:
4693 case SCI_LINEDOWNEXTEND:
4694 case SCI_PARADOWN:
4695 case SCI_PARADOWNEXTEND:
4696 case SCI_LINEUP:
4697 case SCI_LINEUPEXTEND:
4698 case SCI_PARAUP:
4699 case SCI_PARAUPEXTEND:
4700 case SCI_CHARLEFT:
4701 case SCI_CHARLEFTEXTEND:
4702 case SCI_CHARRIGHT:
4703 case SCI_CHARRIGHTEXTEND:
4704 case SCI_WORDLEFT:
4705 case SCI_WORDLEFTEXTEND:
4706 case SCI_WORDRIGHT:
4707 case SCI_WORDRIGHTEXTEND:
4708 case SCI_WORDPARTLEFT:
4709 case SCI_WORDPARTLEFTEXTEND:
4710 case SCI_WORDPARTRIGHT:
4711 case SCI_WORDPARTRIGHTEXTEND:
4712 case SCI_WORDLEFTEND:
4713 case SCI_WORDLEFTENDEXTEND:
4714 case SCI_WORDRIGHTEND:
4715 case SCI_WORDRIGHTENDEXTEND:
4716 case SCI_HOME:
4717 case SCI_HOMEEXTEND:
4718 case SCI_LINEEND:
4719 case SCI_LINEENDEXTEND:
4720 case SCI_HOMEWRAP:
4721 case SCI_HOMEWRAPEXTEND:
4722 case SCI_LINEENDWRAP:
4723 case SCI_LINEENDWRAPEXTEND:
4724 case SCI_DOCUMENTSTART:
4725 case SCI_DOCUMENTSTARTEXTEND:
4726 case SCI_DOCUMENTEND:
4727 case SCI_DOCUMENTENDEXTEND:
4728 case SCI_STUTTEREDPAGEUP:
4729 case SCI_STUTTEREDPAGEUPEXTEND:
4730 case SCI_STUTTEREDPAGEDOWN:
4731 case SCI_STUTTEREDPAGEDOWNEXTEND:
4732 case SCI_PAGEUP:
4733 case SCI_PAGEUPEXTEND:
4734 case SCI_PAGEDOWN:
4735 case SCI_PAGEDOWNEXTEND:
4736 case SCI_EDITTOGGLEOVERTYPE:
4737 case SCI_CANCEL:
4738 case SCI_DELETEBACK:
4739 case SCI_TAB:
4740 case SCI_BACKTAB:
4741 case SCI_FORMFEED:
4742 case SCI_VCHOME:
4743 case SCI_VCHOMEEXTEND:
4744 case SCI_VCHOMEWRAP:
4745 case SCI_VCHOMEWRAPEXTEND:
4746 case SCI_DELWORDLEFT:
4747 case SCI_DELWORDRIGHT:
4748 case SCI_DELWORDRIGHTEND:
4749 case SCI_DELLINELEFT:
4750 case SCI_DELLINERIGHT:
4751 case SCI_LINECOPY:
4752 case SCI_LINECUT:
4753 case SCI_LINEDELETE:
4754 case SCI_LINETRANSPOSE:
4755 case SCI_LINEDUPLICATE:
4756 case SCI_LOWERCASE:
4757 case SCI_UPPERCASE:
4758 case SCI_LINESCROLLDOWN:
4759 case SCI_LINESCROLLUP:
4760 case SCI_DELETEBACKNOTLINE:
4761 case SCI_HOMEDISPLAY:
4762 case SCI_HOMEDISPLAYEXTEND:
4763 case SCI_LINEENDDISPLAY:
4764 case SCI_LINEENDDISPLAYEXTEND:
4765 case SCI_SETSELECTIONMODE:
4766 case SCI_LINEDOWNRECTEXTEND:
4767 case SCI_LINEUPRECTEXTEND:
4768 case SCI_CHARLEFTRECTEXTEND:
4769 case SCI_CHARRIGHTRECTEXTEND:
4770 case SCI_HOMERECTEXTEND:
4771 case SCI_VCHOMERECTEXTEND:
4772 case SCI_LINEENDRECTEXTEND:
4773 case SCI_PAGEUPRECTEXTEND:
4774 case SCI_PAGEDOWNRECTEXTEND:
4775 case SCI_SELECTIONDUPLICATE:
4776 case SCI_COPYALLOWLINE:
4777 case SCI_VERTICALCENTRECARET:
4778 case SCI_MOVESELECTEDLINESUP:
4779 case SCI_MOVESELECTEDLINESDOWN:
4780 case SCI_SCROLLTOSTART:
4781 case SCI_SCROLLTOEND:
4782 break;
4784 // Filter out all others like display changes. Also, newlines are redundant
4785 // with char insert messages.
4786 case SCI_NEWLINE:
4787 default:
4788 // printf("Filtered out %ld of macro recording\n", iMessage);
4789 return ;
4792 // Send notification
4793 SCNotification scn = {0};
4794 scn.nmhdr.code = SCN_MACRORECORD;
4795 scn.message = iMessage;
4796 scn.wParam = wParam;
4797 scn.lParam = lParam;
4798 NotifyParent(scn);
4801 // Something has changed that the container should know about
4802 void Editor::ContainerNeedsUpdate(int flags) {
4803 needUpdateUI |= flags;
4807 * Force scroll and keep position relative to top of window.
4809 * If stuttered = true and not already at first/last row, move to first/last row of window.
4810 * If stuttered = true and already at first/last row, scroll as normal.
4812 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
4813 int topLineNew;
4814 SelectionPosition newPos;
4816 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
4817 int topStutterLine = topLine + caretYSlop;
4818 int bottomStutterLine =
4819 pdoc->LineFromPosition(PositionFromLocation(
4820 Point(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
4821 - caretYSlop - 1;
4823 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
4824 topLineNew = topLine;
4825 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
4826 false, false, UserVirtualSpace());
4828 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
4829 topLineNew = topLine;
4830 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
4831 false, false, UserVirtualSpace());
4833 } else {
4834 Point pt = LocationFromPosition(sel.MainCaret());
4836 topLineNew = Platform::Clamp(
4837 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
4838 newPos = SPositionFromLocation(
4839 Point(lastXChosen - xOffset, pt.y + direction * (vs.lineHeight * LinesToScroll())),
4840 false, false, UserVirtualSpace());
4843 if (topLineNew != topLine) {
4844 SetTopLine(topLineNew);
4845 MovePositionTo(newPos, selt);
4846 Redraw();
4847 SetVerticalScrollPos();
4848 } else {
4849 MovePositionTo(newPos, selt);
4853 void Editor::ChangeCaseOfSelection(int caseMapping) {
4854 UndoGroup ug(pdoc);
4855 for (size_t r=0; r<sel.Count(); r++) {
4856 SelectionRange current = sel.Range(r);
4857 SelectionRange currentNoVS = current;
4858 currentNoVS.ClearVirtualSpace();
4859 char *text = CopyRange(currentNoVS.Start().Position(), currentNoVS.End().Position());
4860 size_t rangeBytes = currentNoVS.Length();
4861 if (rangeBytes > 0) {
4862 std::string sText(text, rangeBytes);
4864 std::string sMapped = CaseMapString(sText, caseMapping);
4866 if (sMapped != sText) {
4867 size_t firstDifference = 0;
4868 while (sMapped[firstDifference] == sText[firstDifference])
4869 firstDifference++;
4870 size_t lastDifference = sMapped.size() - 1;
4871 while (sMapped[lastDifference] == sText[lastDifference])
4872 lastDifference--;
4873 size_t endSame = sMapped.size() - 1 - lastDifference;
4874 pdoc->DeleteChars(
4875 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
4876 static_cast<int>(rangeBytes - firstDifference - endSame));
4877 pdoc->InsertString(
4878 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
4879 sMapped.c_str() + firstDifference,
4880 static_cast<int>(lastDifference - firstDifference + 1));
4881 // Automatic movement changes selection so reset to exactly the same as it was.
4882 sel.Range(r) = current;
4885 delete []text;
4889 void Editor::LineTranspose() {
4890 int line = pdoc->LineFromPosition(sel.MainCaret());
4891 if (line > 0) {
4892 UndoGroup ug(pdoc);
4893 int startPrev = pdoc->LineStart(line - 1);
4894 int endPrev = pdoc->LineEnd(line - 1);
4895 int start = pdoc->LineStart(line);
4896 int end = pdoc->LineEnd(line);
4897 char *line1 = CopyRange(startPrev, endPrev);
4898 int len1 = endPrev - startPrev;
4899 char *line2 = CopyRange(start, end);
4900 int len2 = end - start;
4901 pdoc->DeleteChars(start, len2);
4902 pdoc->DeleteChars(startPrev, len1);
4903 pdoc->InsertString(startPrev, line2, len2);
4904 pdoc->InsertString(start - len1 + len2, line1, len1);
4905 MovePositionTo(SelectionPosition(start - len1 + len2));
4906 delete []line1;
4907 delete []line2;
4911 void Editor::Duplicate(bool forLine) {
4912 if (sel.Empty()) {
4913 forLine = true;
4915 UndoGroup ug(pdoc);
4916 const char *eol = "";
4917 int eolLen = 0;
4918 if (forLine) {
4919 eol = StringFromEOLMode(pdoc->eolMode);
4920 eolLen = istrlen(eol);
4922 for (size_t r=0; r<sel.Count(); r++) {
4923 SelectionPosition start = sel.Range(r).Start();
4924 SelectionPosition end = sel.Range(r).End();
4925 if (forLine) {
4926 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4927 start = SelectionPosition(pdoc->LineStart(line));
4928 end = SelectionPosition(pdoc->LineEnd(line));
4930 char *text = CopyRange(start.Position(), end.Position());
4931 if (forLine)
4932 pdoc->InsertString(end.Position(), eol, eolLen);
4933 pdoc->InsertString(end.Position() + eolLen, text, SelectionRange(end, start).Length());
4934 delete []text;
4936 if (sel.Count() && sel.IsRectangular()) {
4937 SelectionPosition last = sel.Last();
4938 if (forLine) {
4939 int line = pdoc->LineFromPosition(last.Position());
4940 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
4942 if (sel.Rectangular().anchor > sel.Rectangular().caret)
4943 sel.Rectangular().anchor = last;
4944 else
4945 sel.Rectangular().caret = last;
4946 SetRectangularRange();
4950 void Editor::CancelModes() {
4951 sel.SetMoveExtends(false);
4954 void Editor::NewLine() {
4955 // Remove non-main ranges
4956 InvalidateSelection(sel.RangeMain(), true);
4957 sel.SetSelection(sel.RangeMain());
4959 // Clear main range and insert line end
4960 bool needGroupUndo = !sel.Empty();
4961 if (needGroupUndo)
4962 pdoc->BeginUndoAction();
4964 if (!sel.Empty())
4965 ClearSelection();
4966 const char *eol = "\n";
4967 if (pdoc->eolMode == SC_EOL_CRLF) {
4968 eol = "\r\n";
4969 } else if (pdoc->eolMode == SC_EOL_CR) {
4970 eol = "\r";
4971 } // else SC_EOL_LF -> "\n" already set
4972 bool inserted = pdoc->InsertCString(sel.MainCaret(), eol);
4973 // Want to end undo group before NotifyChar as applications often modify text here
4974 if (needGroupUndo)
4975 pdoc->EndUndoAction();
4976 if (inserted) {
4977 SetEmptySelection(sel.MainCaret() + istrlen(eol));
4978 while (*eol) {
4979 NotifyChar(*eol);
4980 if (recordingMacro) {
4981 char txt[2];
4982 txt[0] = *eol;
4983 txt[1] = '\0';
4984 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
4986 eol++;
4989 SetLastXChosen();
4990 SetScrollBars();
4991 EnsureCaretVisible();
4992 // Avoid blinking during rapid typing:
4993 ShowCaretAtCurrentPosition();
4996 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
4997 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
4998 if (sel.IsRectangular()) {
4999 if (selt == Selection::noSel) {
5000 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
5001 } else {
5002 caretToUse = sel.Rectangular().caret;
5006 Point pt = LocationFromPosition(caretToUse);
5007 int skipLines = 0;
5009 if (vs.annotationVisible) {
5010 int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
5011 Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
5012 int subLine = (pt.y - ptStartLine.y) / vs.lineHeight;
5014 if (direction < 0 && subLine == 0) {
5015 int lineDisplay = cs.DisplayFromDoc(lineDoc);
5016 if (lineDisplay > 0) {
5017 skipLines = pdoc->AnnotationLines(cs.DocFromDisplay(lineDisplay - 1));
5019 } else if (direction > 0 && subLine >= (cs.GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
5020 skipLines = pdoc->AnnotationLines(lineDoc);
5024 int newY = pt.y + (1 + skipLines) * direction * vs.lineHeight;
5025 SelectionPosition posNew = SPositionFromLocation(
5026 Point(lastXChosen - xOffset, newY), false, false, UserVirtualSpace());
5028 if (direction < 0) {
5029 // Line wrapping may lead to a location on the same line, so
5030 // seek back if that is the case.
5031 Point ptNew = LocationFromPosition(posNew.Position());
5032 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
5033 posNew.Add(-1);
5034 posNew.SetVirtualSpace(0);
5035 ptNew = LocationFromPosition(posNew.Position());
5037 } else if (direction > 0 && posNew.Position() != pdoc->Length()) {
5038 // There is an equivalent case when moving down which skips
5039 // over a line.
5040 Point ptNew = LocationFromPosition(posNew.Position());
5041 while ((posNew.Position() > caretToUse.Position()) && (ptNew.y > newY)) {
5042 posNew.Add(-1);
5043 posNew.SetVirtualSpace(0);
5044 ptNew = LocationFromPosition(posNew.Position());
5048 MovePositionTo(MovePositionSoVisible(posNew, direction), selt);
5051 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
5052 int lineDoc, savedPos = sel.MainCaret();
5053 do {
5054 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
5055 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
5056 if (direction > 0) {
5057 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
5058 if (selt == Selection::noSel) {
5059 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
5061 break;
5064 } while (!cs.GetVisible(lineDoc));
5067 int Editor::StartEndDisplayLine(int pos, bool start) {
5068 RefreshStyleData();
5069 int line = pdoc->LineFromPosition(pos);
5070 AutoSurface surface(this);
5071 AutoLineLayout ll(llc, RetrieveLineLayout(line));
5072 int posRet = INVALID_POSITION;
5073 if (surface && ll) {
5074 unsigned int posLineStart = pdoc->LineStart(line);
5075 LayoutLine(line, surface, vs, ll, wrapWidth);
5076 int posInLine = pos - posLineStart;
5077 if (posInLine <= ll->maxLineLength) {
5078 for (int subLine = 0; subLine < ll->lines; subLine++) {
5079 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
5080 if (start) {
5081 posRet = ll->LineStart(subLine) + posLineStart;
5082 } else {
5083 if (subLine == ll->lines - 1)
5084 posRet = ll->LineStart(subLine + 1) + posLineStart;
5085 else
5086 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
5092 if (posRet == INVALID_POSITION) {
5093 return pos;
5094 } else {
5095 return posRet;
5099 int Editor::KeyCommand(unsigned int iMessage) {
5100 switch (iMessage) {
5101 case SCI_LINEDOWN:
5102 CursorUpOrDown(1);
5103 break;
5104 case SCI_LINEDOWNEXTEND:
5105 CursorUpOrDown(1, Selection::selStream);
5106 break;
5107 case SCI_LINEDOWNRECTEXTEND:
5108 CursorUpOrDown(1, Selection::selRectangle);
5109 break;
5110 case SCI_PARADOWN:
5111 ParaUpOrDown(1);
5112 break;
5113 case SCI_PARADOWNEXTEND:
5114 ParaUpOrDown(1, Selection::selStream);
5115 break;
5116 case SCI_LINESCROLLDOWN:
5117 ScrollTo(topLine + 1);
5118 MoveCaretInsideView(false);
5119 break;
5120 case SCI_LINEUP:
5121 CursorUpOrDown(-1);
5122 break;
5123 case SCI_LINEUPEXTEND:
5124 CursorUpOrDown(-1, Selection::selStream);
5125 break;
5126 case SCI_LINEUPRECTEXTEND:
5127 CursorUpOrDown(-1, Selection::selRectangle);
5128 break;
5129 case SCI_PARAUP:
5130 ParaUpOrDown(-1);
5131 break;
5132 case SCI_PARAUPEXTEND:
5133 ParaUpOrDown(-1, Selection::selStream);
5134 break;
5135 case SCI_LINESCROLLUP:
5136 ScrollTo(topLine - 1);
5137 MoveCaretInsideView(false);
5138 break;
5139 case SCI_CHARLEFT:
5140 if (SelectionEmpty() || sel.MoveExtends()) {
5141 if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5142 SelectionPosition spCaret = sel.RangeMain().caret;
5143 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5144 MovePositionTo(spCaret);
5145 } else {
5146 MovePositionTo(MovePositionSoVisible(
5147 SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
5149 } else {
5150 MovePositionTo(sel.LimitsForRectangularElseMain().start);
5152 SetLastXChosen();
5153 break;
5154 case SCI_CHARLEFTEXTEND:
5155 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5156 SelectionPosition spCaret = sel.RangeMain().caret;
5157 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5158 MovePositionTo(spCaret, Selection::selStream);
5159 } else {
5160 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
5162 SetLastXChosen();
5163 break;
5164 case SCI_CHARLEFTRECTEXTEND:
5165 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5166 SelectionPosition spCaret = sel.RangeMain().caret;
5167 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5168 MovePositionTo(spCaret, Selection::selRectangle);
5169 } else {
5170 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
5172 SetLastXChosen();
5173 break;
5174 case SCI_CHARRIGHT:
5175 if (SelectionEmpty() || sel.MoveExtends()) {
5176 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5177 SelectionPosition spCaret = sel.RangeMain().caret;
5178 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5179 MovePositionTo(spCaret);
5180 } else {
5181 MovePositionTo(MovePositionSoVisible(
5182 SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
5184 } else {
5185 MovePositionTo(sel.LimitsForRectangularElseMain().end);
5187 SetLastXChosen();
5188 break;
5189 case SCI_CHARRIGHTEXTEND:
5190 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5191 SelectionPosition spCaret = sel.RangeMain().caret;
5192 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5193 MovePositionTo(spCaret, Selection::selStream);
5194 } else {
5195 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
5197 SetLastXChosen();
5198 break;
5199 case SCI_CHARRIGHTRECTEXTEND:
5200 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5201 SelectionPosition spCaret = sel.RangeMain().caret;
5202 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5203 MovePositionTo(spCaret, Selection::selRectangle);
5204 } else {
5205 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
5207 SetLastXChosen();
5208 break;
5209 case SCI_WORDLEFT:
5210 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
5211 SetLastXChosen();
5212 break;
5213 case SCI_WORDLEFTEXTEND:
5214 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
5215 SetLastXChosen();
5216 break;
5217 case SCI_WORDRIGHT:
5218 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
5219 SetLastXChosen();
5220 break;
5221 case SCI_WORDRIGHTEXTEND:
5222 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
5223 SetLastXChosen();
5224 break;
5226 case SCI_WORDLEFTEND:
5227 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
5228 SetLastXChosen();
5229 break;
5230 case SCI_WORDLEFTENDEXTEND:
5231 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
5232 SetLastXChosen();
5233 break;
5234 case SCI_WORDRIGHTEND:
5235 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
5236 SetLastXChosen();
5237 break;
5238 case SCI_WORDRIGHTENDEXTEND:
5239 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
5240 SetLastXChosen();
5241 break;
5243 case SCI_HOME:
5244 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5245 SetLastXChosen();
5246 break;
5247 case SCI_HOMEEXTEND:
5248 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
5249 SetLastXChosen();
5250 break;
5251 case SCI_HOMERECTEXTEND:
5252 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
5253 SetLastXChosen();
5254 break;
5255 case SCI_LINEEND:
5256 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
5257 SetLastXChosen();
5258 break;
5259 case SCI_LINEENDEXTEND:
5260 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
5261 SetLastXChosen();
5262 break;
5263 case SCI_LINEENDRECTEXTEND:
5264 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
5265 SetLastXChosen();
5266 break;
5267 case SCI_HOMEWRAP: {
5268 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5269 if (sel.RangeMain().caret <= homePos)
5270 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5271 MovePositionTo(homePos);
5272 SetLastXChosen();
5274 break;
5275 case SCI_HOMEWRAPEXTEND: {
5276 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5277 if (sel.RangeMain().caret <= homePos)
5278 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5279 MovePositionTo(homePos, Selection::selStream);
5280 SetLastXChosen();
5282 break;
5283 case SCI_LINEENDWRAP: {
5284 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5285 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5286 if (endPos > realEndPos // if moved past visible EOLs
5287 || sel.RangeMain().caret >= endPos) // if at end of display line already
5288 endPos = realEndPos;
5289 MovePositionTo(endPos);
5290 SetLastXChosen();
5292 break;
5293 case SCI_LINEENDWRAPEXTEND: {
5294 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5295 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5296 if (endPos > realEndPos // if moved past visible EOLs
5297 || sel.RangeMain().caret >= endPos) // if at end of display line already
5298 endPos = realEndPos;
5299 MovePositionTo(endPos, Selection::selStream);
5300 SetLastXChosen();
5302 break;
5303 case SCI_DOCUMENTSTART:
5304 MovePositionTo(0);
5305 SetLastXChosen();
5306 break;
5307 case SCI_DOCUMENTSTARTEXTEND:
5308 MovePositionTo(0, Selection::selStream);
5309 SetLastXChosen();
5310 break;
5311 case SCI_DOCUMENTEND:
5312 MovePositionTo(pdoc->Length());
5313 SetLastXChosen();
5314 break;
5315 case SCI_DOCUMENTENDEXTEND:
5316 MovePositionTo(pdoc->Length(), Selection::selStream);
5317 SetLastXChosen();
5318 break;
5319 case SCI_STUTTEREDPAGEUP:
5320 PageMove(-1, Selection::noSel, true);
5321 break;
5322 case SCI_STUTTEREDPAGEUPEXTEND:
5323 PageMove(-1, Selection::selStream, true);
5324 break;
5325 case SCI_STUTTEREDPAGEDOWN:
5326 PageMove(1, Selection::noSel, true);
5327 break;
5328 case SCI_STUTTEREDPAGEDOWNEXTEND:
5329 PageMove(1, Selection::selStream, true);
5330 break;
5331 case SCI_PAGEUP:
5332 PageMove(-1);
5333 break;
5334 case SCI_PAGEUPEXTEND:
5335 PageMove(-1, Selection::selStream);
5336 break;
5337 case SCI_PAGEUPRECTEXTEND:
5338 PageMove(-1, Selection::selRectangle);
5339 break;
5340 case SCI_PAGEDOWN:
5341 PageMove(1);
5342 break;
5343 case SCI_PAGEDOWNEXTEND:
5344 PageMove(1, Selection::selStream);
5345 break;
5346 case SCI_PAGEDOWNRECTEXTEND:
5347 PageMove(1, Selection::selRectangle);
5348 break;
5349 case SCI_EDITTOGGLEOVERTYPE:
5350 inOverstrike = !inOverstrike;
5351 DropCaret();
5352 ShowCaretAtCurrentPosition();
5353 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
5354 NotifyUpdateUI();
5355 break;
5356 case SCI_CANCEL: // Cancel any modes - handled in subclass
5357 // Also unselect text
5358 CancelModes();
5359 break;
5360 case SCI_DELETEBACK:
5361 DelCharBack(true);
5362 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5363 SetLastXChosen();
5365 EnsureCaretVisible();
5366 break;
5367 case SCI_DELETEBACKNOTLINE:
5368 DelCharBack(false);
5369 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5370 SetLastXChosen();
5372 EnsureCaretVisible();
5373 break;
5374 case SCI_TAB:
5375 Indent(true);
5376 if (caretSticky == SC_CARETSTICKY_OFF) {
5377 SetLastXChosen();
5379 EnsureCaretVisible();
5380 ShowCaretAtCurrentPosition(); // Avoid blinking
5381 break;
5382 case SCI_BACKTAB:
5383 Indent(false);
5384 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5385 SetLastXChosen();
5387 EnsureCaretVisible();
5388 ShowCaretAtCurrentPosition(); // Avoid blinking
5389 break;
5390 case SCI_NEWLINE:
5391 NewLine();
5392 break;
5393 case SCI_FORMFEED:
5394 AddChar('\f');
5395 break;
5396 case SCI_VCHOME:
5397 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
5398 SetLastXChosen();
5399 break;
5400 case SCI_VCHOMEEXTEND:
5401 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
5402 SetLastXChosen();
5403 break;
5404 case SCI_VCHOMERECTEXTEND:
5405 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
5406 SetLastXChosen();
5407 break;
5408 case SCI_VCHOMEWRAP: {
5409 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5410 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5411 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5412 homePos = viewLineStart;
5414 MovePositionTo(homePos);
5415 SetLastXChosen();
5417 break;
5418 case SCI_VCHOMEWRAPEXTEND: {
5419 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5420 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5421 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5422 homePos = viewLineStart;
5424 MovePositionTo(homePos, Selection::selStream);
5425 SetLastXChosen();
5427 break;
5428 case SCI_ZOOMIN:
5429 if (vs.zoomLevel < 20) {
5430 vs.zoomLevel++;
5431 InvalidateStyleRedraw();
5432 NotifyZoom();
5434 break;
5435 case SCI_ZOOMOUT:
5436 if (vs.zoomLevel > -10) {
5437 vs.zoomLevel--;
5438 InvalidateStyleRedraw();
5439 NotifyZoom();
5441 break;
5442 case SCI_DELWORDLEFT: {
5443 int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
5444 pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
5445 sel.RangeMain().ClearVirtualSpace();
5446 SetLastXChosen();
5448 break;
5449 case SCI_DELWORDRIGHT: {
5450 UndoGroup ug(pdoc);
5451 sel.RangeMain().caret = SelectionPosition(
5452 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5453 sel.RangeMain().anchor = sel.RangeMain().caret;
5454 int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
5455 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5457 break;
5458 case SCI_DELWORDRIGHTEND: {
5459 UndoGroup ug(pdoc);
5460 sel.RangeMain().caret = SelectionPosition(
5461 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5462 int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
5463 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5465 break;
5466 case SCI_DELLINELEFT: {
5467 int line = pdoc->LineFromPosition(sel.MainCaret());
5468 int start = pdoc->LineStart(line);
5469 pdoc->DeleteChars(start, sel.MainCaret() - start);
5470 sel.RangeMain().ClearVirtualSpace();
5471 SetLastXChosen();
5473 break;
5474 case SCI_DELLINERIGHT: {
5475 int line = pdoc->LineFromPosition(sel.MainCaret());
5476 int end = pdoc->LineEnd(line);
5477 pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
5479 break;
5480 case SCI_LINECOPY: {
5481 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5482 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5483 CopyRangeToClipboard(pdoc->LineStart(lineStart),
5484 pdoc->LineStart(lineEnd + 1));
5486 break;
5487 case SCI_LINECUT: {
5488 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5489 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5490 int start = pdoc->LineStart(lineStart);
5491 int end = pdoc->LineStart(lineEnd + 1);
5492 SetSelection(start, end);
5493 Cut();
5494 SetLastXChosen();
5496 break;
5497 case SCI_LINEDELETE: {
5498 int line = pdoc->LineFromPosition(sel.MainCaret());
5499 int start = pdoc->LineStart(line);
5500 int end = pdoc->LineStart(line + 1);
5501 pdoc->DeleteChars(start, end - start);
5503 break;
5504 case SCI_LINETRANSPOSE:
5505 LineTranspose();
5506 break;
5507 case SCI_LINEDUPLICATE:
5508 Duplicate(true);
5509 break;
5510 case SCI_SELECTIONDUPLICATE:
5511 Duplicate(false);
5512 break;
5513 case SCI_LOWERCASE:
5514 ChangeCaseOfSelection(cmLower);
5515 break;
5516 case SCI_UPPERCASE:
5517 ChangeCaseOfSelection(cmUpper);
5518 break;
5519 case SCI_WORDPARTLEFT:
5520 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
5521 SetLastXChosen();
5522 break;
5523 case SCI_WORDPARTLEFTEXTEND:
5524 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
5525 SetLastXChosen();
5526 break;
5527 case SCI_WORDPARTRIGHT:
5528 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
5529 SetLastXChosen();
5530 break;
5531 case SCI_WORDPARTRIGHTEXTEND:
5532 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
5533 SetLastXChosen();
5534 break;
5535 case SCI_HOMEDISPLAY:
5536 MovePositionTo(MovePositionSoVisible(
5537 StartEndDisplayLine(sel.MainCaret(), true), -1));
5538 SetLastXChosen();
5539 break;
5540 case SCI_HOMEDISPLAYEXTEND:
5541 MovePositionTo(MovePositionSoVisible(
5542 StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
5543 SetLastXChosen();
5544 break;
5545 case SCI_LINEENDDISPLAY:
5546 MovePositionTo(MovePositionSoVisible(
5547 StartEndDisplayLine(sel.MainCaret(), false), 1));
5548 SetLastXChosen();
5549 break;
5550 case SCI_LINEENDDISPLAYEXTEND:
5551 MovePositionTo(MovePositionSoVisible(
5552 StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
5553 SetLastXChosen();
5554 break;
5555 case SCI_SCROLLTOSTART:
5556 ScrollTo(0);
5557 break;
5558 case SCI_SCROLLTOEND:
5559 ScrollTo(MaxScrollPos());
5560 break;
5562 return 0;
5565 int Editor::KeyDefault(int, int) {
5566 return 0;
5569 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
5570 DwellEnd(false);
5571 int msg = kmap.Find(key, modifiers);
5572 if (msg) {
5573 if (consumed)
5574 *consumed = true;
5575 return WndProc(msg, 0, 0);
5576 } else {
5577 if (consumed)
5578 *consumed = false;
5579 return KeyDefault(key, modifiers);
5583 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
5584 int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
5585 (alt ? SCI_ALT : 0);
5586 return KeyDownWithModifiers(key, modifiers, consumed);
5589 void Editor::Indent(bool forwards) {
5590 for (size_t r=0; r<sel.Count(); r++) {
5591 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
5592 int caretPosition = sel.Range(r).caret.Position();
5593 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
5594 if (lineOfAnchor == lineCurrentPos) {
5595 if (forwards) {
5596 UndoGroup ug(pdoc);
5597 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
5598 caretPosition = sel.Range(r).caret.Position();
5599 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
5600 pdoc->tabIndents) {
5601 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5602 int indentationStep = pdoc->IndentSize();
5603 pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
5604 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5605 } else {
5606 if (pdoc->useTabs) {
5607 pdoc->InsertChar(caretPosition, '\t');
5608 sel.Range(r) = SelectionRange(caretPosition+1);
5609 } else {
5610 int numSpaces = (pdoc->tabInChars) -
5611 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
5612 if (numSpaces < 1)
5613 numSpaces = pdoc->tabInChars;
5614 for (int i = 0; i < numSpaces; i++) {
5615 pdoc->InsertChar(caretPosition + i, ' ');
5617 sel.Range(r) = SelectionRange(caretPosition+numSpaces);
5620 } else {
5621 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
5622 pdoc->tabIndents) {
5623 UndoGroup ug(pdoc);
5624 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5625 int indentationStep = pdoc->IndentSize();
5626 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
5627 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5628 } else {
5629 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
5630 pdoc->tabInChars;
5631 if (newColumn < 0)
5632 newColumn = 0;
5633 int newPos = caretPosition;
5634 while (pdoc->GetColumn(newPos) > newColumn)
5635 newPos--;
5636 sel.Range(r) = SelectionRange(newPos);
5639 } else { // Multiline
5640 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
5641 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
5642 // Multiple lines selected so indent / dedent
5643 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
5644 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
5645 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
5646 lineBottomSel--; // If not selecting any characters on a line, do not indent
5648 UndoGroup ug(pdoc);
5649 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
5651 if (lineOfAnchor < lineCurrentPos) {
5652 if (currentPosPosOnLine == 0)
5653 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5654 else
5655 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
5656 } else {
5657 if (anchorPosOnLine == 0)
5658 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5659 else
5660 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
5666 class CaseFolderASCII : public CaseFolderTable {
5667 public:
5668 CaseFolderASCII() {
5669 StandardASCII();
5671 ~CaseFolderASCII() {
5676 CaseFolder *Editor::CaseFolderForEncoding() {
5677 // Simple default that only maps ASCII upper case to lower case.
5678 return new CaseFolderASCII();
5682 * Search of a text in the document, in the given range.
5683 * @return The position of the found text, -1 if not found.
5685 long Editor::FindText(
5686 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5687 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5688 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
5690 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
5691 int lengthFound = istrlen(ft->lpstrText);
5692 std::auto_ptr<CaseFolder> pcf(CaseFolderForEncoding());
5693 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
5694 (wParam & SCFIND_MATCHCASE) != 0,
5695 (wParam & SCFIND_WHOLEWORD) != 0,
5696 (wParam & SCFIND_WORDSTART) != 0,
5697 (wParam & SCFIND_REGEXP) != 0,
5698 wParam,
5699 &lengthFound,
5700 pcf.get());
5701 if (pos != -1) {
5702 ft->chrgText.cpMin = pos;
5703 ft->chrgText.cpMax = pos + lengthFound;
5705 return pos;
5709 * Relocatable search support : Searches relative to current selection
5710 * point and sets the selection to the found text range with
5711 * each search.
5714 * Anchor following searches at current selection start: This allows
5715 * multiple incremental interactive searches to be macro recorded
5716 * while still setting the selection to found text so the find/select
5717 * operation is self-contained.
5719 void Editor::SearchAnchor() {
5720 searchAnchor = SelectionStart().Position();
5723 // Simple RAII wrapper for CaseFolder as std::auto_ptr is now deprecated
5724 class ScopedCaseFolder {
5725 CaseFolder *pcf;
5726 public:
5727 ScopedCaseFolder(CaseFolder *pcf_) : pcf(pcf_) {
5729 ~ScopedCaseFolder() {
5730 delete pcf;
5731 pcf = 0;
5733 CaseFolder *get() const { return pcf; }
5737 * Find text from current search anchor: Must call @c SearchAnchor first.
5738 * Used for next text and previous text requests.
5739 * @return The position of the found text, -1 if not found.
5741 long Editor::SearchText(
5742 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
5743 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5744 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5745 sptr_t lParam) { ///< The text to search for.
5747 const char *txt = reinterpret_cast<char *>(lParam);
5748 int pos;
5749 int lengthFound = istrlen(txt);
5750 ScopedCaseFolder pcf(CaseFolderForEncoding());
5751 if (iMessage == SCI_SEARCHNEXT) {
5752 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
5753 (wParam & SCFIND_MATCHCASE) != 0,
5754 (wParam & SCFIND_WHOLEWORD) != 0,
5755 (wParam & SCFIND_WORDSTART) != 0,
5756 (wParam & SCFIND_REGEXP) != 0,
5757 wParam,
5758 &lengthFound,
5759 pcf.get());
5760 } else {
5761 pos = pdoc->FindText(searchAnchor, 0, txt,
5762 (wParam & SCFIND_MATCHCASE) != 0,
5763 (wParam & SCFIND_WHOLEWORD) != 0,
5764 (wParam & SCFIND_WORDSTART) != 0,
5765 (wParam & SCFIND_REGEXP) != 0,
5766 wParam,
5767 &lengthFound,
5768 pcf.get());
5770 if (pos != -1) {
5771 SetSelection(pos, pos + lengthFound);
5774 return pos;
5777 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
5778 std::string ret(s);
5779 for (size_t i=0; i<ret.size(); i++) {
5780 switch (caseMapping) {
5781 case cmUpper:
5782 if (ret[i] >= 'a' && ret[i] <= 'z')
5783 ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
5784 break;
5785 case cmLower:
5786 if (ret[i] >= 'A' && ret[i] <= 'Z')
5787 ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
5788 break;
5791 return ret;
5795 * Search for text in the target range of the document.
5796 * @return The position of the found text, -1 if not found.
5798 long Editor::SearchInTarget(const char *text, int length) {
5799 int lengthFound = length;
5801 ScopedCaseFolder pcf(CaseFolderForEncoding());
5802 int pos = pdoc->FindText(targetStart, targetEnd, text,
5803 (searchFlags & SCFIND_MATCHCASE) != 0,
5804 (searchFlags & SCFIND_WHOLEWORD) != 0,
5805 (searchFlags & SCFIND_WORDSTART) != 0,
5806 (searchFlags & SCFIND_REGEXP) != 0,
5807 searchFlags,
5808 &lengthFound,
5809 pcf.get());
5810 if (pos != -1) {
5811 targetStart = pos;
5812 targetEnd = pos + lengthFound;
5814 return pos;
5817 void Editor::GoToLine(int lineNo) {
5818 if (lineNo > pdoc->LinesTotal())
5819 lineNo = pdoc->LinesTotal();
5820 if (lineNo < 0)
5821 lineNo = 0;
5822 SetEmptySelection(pdoc->LineStart(lineNo));
5823 ShowCaretAtCurrentPosition();
5824 EnsureCaretVisible();
5827 static bool Close(Point pt1, Point pt2) {
5828 if (abs(pt1.x - pt2.x) > 3)
5829 return false;
5830 if (abs(pt1.y - pt2.y) > 3)
5831 return false;
5832 return true;
5835 char *Editor::CopyRange(int start, int end) {
5836 char *text = 0;
5837 if (start < end) {
5838 int len = end - start;
5839 text = new char[len + 1];
5840 for (int i = 0; i < len; i++) {
5841 text[i] = pdoc->CharAt(start + i);
5843 text[len] = '\0';
5845 return text;
5848 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
5849 if (sel.Empty()) {
5850 if (allowLineCopy) {
5851 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
5852 int start = pdoc->LineStart(currentLine);
5853 int end = pdoc->LineEnd(currentLine);
5855 char *text = CopyRange(start, end);
5856 size_t textLen = text ? strlen(text) : 0;
5857 // include room for \r\n\0
5858 textLen += 3;
5859 char *textWithEndl = new char[textLen];
5860 textWithEndl[0] = '\0';
5861 if (text)
5862 strncat(textWithEndl, text, textLen);
5863 if (pdoc->eolMode != SC_EOL_LF)
5864 strncat(textWithEndl, "\r", textLen);
5865 if (pdoc->eolMode != SC_EOL_CR)
5866 strncat(textWithEndl, "\n", textLen);
5867 ss->Set(textWithEndl, static_cast<int>(strlen(textWithEndl) + 1),
5868 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, true);
5869 delete []text;
5871 } else {
5872 int delimiterLength = 0;
5873 if (sel.selType == Selection::selRectangle) {
5874 if (pdoc->eolMode == SC_EOL_CRLF) {
5875 delimiterLength = 2;
5876 } else {
5877 delimiterLength = 1;
5880 size_t size = sel.Length() + delimiterLength * sel.Count();
5881 char *text = new char[size + 1];
5882 int j = 0;
5883 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
5884 if (sel.selType == Selection::selRectangle)
5885 std::sort(rangesInOrder.begin(), rangesInOrder.end());
5886 for (size_t r=0; r<rangesInOrder.size(); r++) {
5887 SelectionRange current = rangesInOrder[r];
5888 for (int i = current.Start().Position();
5889 i < current.End().Position();
5890 i++) {
5891 text[j++] = pdoc->CharAt(i);
5893 if (sel.selType == Selection::selRectangle) {
5894 if (pdoc->eolMode != SC_EOL_LF) {
5895 text[j++] = '\r';
5897 if (pdoc->eolMode != SC_EOL_CR) {
5898 text[j++] = '\n';
5902 text[size] = '\0';
5903 ss->Set(text, static_cast<int>(size + 1), pdoc->dbcsCodePage,
5904 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
5908 void Editor::CopyRangeToClipboard(int start, int end) {
5909 start = pdoc->ClampPositionIntoDocument(start);
5910 end = pdoc->ClampPositionIntoDocument(end);
5911 SelectionText selectedText;
5912 selectedText.Set(CopyRange(start, end), end - start + 1,
5913 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5914 CopyToClipboard(selectedText);
5917 void Editor::CopyText(int length, const char *text) {
5918 SelectionText selectedText;
5919 selectedText.Copy(text, length + 1,
5920 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5921 CopyToClipboard(selectedText);
5924 void Editor::SetDragPosition(SelectionPosition newPos) {
5925 if (newPos.Position() >= 0) {
5926 newPos = MovePositionOutsideChar(newPos, 1);
5927 posDrop = newPos;
5929 if (!(posDrag == newPos)) {
5930 caret.on = true;
5931 SetTicking(true);
5932 InvalidateCaret();
5933 posDrag = newPos;
5934 InvalidateCaret();
5938 void Editor::DisplayCursor(Window::Cursor c) {
5939 if (cursorMode == SC_CURSORNORMAL)
5940 wMain.SetCursor(c);
5941 else
5942 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
5945 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
5946 int xMove = ptStart.x - ptNow.x;
5947 int yMove = ptStart.y - ptNow.y;
5948 int distanceSquared = xMove * xMove + yMove * yMove;
5949 return distanceSquared > 16;
5952 void Editor::StartDrag() {
5953 // Always handled by subclasses
5954 //SetMouseCapture(true);
5955 //DisplayCursor(Window::cursorArrow);
5958 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
5959 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
5960 if (inDragDrop == ddDragging)
5961 dropWentOutside = false;
5963 bool positionWasInSelection = PositionInSelection(position.Position());
5965 bool positionOnEdgeOfSelection =
5966 (position == SelectionStart()) || (position == SelectionEnd());
5968 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
5969 (positionOnEdgeOfSelection && !moving)) {
5971 SelectionPosition selStart = SelectionStart();
5972 SelectionPosition selEnd = SelectionEnd();
5974 UndoGroup ug(pdoc);
5976 SelectionPosition positionAfterDeletion = position;
5977 if ((inDragDrop == ddDragging) && moving) {
5978 // Remove dragged out text
5979 if (rectangular || sel.selType == Selection::selLines) {
5980 for (size_t r=0; r<sel.Count(); r++) {
5981 if (position >= sel.Range(r).Start()) {
5982 if (position > sel.Range(r).End()) {
5983 positionAfterDeletion.Add(-sel.Range(r).Length());
5984 } else {
5985 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
5989 } else {
5990 if (position > selStart) {
5991 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
5994 ClearSelection();
5996 position = positionAfterDeletion;
5998 if (rectangular) {
5999 PasteRectangular(position, value, istrlen(value));
6000 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
6001 SetEmptySelection(position);
6002 } else {
6003 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
6004 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
6005 if (pdoc->InsertCString(position.Position(), value)) {
6006 SelectionPosition posAfterInsertion = position;
6007 posAfterInsertion.Add(istrlen(value));
6008 SetSelection(posAfterInsertion, position);
6011 } else if (inDragDrop == ddDragging) {
6012 SetEmptySelection(position);
6017 * @return true if given position is inside the selection,
6019 bool Editor::PositionInSelection(int pos) {
6020 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
6021 for (size_t r=0; r<sel.Count(); r++) {
6022 if (sel.Range(r).Contains(pos))
6023 return true;
6025 return false;
6028 bool Editor::PointInSelection(Point pt) {
6029 SelectionPosition pos = SPositionFromLocation(pt, false, true);
6030 Point ptPos = LocationFromPosition(pos);
6031 for (size_t r=0; r<sel.Count(); r++) {
6032 SelectionRange range = sel.Range(r);
6033 if (range.Contains(pos)) {
6034 bool hit = true;
6035 if (pos == range.Start()) {
6036 // see if just before selection
6037 if (pt.x < ptPos.x) {
6038 hit = false;
6041 if (pos == range.End()) {
6042 // see if just after selection
6043 if (pt.x > ptPos.x) {
6044 hit = false;
6047 if (hit)
6048 return true;
6051 return false;
6054 bool Editor::PointInSelMargin(Point pt) {
6055 // Really means: "Point in a margin"
6056 if (vs.fixedColumnWidth > 0) { // There is a margin
6057 PRectangle rcSelMargin = GetClientRectangle();
6058 rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth;
6059 return rcSelMargin.Contains(pt);
6060 } else {
6061 return false;
6065 Window::Cursor Editor::GetMarginCursor(Point pt) {
6066 int x = 0;
6067 for (int margin = 0; margin < ViewStyle::margins; margin++) {
6068 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
6069 return static_cast<Window::Cursor>(vs.ms[margin].cursor);
6070 x += vs.ms[margin].width;
6072 return Window::cursorReverseArrow;
6075 void Editor::TrimAndSetSelection(int currentPos_, int anchor_) {
6076 sel.TrimSelection(SelectionRange(currentPos_, anchor_));
6077 SetSelection(currentPos_, anchor_);
6080 void Editor::LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine) {
6081 int selCurrentPos, selAnchorPos;
6082 if (wholeLine) {
6083 int lineCurrent_ = pdoc->LineFromPosition(lineCurrentPos_);
6084 int lineAnchor_ = pdoc->LineFromPosition(lineAnchorPos_);
6085 if (lineAnchorPos_ < lineCurrentPos_) {
6086 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
6087 selAnchorPos = pdoc->LineStart(lineAnchor_);
6088 } else if (lineAnchorPos_ > lineCurrentPos_) {
6089 selCurrentPos = pdoc->LineStart(lineCurrent_);
6090 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
6091 } else { // Same line, select it
6092 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
6093 selAnchorPos = pdoc->LineStart(lineAnchor_);
6095 } else {
6096 if (lineAnchorPos_ < lineCurrentPos_) {
6097 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
6098 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6099 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6100 } else if (lineAnchorPos_ > lineCurrentPos_) {
6101 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
6102 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6103 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
6104 } else { // Same line, select it
6105 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6106 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6107 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6110 TrimAndSetSelection(selCurrentPos, selAnchorPos);
6113 void Editor::WordSelection(int pos) {
6114 if (pos < wordSelectAnchorStartPos) {
6115 // Extend backward to the word containing pos.
6116 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
6117 // This ensures that a series of empty lines isn't counted as a single "word".
6118 if (!pdoc->IsLineEndPosition(pos))
6119 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
6120 TrimAndSetSelection(pos, wordSelectAnchorEndPos);
6121 } else if (pos > wordSelectAnchorEndPos) {
6122 // Extend forward to the word containing the character to the left of pos.
6123 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
6124 // This ensures that a series of empty lines isn't counted as a single "word".
6125 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
6126 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
6127 TrimAndSetSelection(pos, wordSelectAnchorStartPos);
6128 } else {
6129 // Select only the anchored word
6130 if (pos >= originalAnchorPos)
6131 TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
6132 else
6133 TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
6137 void Editor::DwellEnd(bool mouseMoved) {
6138 if (mouseMoved)
6139 ticksToDwell = dwellDelay;
6140 else
6141 ticksToDwell = SC_TIME_FOREVER;
6142 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
6143 dwelling = false;
6144 NotifyDwelling(ptMouseLast, dwelling);
6148 void Editor::MouseLeave() {
6149 SetHotSpotRange(NULL);
6150 if (!HaveMouseCapture()) {
6151 ptMouseLast = Point(-1,-1);
6152 DwellEnd(true);
6156 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
6157 return (!rectangular && ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0))
6158 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
6161 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
6162 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
6163 ptMouseLast = pt;
6164 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
6165 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6166 inDragDrop = ddNone;
6167 sel.SetMoveExtends(false);
6169 if (NotifyMarginClick(pt, shift, ctrl, alt))
6170 return;
6172 NotifyIndicatorClick(true, newPos.Position(), shift, ctrl, alt);
6174 bool inSelMargin = PointInSelMargin(pt);
6175 // In margin ctrl+(double)click should always select everything
6176 if (ctrl && inSelMargin) {
6177 SelectAll();
6178 lastClickTime = curTime;
6179 lastClick = pt;
6180 return;
6182 if (shift && !inSelMargin) {
6183 SetSelection(newPos);
6185 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
6186 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
6187 SetMouseCapture(true);
6188 if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))
6189 SetEmptySelection(newPos.Position());
6190 bool doubleClick = false;
6191 // Stop mouse button bounce changing selection type
6192 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
6193 if (inSelMargin) {
6194 // Inside margin selection type should be either selSubLine or selWholeLine.
6195 if (selectionType == selSubLine) {
6196 // If it is selSubLine, we're inside a *double* click and word wrap is enabled,
6197 // so we switch to selWholeLine in order to select whole line.
6198 selectionType = selWholeLine;
6199 } else if (selectionType != selSubLine && selectionType != selWholeLine) {
6200 // If it is neither, reset selection type to line selection.
6201 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6203 } else {
6204 if (selectionType == selChar) {
6205 selectionType = selWord;
6206 doubleClick = true;
6207 } else if (selectionType == selWord) {
6208 // Since we ended up here, we're inside a *triple* click, which should always select
6209 // whole line irregardless of word wrap being enabled or not.
6210 selectionType = selWholeLine;
6211 } else {
6212 selectionType = selChar;
6213 originalAnchorPos = sel.MainCaret();
6218 if (selectionType == selWord) {
6219 int charPos = originalAnchorPos;
6220 if (sel.MainCaret() == originalAnchorPos) {
6221 charPos = PositionFromLocation(pt, false, true);
6222 charPos = MovePositionOutsideChar(charPos, -1);
6225 int startWord, endWord;
6226 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
6227 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
6228 endWord = pdoc->ExtendWordSelect(charPos, 1);
6229 } else {
6230 // Selecting backwards, or anchor beyond last character on line. In these cases,
6231 // we select the word containing the character to the *left* of the anchor.
6232 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
6233 startWord = pdoc->ExtendWordSelect(charPos, -1);
6234 endWord = pdoc->ExtendWordSelect(startWord, 1);
6235 } else {
6236 // Anchor at start of line; select nothing to begin with.
6237 startWord = charPos;
6238 endWord = charPos;
6242 wordSelectAnchorStartPos = startWord;
6243 wordSelectAnchorEndPos = endWord;
6244 wordSelectInitialCaretPos = sel.MainCaret();
6245 WordSelection(wordSelectInitialCaretPos);
6246 } else if (selectionType == selSubLine || selectionType == selWholeLine) {
6247 lineAnchorPos = newPos.Position();
6248 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6249 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
6250 } else {
6251 SetEmptySelection(sel.MainCaret());
6253 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
6254 if (doubleClick) {
6255 NotifyDoubleClick(pt, shift, ctrl, alt);
6256 if (PositionIsHotspot(newPos.Position()))
6257 NotifyHotSpotDoubleClicked(newPos.Position(), shift, ctrl, alt);
6259 } else { // Single click
6260 if (inSelMargin) {
6261 sel.selType = Selection::selStream;
6262 if (!shift) {
6263 // Single click in margin: select whole line or only subline if word wrap is enabled
6264 lineAnchorPos = newPos.Position();
6265 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6266 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6267 } else {
6268 // Single shift+click in margin: select from line anchor to clicked line
6269 if (sel.MainAnchor() > sel.MainCaret())
6270 lineAnchorPos = sel.MainAnchor() - 1;
6271 else
6272 lineAnchorPos = sel.MainAnchor();
6273 // Reset selection type if there is an empty selection.
6274 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
6275 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
6276 // This ensures that we continue selecting in the same selection mode.
6277 if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
6278 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6279 LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
6282 SetDragPosition(SelectionPosition(invalidPosition));
6283 SetMouseCapture(true);
6284 } else {
6285 if (PointIsHotspot(pt)) {
6286 NotifyHotSpotClicked(newPos.Position(), shift, ctrl, alt);
6287 hotSpotClickPos = PositionFromLocation(pt,true,false);
6289 if (!shift) {
6290 if (PointInSelection(pt) && !SelectionEmpty())
6291 inDragDrop = ddInitial;
6292 else
6293 inDragDrop = ddNone;
6295 SetMouseCapture(true);
6296 if (inDragDrop != ddInitial) {
6297 SetDragPosition(SelectionPosition(invalidPosition));
6298 if (!shift) {
6299 if (ctrl && multipleSelection) {
6300 SelectionRange range(newPos);
6301 sel.TentativeSelection(range);
6302 InvalidateSelection(range, true);
6303 } else {
6304 InvalidateSelection(SelectionRange(newPos), true);
6305 if (sel.Count() > 1)
6306 Redraw();
6307 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
6308 sel.Clear();
6309 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6310 SetSelection(newPos, newPos);
6313 SelectionPosition anchorCurrent = newPos;
6314 if (shift)
6315 anchorCurrent = sel.IsRectangular() ?
6316 sel.Rectangular().anchor : sel.RangeMain().anchor;
6317 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6318 selectionType = selChar;
6319 originalAnchorPos = sel.MainCaret();
6320 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
6321 SetRectangularRange();
6325 lastClickTime = curTime;
6326 lastClick = pt;
6327 lastXChosen = pt.x + xOffset;
6328 ShowCaretAtCurrentPosition();
6331 bool Editor::PositionIsHotspot(int position) {
6332 return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
6335 bool Editor::PointIsHotspot(Point pt) {
6336 int pos = PositionFromLocation(pt, true);
6337 if (pos == INVALID_POSITION)
6338 return false;
6339 return PositionIsHotspot(pos);
6342 void Editor::SetHotSpotRange(Point *pt) {
6343 if (pt) {
6344 int pos = PositionFromLocation(*pt);
6346 // If we don't limit this to word characters then the
6347 // range can encompass more than the run range and then
6348 // the underline will not be drawn properly.
6349 int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
6350 int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
6352 // Only invalidate the range if the hotspot range has changed...
6353 if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
6354 if (hsStart != -1) {
6355 InvalidateRange(hsStart, hsEnd);
6357 hsStart = hsStart_;
6358 hsEnd = hsEnd_;
6359 InvalidateRange(hsStart, hsEnd);
6361 } else {
6362 if (hsStart != -1) {
6363 int hsStart_ = hsStart;
6364 int hsEnd_ = hsEnd;
6365 hsStart = -1;
6366 hsEnd = -1;
6367 InvalidateRange(hsStart_, hsEnd_);
6368 } else {
6369 hsStart = -1;
6370 hsEnd = -1;
6375 void Editor::GetHotSpotRange(int &hsStart_, int &hsEnd_) {
6376 hsStart_ = hsStart;
6377 hsEnd_ = hsEnd;
6380 void Editor::ButtonMove(Point pt) {
6381 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
6382 DwellEnd(true);
6385 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
6386 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6387 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
6389 if (inDragDrop == ddInitial) {
6390 if (DragThreshold(ptMouseLast, pt)) {
6391 SetMouseCapture(false);
6392 SetDragPosition(movePos);
6393 CopySelectionRange(&drag);
6394 StartDrag();
6396 return;
6399 ptMouseLast = pt;
6400 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
6401 if (HaveMouseCapture()) {
6403 // Slow down autoscrolling/selection
6404 autoScrollTimer.ticksToWait -= timer.tickSize;
6405 if (autoScrollTimer.ticksToWait > 0)
6406 return;
6407 autoScrollTimer.ticksToWait = autoScrollDelay;
6409 // Adjust selection
6410 if (posDrag.IsValid()) {
6411 SetDragPosition(movePos);
6412 } else {
6413 if (selectionType == selChar) {
6414 if (sel.IsRectangular()) {
6415 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
6416 SetSelection(movePos, sel.RangeMain().anchor);
6417 } else if (sel.Count() > 1) {
6418 SelectionRange range(movePos, sel.RangeMain().anchor);
6419 sel.TentativeSelection(range);
6420 InvalidateSelection(range, true);
6421 } else {
6422 SetSelection(movePos, sel.RangeMain().anchor);
6424 } else if (selectionType == selWord) {
6425 // Continue selecting by word
6426 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
6427 // No need to do anything. Previously this case was lumped
6428 // in with "Moved forward", but that can be harmful in this
6429 // case: a handler for the NotifyDoubleClick re-adjusts
6430 // the selection for a fancier definition of "word" (for
6431 // example, in Perl it is useful to include the leading
6432 // '$', '%' or '@' on variables for word selection). In this
6433 // the ButtonMove() called via Tick() for auto-scrolling
6434 // could result in the fancier word selection adjustment
6435 // being unmade.
6436 } else {
6437 wordSelectInitialCaretPos = -1;
6438 WordSelection(movePos.Position());
6440 } else {
6441 // Continue selecting by line
6442 LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
6446 // Autoscroll
6447 PRectangle rcClient = GetClientRectangle();
6448 int lineMove = DisplayFromPosition(movePos.Position());
6449 if (pt.y > rcClient.bottom) {
6450 ScrollTo(lineMove - LinesOnScreen() + 1);
6451 Redraw();
6452 } else if (pt.y < rcClient.top) {
6453 ScrollTo(lineMove);
6454 Redraw();
6456 EnsureCaretVisible(false, false, true);
6458 if (hsStart != -1 && !PositionIsHotspot(movePos.Position()))
6459 SetHotSpotRange(NULL);
6461 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,false) != hotSpotClickPos) {
6462 if (inDragDrop == ddNone) {
6463 DisplayCursor(Window::cursorText);
6465 hotSpotClickPos = INVALID_POSITION;
6468 } else {
6469 if (vs.fixedColumnWidth > 0) { // There is a margin
6470 if (PointInSelMargin(pt)) {
6471 DisplayCursor(GetMarginCursor(pt));
6472 SetHotSpotRange(NULL);
6473 return; // No need to test for selection
6476 // Display regular (drag) cursor over selection
6477 if (PointInSelection(pt) && !SelectionEmpty()) {
6478 DisplayCursor(Window::cursorArrow);
6479 } else if (PointIsHotspot(pt)) {
6480 DisplayCursor(Window::cursorHand);
6481 SetHotSpotRange(&pt);
6482 } else {
6483 DisplayCursor(Window::cursorText);
6484 SetHotSpotRange(NULL);
6489 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
6490 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
6491 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
6492 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6493 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6494 if (inDragDrop == ddInitial) {
6495 inDragDrop = ddNone;
6496 SetEmptySelection(newPos);
6497 selectionType = selChar;
6498 originalAnchorPos = sel.MainCaret();
6500 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
6501 hotSpotClickPos = INVALID_POSITION;
6502 NotifyHotSpotReleaseClick(newPos.Position(), false, ctrl, false);
6504 if (HaveMouseCapture()) {
6505 if (PointInSelMargin(pt)) {
6506 DisplayCursor(GetMarginCursor(pt));
6507 } else {
6508 DisplayCursor(Window::cursorText);
6509 SetHotSpotRange(NULL);
6511 ptMouseLast = pt;
6512 SetMouseCapture(false);
6513 NotifyIndicatorClick(false, newPos.Position(), false, false, false);
6514 if (inDragDrop == ddDragging) {
6515 SelectionPosition selStart = SelectionStart();
6516 SelectionPosition selEnd = SelectionEnd();
6517 if (selStart < selEnd) {
6518 if (drag.len) {
6519 if (ctrl) {
6520 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6521 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6523 } else if (newPos < selStart) {
6524 pdoc->DeleteChars(selStart.Position(), drag.len);
6525 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6526 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6528 } else if (newPos > selEnd) {
6529 pdoc->DeleteChars(selStart.Position(), drag.len);
6530 newPos.Add(-drag.len);
6531 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6532 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6534 } else {
6535 SetEmptySelection(newPos.Position());
6537 drag.Free();
6539 selectionType = selChar;
6541 } else {
6542 if (selectionType == selChar) {
6543 if (sel.Count() > 1) {
6544 sel.RangeMain() =
6545 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
6546 InvalidateSelection(sel.RangeMain(), true);
6547 } else {
6548 SetSelection(newPos, sel.RangeMain().anchor);
6551 sel.CommitTentative();
6553 SetRectangularRange();
6554 lastClickTime = curTime;
6555 lastClick = pt;
6556 lastXChosen = pt.x + xOffset;
6557 if (sel.selType == Selection::selStream) {
6558 SetLastXChosen();
6560 inDragDrop = ddNone;
6561 EnsureCaretVisible(false);
6565 // Called frequently to perform background UI including
6566 // caret blinking and automatic scrolling.
6567 void Editor::Tick() {
6568 if (HaveMouseCapture()) {
6569 // Auto scroll
6570 ButtonMove(ptMouseLast);
6572 if (caret.period > 0) {
6573 timer.ticksToWait -= timer.tickSize;
6574 if (timer.ticksToWait <= 0) {
6575 caret.on = !caret.on;
6576 timer.ticksToWait = caret.period;
6577 if (caret.active) {
6578 InvalidateCaret();
6582 if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) {
6583 scrollWidth = lineWidthMaxSeen;
6584 SetScrollBars();
6586 if ((dwellDelay < SC_TIME_FOREVER) &&
6587 (ticksToDwell > 0) &&
6588 (!HaveMouseCapture()) &&
6589 (ptMouseLast.y >= 0)) {
6590 ticksToDwell -= timer.tickSize;
6591 if (ticksToDwell <= 0) {
6592 dwelling = true;
6593 NotifyDwelling(ptMouseLast, dwelling);
6598 bool Editor::Idle() {
6600 bool idleDone;
6602 bool wrappingDone = wrapState == eWrapNone;
6604 if (!wrappingDone) {
6605 // Wrap lines during idle.
6606 WrapLines(false, -1);
6607 // No more wrapping
6608 if (wrapStart == wrapEnd)
6609 wrappingDone = true;
6612 // Add more idle things to do here, but make sure idleDone is
6613 // set correctly before the function returns. returning
6614 // false will stop calling this idle funtion until SetIdle() is
6615 // called again.
6617 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
6619 return !idleDone;
6622 void Editor::SetFocusState(bool focusState) {
6623 hasFocus = focusState;
6624 NotifyFocus(hasFocus);
6625 if (hasFocus) {
6626 ShowCaretAtCurrentPosition();
6627 } else {
6628 CancelModes();
6629 DropCaret();
6633 int Editor::PositionAfterArea(PRectangle rcArea) {
6634 // The start of the document line after the display line after the area
6635 // This often means that the line after a modification is restyled which helps
6636 // detect multiline comment additions and heals single line comments
6637 int lineAfter = topLine + (rcArea.bottom - 1) / vs.lineHeight + 1;
6638 if (lineAfter < cs.LinesDisplayed())
6639 return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
6640 else
6641 return pdoc->Length();
6644 // Style to a position within the view. If this causes a change at end of last line then
6645 // affects later lines so style all the viewed text.
6646 void Editor::StyleToPositionInView(Position pos) {
6647 int endWindow = PositionAfterArea(GetClientRectangle());
6648 if (pos > endWindow)
6649 pos = endWindow;
6650 int styleAtEnd = pdoc->StyleAt(pos-1);
6651 pdoc->EnsureStyledTo(pos);
6652 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleAt(pos-1))) {
6653 // Style at end of line changed so is multi-line change like starting a comment
6654 // so require rest of window to be styled.
6655 pdoc->EnsureStyledTo(endWindow);
6659 void Editor::IdleStyling() {
6660 // Style the line after the modification as this allows modifications that change just the
6661 // line of the modification to heal instead of propagating to the rest of the window.
6662 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(styleNeeded.upTo) + 2));
6664 if (needUpdateUI) {
6665 NotifyUpdateUI();
6666 needUpdateUI = 0;
6668 styleNeeded.Reset();
6671 void Editor::QueueStyling(int upTo) {
6672 styleNeeded.NeedUpTo(upTo);
6675 bool Editor::PaintContains(PRectangle rc) {
6676 if (rc.Empty()) {
6677 return true;
6678 } else {
6679 return rcPaint.Contains(rc);
6683 bool Editor::PaintContainsMargin() {
6684 PRectangle rcSelMargin = GetClientRectangle();
6685 rcSelMargin.right = vs.fixedColumnWidth;
6686 return PaintContains(rcSelMargin);
6689 void Editor::CheckForChangeOutsidePaint(Range r) {
6690 if (paintState == painting && !paintingAllText) {
6691 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
6692 if (!r.Valid())
6693 return;
6695 PRectangle rcRange = RectangleFromRange(r.start, r.end);
6696 PRectangle rcText = GetTextRectangle();
6697 if (rcRange.top < rcText.top) {
6698 rcRange.top = rcText.top;
6700 if (rcRange.bottom > rcText.bottom) {
6701 rcRange.bottom = rcText.bottom;
6704 if (!PaintContains(rcRange)) {
6705 AbandonPaint();
6710 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
6711 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
6712 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
6713 CheckForChangeOutsidePaint(Range(braces[0]));
6714 CheckForChangeOutsidePaint(Range(pos0));
6715 braces[0] = pos0;
6717 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
6718 CheckForChangeOutsidePaint(Range(braces[1]));
6719 CheckForChangeOutsidePaint(Range(pos1));
6720 braces[1] = pos1;
6722 bracesMatchStyle = matchStyle;
6723 if (paintState == notPainting) {
6724 Redraw();
6729 void Editor::SetAnnotationHeights(int start, int end) {
6730 if (vs.annotationVisible) {
6731 bool changedHeight = false;
6732 for (int line=start; line<end && line<pdoc->LinesTotal(); line++) {
6733 int linesWrapped = 1;
6734 if (wrapState != eWrapNone) {
6735 AutoSurface surface(this);
6736 AutoLineLayout ll(llc, RetrieveLineLayout(line));
6737 if (surface && ll) {
6738 LayoutLine(line, surface, vs, ll, wrapWidth);
6739 linesWrapped = ll->lines;
6742 if (cs.SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
6743 changedHeight = true;
6745 if (changedHeight) {
6746 Redraw();
6751 void Editor::SetDocPointer(Document *document) {
6752 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
6753 pdoc->RemoveWatcher(this, 0);
6754 pdoc->Release();
6755 if (document == NULL) {
6756 pdoc = new Document();
6757 } else {
6758 pdoc = document;
6760 pdoc->AddRef();
6762 // Ensure all positions within document
6763 sel.Clear();
6764 targetStart = 0;
6765 targetEnd = 0;
6767 braces[0] = invalidPosition;
6768 braces[1] = invalidPosition;
6770 // Reset the contraction state to fully shown.
6771 cs.Clear();
6772 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6773 SetAnnotationHeights(0, pdoc->LinesTotal());
6774 llc.Deallocate();
6775 NeedWrapping();
6777 pdoc->AddWatcher(this, 0);
6778 SetScrollBars();
6779 Redraw();
6782 void Editor::SetAnnotationVisible(int visible) {
6783 if (vs.annotationVisible != visible) {
6784 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
6785 vs.annotationVisible = visible;
6786 if (changedFromOrToHidden) {
6787 int dir = vs.annotationVisible ? 1 : -1;
6788 for (int line=0; line<pdoc->LinesTotal(); line++) {
6789 int annotationLines = pdoc->AnnotationLines(line);
6790 if (annotationLines > 0) {
6791 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
6795 Redraw();
6800 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
6802 void Editor::Expand(int &line, bool doExpand) {
6803 int lineMaxSubord = pdoc->GetLastChild(line);
6804 line++;
6805 while (line <= lineMaxSubord) {
6806 if (doExpand)
6807 cs.SetVisible(line, line, true);
6808 int level = pdoc->GetLevel(line);
6809 if (level & SC_FOLDLEVELHEADERFLAG) {
6810 if (doExpand && cs.GetExpanded(line)) {
6811 Expand(line, true);
6812 } else {
6813 Expand(line, false);
6815 } else {
6816 line++;
6821 void Editor::ToggleContraction(int line) {
6822 if (line >= 0) {
6823 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
6824 line = pdoc->GetFoldParent(line);
6825 if (line < 0)
6826 return;
6829 if (cs.GetExpanded(line)) {
6830 int lineMaxSubord = pdoc->GetLastChild(line);
6831 if (lineMaxSubord > line) {
6832 cs.SetExpanded(line, 0);
6833 cs.SetVisible(line + 1, lineMaxSubord, false);
6835 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
6836 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
6837 // This does not re-expand the fold
6838 EnsureCaretVisible();
6841 SetScrollBars();
6842 Redraw();
6845 } else {
6846 if (!(cs.GetVisible(line))) {
6847 EnsureLineVisible(line, false);
6848 GoToLine(line);
6850 cs.SetExpanded(line, 1);
6851 Expand(line, true);
6852 SetScrollBars();
6853 Redraw();
6858 int Editor::ContractedFoldNext(int lineStart) {
6859 for (int line = lineStart; line<pdoc->LinesTotal();) {
6860 if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
6861 return line;
6862 line = cs.ContractedNext(line+1);
6863 if (line < 0)
6864 return -1;
6867 return -1;
6871 * Recurse up from this line to find any folds that prevent this line from being visible
6872 * and unfold them all.
6874 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
6876 // In case in need of wrapping to ensure DisplayFromDoc works.
6877 if (lineDoc >= wrapStart)
6878 WrapLines(true, -1);
6880 if (!cs.GetVisible(lineDoc)) {
6881 int lookLine = lineDoc;
6882 int lookLineLevel = pdoc->GetLevel(lookLine);
6883 while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
6884 lookLineLevel = pdoc->GetLevel(--lookLine);
6886 int lineParent = pdoc->GetFoldParent(lookLine);
6887 if (lineParent >= 0) {
6888 if (lineDoc != lineParent)
6889 EnsureLineVisible(lineParent, enforcePolicy);
6890 if (!cs.GetExpanded(lineParent)) {
6891 cs.SetExpanded(lineParent, 1);
6892 Expand(lineParent, true);
6895 SetScrollBars();
6896 Redraw();
6898 if (enforcePolicy) {
6899 int lineDisplay = cs.DisplayFromDoc(lineDoc);
6900 if (visiblePolicy & VISIBLE_SLOP) {
6901 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
6902 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
6903 SetVerticalScrollPos();
6904 Redraw();
6905 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
6906 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
6907 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
6908 SetVerticalScrollPos();
6909 Redraw();
6911 } else {
6912 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
6913 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
6914 SetVerticalScrollPos();
6915 Redraw();
6921 int Editor::GetTag(char *tagValue, int tagNumber) {
6922 char name[3] = "\\?";
6923 const char *text = 0;
6924 int length = 0;
6925 if ((tagNumber >= 1) && (tagNumber <= 9)) {
6926 name[1] = static_cast<char>(tagNumber + '0');
6927 length = 2;
6928 text = pdoc->SubstituteByPosition(name, &length);
6930 if (tagValue) {
6931 if (text)
6932 memcpy(tagValue, text, length + 1);
6933 else
6934 *tagValue = '\0';
6936 return length;
6939 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
6940 UndoGroup ug(pdoc);
6941 if (length == -1)
6942 length = istrlen(text);
6943 if (replacePatterns) {
6944 text = pdoc->SubstituteByPosition(text, &length);
6945 if (!text) {
6946 return 0;
6949 if (targetStart != targetEnd)
6950 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
6951 targetEnd = targetStart;
6952 pdoc->InsertString(targetStart, text, length);
6953 targetEnd = targetStart + length;
6954 return length;
6957 bool Editor::IsUnicodeMode() const {
6958 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
6961 int Editor::CodePage() const {
6962 if (pdoc)
6963 return pdoc->dbcsCodePage;
6964 else
6965 return 0;
6968 int Editor::WrapCount(int line) {
6969 AutoSurface surface(this);
6970 AutoLineLayout ll(llc, RetrieveLineLayout(line));
6972 if (surface && ll) {
6973 LayoutLine(line, surface, vs, ll, wrapWidth);
6974 return ll->lines;
6975 } else {
6976 return 1;
6980 void Editor::AddStyledText(char *buffer, int appendLength) {
6981 // The buffer consists of alternating character bytes and style bytes
6982 int textLength = appendLength / 2;
6983 char *text = new char[textLength];
6984 int i;
6985 for (i = 0; i < textLength; i++) {
6986 text[i] = buffer[i*2];
6988 pdoc->InsertString(CurrentPosition(), text, textLength);
6989 for (i = 0; i < textLength; i++) {
6990 text[i] = buffer[i*2+1];
6992 pdoc->StartStyling(CurrentPosition(), static_cast<char>(0xff));
6993 pdoc->SetStyles(textLength, text);
6994 delete []text;
6995 SetEmptySelection(sel.MainCaret() + textLength);
6998 static bool ValidMargin(unsigned long wParam) {
6999 return wParam < ViewStyle::margins;
7002 static char *CharPtrFromSPtr(sptr_t lParam) {
7003 return reinterpret_cast<char *>(lParam);
7006 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7007 vs.EnsureStyle(wParam);
7008 switch (iMessage) {
7009 case SCI_STYLESETFORE:
7010 vs.styles[wParam].fore = ColourDesired(lParam);
7011 break;
7012 case SCI_STYLESETBACK:
7013 vs.styles[wParam].back = ColourDesired(lParam);
7014 break;
7015 case SCI_STYLESETBOLD:
7016 vs.styles[wParam].weight = lParam != 0 ? SC_WEIGHT_BOLD : SC_WEIGHT_NORMAL;
7017 break;
7018 case SCI_STYLESETWEIGHT:
7019 vs.styles[wParam].weight = lParam;
7020 break;
7021 case SCI_STYLESETITALIC:
7022 vs.styles[wParam].italic = lParam != 0;
7023 break;
7024 case SCI_STYLESETEOLFILLED:
7025 vs.styles[wParam].eolFilled = lParam != 0;
7026 break;
7027 case SCI_STYLESETSIZE:
7028 vs.styles[wParam].size = lParam * SC_FONT_SIZE_MULTIPLIER;
7029 break;
7030 case SCI_STYLESETSIZEFRACTIONAL:
7031 vs.styles[wParam].size = lParam;
7032 break;
7033 case SCI_STYLESETFONT:
7034 if (lParam != 0) {
7035 vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam));
7037 break;
7038 case SCI_STYLESETUNDERLINE:
7039 vs.styles[wParam].underline = lParam != 0;
7040 break;
7041 case SCI_STYLESETCASE:
7042 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
7043 break;
7044 case SCI_STYLESETCHARACTERSET:
7045 vs.styles[wParam].characterSet = lParam;
7046 break;
7047 case SCI_STYLESETVISIBLE:
7048 vs.styles[wParam].visible = lParam != 0;
7049 break;
7050 case SCI_STYLESETCHANGEABLE:
7051 vs.styles[wParam].changeable = lParam != 0;
7052 break;
7053 case SCI_STYLESETHOTSPOT:
7054 vs.styles[wParam].hotspot = lParam != 0;
7055 break;
7057 InvalidateStyleRedraw();
7060 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7061 vs.EnsureStyle(wParam);
7062 switch (iMessage) {
7063 case SCI_STYLEGETFORE:
7064 return vs.styles[wParam].fore.AsLong();
7065 case SCI_STYLEGETBACK:
7066 return vs.styles[wParam].back.AsLong();
7067 case SCI_STYLEGETBOLD:
7068 return vs.styles[wParam].weight > SC_WEIGHT_NORMAL;
7069 case SCI_STYLEGETWEIGHT:
7070 return vs.styles[wParam].weight;
7071 case SCI_STYLEGETITALIC:
7072 return vs.styles[wParam].italic ? 1 : 0;
7073 case SCI_STYLEGETEOLFILLED:
7074 return vs.styles[wParam].eolFilled ? 1 : 0;
7075 case SCI_STYLEGETSIZE:
7076 return vs.styles[wParam].size / SC_FONT_SIZE_MULTIPLIER;
7077 case SCI_STYLEGETSIZEFRACTIONAL:
7078 return vs.styles[wParam].size;
7079 case SCI_STYLEGETFONT:
7080 if (!vs.styles[wParam].fontName)
7081 return 0;
7082 if (lParam != 0)
7083 strcpy(CharPtrFromSPtr(lParam), vs.styles[wParam].fontName);
7084 return strlen(vs.styles[wParam].fontName);
7085 case SCI_STYLEGETUNDERLINE:
7086 return vs.styles[wParam].underline ? 1 : 0;
7087 case SCI_STYLEGETCASE:
7088 return static_cast<int>(vs.styles[wParam].caseForce);
7089 case SCI_STYLEGETCHARACTERSET:
7090 return vs.styles[wParam].characterSet;
7091 case SCI_STYLEGETVISIBLE:
7092 return vs.styles[wParam].visible ? 1 : 0;
7093 case SCI_STYLEGETCHANGEABLE:
7094 return vs.styles[wParam].changeable ? 1 : 0;
7095 case SCI_STYLEGETHOTSPOT:
7096 return vs.styles[wParam].hotspot ? 1 : 0;
7098 return 0;
7101 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
7102 const size_t n = strlen(val);
7103 if (lParam != 0) {
7104 char *ptr = reinterpret_cast<char *>(lParam);
7105 strcpy(ptr, val);
7107 return n; // Not including NUL
7110 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7111 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
7113 // Optional macro recording hook
7114 if (recordingMacro)
7115 NotifyMacroRecord(iMessage, wParam, lParam);
7117 switch (iMessage) {
7119 case SCI_GETTEXT: {
7120 if (lParam == 0)
7121 return pdoc->Length() + 1;
7122 if (wParam == 0)
7123 return 0;
7124 char *ptr = CharPtrFromSPtr(lParam);
7125 unsigned int iChar = 0;
7126 for (; iChar < wParam - 1; iChar++)
7127 ptr[iChar] = pdoc->CharAt(iChar);
7128 ptr[iChar] = '\0';
7129 return iChar;
7132 case SCI_SETTEXT: {
7133 if (lParam == 0)
7134 return 0;
7135 UndoGroup ug(pdoc);
7136 pdoc->DeleteChars(0, pdoc->Length());
7137 SetEmptySelection(0);
7138 pdoc->InsertCString(0, CharPtrFromSPtr(lParam));
7139 return 1;
7142 case SCI_GETTEXTLENGTH:
7143 return pdoc->Length();
7145 case SCI_CUT:
7146 Cut();
7147 SetLastXChosen();
7148 break;
7150 case SCI_COPY:
7151 Copy();
7152 break;
7154 case SCI_COPYALLOWLINE:
7155 CopyAllowLine();
7156 break;
7158 case SCI_VERTICALCENTRECARET:
7159 VerticalCentreCaret();
7160 break;
7162 case SCI_MOVESELECTEDLINESUP:
7163 MoveSelectedLinesUp();
7164 break;
7166 case SCI_MOVESELECTEDLINESDOWN:
7167 MoveSelectedLinesDown();
7168 break;
7170 case SCI_COPYRANGE:
7171 CopyRangeToClipboard(wParam, lParam);
7172 break;
7174 case SCI_COPYTEXT:
7175 CopyText(wParam, CharPtrFromSPtr(lParam));
7176 break;
7178 case SCI_PASTE:
7179 Paste();
7180 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
7181 SetLastXChosen();
7183 EnsureCaretVisible();
7184 break;
7186 case SCI_CLEAR:
7187 Clear();
7188 SetLastXChosen();
7189 EnsureCaretVisible();
7190 break;
7192 case SCI_UNDO:
7193 Undo();
7194 SetLastXChosen();
7195 break;
7197 case SCI_CANUNDO:
7198 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
7200 case SCI_EMPTYUNDOBUFFER:
7201 pdoc->DeleteUndoHistory();
7202 return 0;
7204 case SCI_GETFIRSTVISIBLELINE:
7205 return topLine;
7207 case SCI_SETFIRSTVISIBLELINE:
7208 ScrollTo(wParam);
7209 break;
7211 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
7212 int lineStart = pdoc->LineStart(wParam);
7213 int lineEnd = pdoc->LineStart(wParam + 1);
7214 if (lParam == 0) {
7215 return lineEnd - lineStart;
7217 char *ptr = CharPtrFromSPtr(lParam);
7218 int iPlace = 0;
7219 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
7220 ptr[iPlace++] = pdoc->CharAt(iChar);
7222 return iPlace;
7225 case SCI_GETLINECOUNT:
7226 if (pdoc->LinesTotal() == 0)
7227 return 1;
7228 else
7229 return pdoc->LinesTotal();
7231 case SCI_GETMODIFY:
7232 return !pdoc->IsSavePoint();
7234 case SCI_SETSEL: {
7235 int nStart = static_cast<int>(wParam);
7236 int nEnd = static_cast<int>(lParam);
7237 if (nEnd < 0)
7238 nEnd = pdoc->Length();
7239 if (nStart < 0)
7240 nStart = nEnd; // Remove selection
7241 InvalidateSelection(SelectionRange(nStart, nEnd));
7242 sel.Clear();
7243 sel.selType = Selection::selStream;
7244 SetSelection(nEnd, nStart);
7245 EnsureCaretVisible();
7247 break;
7249 case SCI_GETSELTEXT: {
7250 SelectionText selectedText;
7251 CopySelectionRange(&selectedText);
7252 if (lParam == 0) {
7253 return selectedText.len ? selectedText.len : 1;
7254 } else {
7255 char *ptr = CharPtrFromSPtr(lParam);
7256 int iChar = 0;
7257 if (selectedText.len) {
7258 for (; iChar < selectedText.len; iChar++)
7259 ptr[iChar] = selectedText.s[iChar];
7260 } else {
7261 ptr[0] = '\0';
7263 return iChar;
7267 case SCI_LINEFROMPOSITION:
7268 if (static_cast<int>(wParam) < 0)
7269 return 0;
7270 return pdoc->LineFromPosition(wParam);
7272 case SCI_POSITIONFROMLINE:
7273 if (static_cast<int>(wParam) < 0)
7274 wParam = pdoc->LineFromPosition(SelectionStart().Position());
7275 if (wParam == 0)
7276 return 0; // Even if there is no text, there is a first line that starts at 0
7277 if (static_cast<int>(wParam) > pdoc->LinesTotal())
7278 return -1;
7279 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
7280 // return -1;
7281 return pdoc->LineStart(wParam);
7283 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
7284 case SCI_LINELENGTH:
7285 if ((static_cast<int>(wParam) < 0) ||
7286 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
7287 return 0;
7288 return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
7290 case SCI_REPLACESEL: {
7291 if (lParam == 0)
7292 return 0;
7293 UndoGroup ug(pdoc);
7294 ClearSelection();
7295 char *replacement = CharPtrFromSPtr(lParam);
7296 pdoc->InsertCString(sel.MainCaret(), replacement);
7297 SetEmptySelection(sel.MainCaret() + istrlen(replacement));
7298 EnsureCaretVisible();
7300 break;
7302 case SCI_SETTARGETSTART:
7303 targetStart = wParam;
7304 break;
7306 case SCI_GETTARGETSTART:
7307 return targetStart;
7309 case SCI_SETTARGETEND:
7310 targetEnd = wParam;
7311 break;
7313 case SCI_GETTARGETEND:
7314 return targetEnd;
7316 case SCI_TARGETFROMSELECTION:
7317 if (sel.MainCaret() < sel.MainAnchor()) {
7318 targetStart = sel.MainCaret();
7319 targetEnd = sel.MainAnchor();
7320 } else {
7321 targetStart = sel.MainAnchor();
7322 targetEnd = sel.MainCaret();
7324 break;
7326 case SCI_REPLACETARGET:
7327 PLATFORM_ASSERT(lParam);
7328 return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam);
7330 case SCI_REPLACETARGETRE:
7331 PLATFORM_ASSERT(lParam);
7332 return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam);
7334 case SCI_SEARCHINTARGET:
7335 PLATFORM_ASSERT(lParam);
7336 return SearchInTarget(CharPtrFromSPtr(lParam), wParam);
7338 case SCI_SETSEARCHFLAGS:
7339 searchFlags = wParam;
7340 break;
7342 case SCI_GETSEARCHFLAGS:
7343 return searchFlags;
7345 case SCI_GETTAG:
7346 return GetTag(CharPtrFromSPtr(lParam), wParam);
7348 case SCI_POSITIONBEFORE:
7349 return pdoc->MovePositionOutsideChar(wParam - 1, -1, true);
7351 case SCI_POSITIONAFTER:
7352 return pdoc->MovePositionOutsideChar(wParam + 1, 1, true);
7354 case SCI_LINESCROLL:
7355 ScrollTo(topLine + lParam);
7356 HorizontalScrollTo(xOffset + wParam * vs.spaceWidth);
7357 return 1;
7359 case SCI_SETXOFFSET:
7360 xOffset = wParam;
7361 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7362 SetHorizontalScrollPos();
7363 Redraw();
7364 break;
7366 case SCI_GETXOFFSET:
7367 return xOffset;
7369 case SCI_CHOOSECARETX:
7370 SetLastXChosen();
7371 break;
7373 case SCI_SCROLLCARET:
7374 EnsureCaretVisible();
7375 break;
7377 case SCI_SETREADONLY:
7378 pdoc->SetReadOnly(wParam != 0);
7379 return 1;
7381 case SCI_GETREADONLY:
7382 return pdoc->IsReadOnly();
7384 case SCI_CANPASTE:
7385 return CanPaste();
7387 case SCI_POINTXFROMPOSITION:
7388 if (lParam < 0) {
7389 return 0;
7390 } else {
7391 Point pt = LocationFromPosition(lParam);
7392 return pt.x;
7395 case SCI_POINTYFROMPOSITION:
7396 if (lParam < 0) {
7397 return 0;
7398 } else {
7399 Point pt = LocationFromPosition(lParam);
7400 return pt.y;
7403 case SCI_FINDTEXT:
7404 return FindText(wParam, lParam);
7406 case SCI_GETTEXTRANGE: {
7407 if (lParam == 0)
7408 return 0;
7409 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7410 int cpMax = tr->chrg.cpMax;
7411 if (cpMax == -1)
7412 cpMax = pdoc->Length();
7413 PLATFORM_ASSERT(cpMax <= pdoc->Length());
7414 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
7415 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
7416 // Spec says copied text is terminated with a NUL
7417 tr->lpstrText[len] = '\0';
7418 return len; // Not including NUL
7421 case SCI_HIDESELECTION:
7422 hideSelection = wParam != 0;
7423 Redraw();
7424 break;
7426 case SCI_FORMATRANGE:
7427 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
7429 case SCI_GETMARGINLEFT:
7430 return vs.leftMarginWidth;
7432 case SCI_GETMARGINRIGHT:
7433 return vs.rightMarginWidth;
7435 case SCI_SETMARGINLEFT:
7436 vs.leftMarginWidth = lParam;
7437 InvalidateStyleRedraw();
7438 break;
7440 case SCI_SETMARGINRIGHT:
7441 vs.rightMarginWidth = lParam;
7442 InvalidateStyleRedraw();
7443 break;
7445 // Control specific mesages
7447 case SCI_ADDTEXT: {
7448 if (lParam == 0)
7449 return 0;
7450 pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam);
7451 SetEmptySelection(sel.MainCaret() + wParam);
7452 return 0;
7455 case SCI_ADDSTYLEDTEXT:
7456 if (lParam)
7457 AddStyledText(CharPtrFromSPtr(lParam), wParam);
7458 return 0;
7460 case SCI_INSERTTEXT: {
7461 if (lParam == 0)
7462 return 0;
7463 int insertPos = wParam;
7464 if (static_cast<int>(wParam) == -1)
7465 insertPos = CurrentPosition();
7466 int newCurrent = CurrentPosition();
7467 char *sz = CharPtrFromSPtr(lParam);
7468 pdoc->InsertCString(insertPos, sz);
7469 if (newCurrent > insertPos)
7470 newCurrent += istrlen(sz);
7471 SetEmptySelection(newCurrent);
7472 return 0;
7475 case SCI_APPENDTEXT:
7476 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam);
7477 return 0;
7479 case SCI_CLEARALL:
7480 ClearAll();
7481 return 0;
7483 case SCI_DELETERANGE:
7484 pdoc->DeleteChars(wParam, lParam);
7485 return 0;
7487 case SCI_CLEARDOCUMENTSTYLE:
7488 ClearDocumentStyle();
7489 return 0;
7491 case SCI_SETUNDOCOLLECTION:
7492 pdoc->SetUndoCollection(wParam != 0);
7493 return 0;
7495 case SCI_GETUNDOCOLLECTION:
7496 return pdoc->IsCollectingUndo();
7498 case SCI_BEGINUNDOACTION:
7499 pdoc->BeginUndoAction();
7500 return 0;
7502 case SCI_ENDUNDOACTION:
7503 pdoc->EndUndoAction();
7504 return 0;
7506 case SCI_GETCARETPERIOD:
7507 return caret.period;
7509 case SCI_SETCARETPERIOD:
7510 caret.period = wParam;
7511 break;
7513 case SCI_SETWORDCHARS: {
7514 pdoc->SetDefaultCharClasses(false);
7515 if (lParam == 0)
7516 return 0;
7517 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
7519 break;
7521 case SCI_SETWHITESPACECHARS: {
7522 if (lParam == 0)
7523 return 0;
7524 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
7526 break;
7528 case SCI_SETCHARSDEFAULT:
7529 pdoc->SetDefaultCharClasses(true);
7530 break;
7532 case SCI_GETLENGTH:
7533 return pdoc->Length();
7535 case SCI_ALLOCATE:
7536 pdoc->Allocate(wParam);
7537 break;
7539 case SCI_GETCHARAT:
7540 return pdoc->CharAt(wParam);
7542 case SCI_SETCURRENTPOS:
7543 if (sel.IsRectangular()) {
7544 sel.Rectangular().caret.SetPosition(wParam);
7545 SetRectangularRange();
7546 Redraw();
7547 } else {
7548 SetSelection(wParam, sel.MainAnchor());
7550 break;
7552 case SCI_GETCURRENTPOS:
7553 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
7555 case SCI_SETANCHOR:
7556 if (sel.IsRectangular()) {
7557 sel.Rectangular().anchor.SetPosition(wParam);
7558 SetRectangularRange();
7559 Redraw();
7560 } else {
7561 SetSelection(sel.MainCaret(), wParam);
7563 break;
7565 case SCI_GETANCHOR:
7566 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
7568 case SCI_SETSELECTIONSTART:
7569 SetSelection(Platform::Maximum(sel.MainCaret(), wParam), wParam);
7570 break;
7572 case SCI_GETSELECTIONSTART:
7573 return sel.LimitsForRectangularElseMain().start.Position();
7575 case SCI_SETSELECTIONEND:
7576 SetSelection(wParam, Platform::Minimum(sel.MainAnchor(), wParam));
7577 break;
7579 case SCI_GETSELECTIONEND:
7580 return sel.LimitsForRectangularElseMain().end.Position();
7582 case SCI_SETEMPTYSELECTION:
7583 SetEmptySelection(wParam);
7584 break;
7586 case SCI_SETPRINTMAGNIFICATION:
7587 printMagnification = wParam;
7588 break;
7590 case SCI_GETPRINTMAGNIFICATION:
7591 return printMagnification;
7593 case SCI_SETPRINTCOLOURMODE:
7594 printColourMode = wParam;
7595 break;
7597 case SCI_GETPRINTCOLOURMODE:
7598 return printColourMode;
7600 case SCI_SETPRINTWRAPMODE:
7601 printWrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
7602 break;
7604 case SCI_GETPRINTWRAPMODE:
7605 return printWrapState;
7607 case SCI_GETSTYLEAT:
7608 if (static_cast<int>(wParam) >= pdoc->Length())
7609 return 0;
7610 else
7611 return pdoc->StyleAt(wParam);
7613 case SCI_REDO:
7614 Redo();
7615 break;
7617 case SCI_SELECTALL:
7618 SelectAll();
7619 break;
7621 case SCI_SETSAVEPOINT:
7622 pdoc->SetSavePoint();
7623 break;
7625 case SCI_GETSTYLEDTEXT: {
7626 if (lParam == 0)
7627 return 0;
7628 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7629 int iPlace = 0;
7630 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
7631 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
7632 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
7634 tr->lpstrText[iPlace] = '\0';
7635 tr->lpstrText[iPlace + 1] = '\0';
7636 return iPlace;
7639 case SCI_CANREDO:
7640 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
7642 case SCI_MARKERLINEFROMHANDLE:
7643 return pdoc->LineFromHandle(wParam);
7645 case SCI_MARKERDELETEHANDLE:
7646 pdoc->DeleteMarkFromHandle(wParam);
7647 break;
7649 case SCI_GETVIEWWS:
7650 return vs.viewWhitespace;
7652 case SCI_SETVIEWWS:
7653 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
7654 Redraw();
7655 break;
7657 case SCI_GETWHITESPACESIZE:
7658 return vs.whitespaceSize;
7660 case SCI_SETWHITESPACESIZE:
7661 vs.whitespaceSize = static_cast<int>(wParam);
7662 Redraw();
7663 break;
7665 case SCI_POSITIONFROMPOINT:
7666 return PositionFromLocation(Point(wParam, lParam), false, false);
7668 case SCI_POSITIONFROMPOINTCLOSE:
7669 return PositionFromLocation(Point(wParam, lParam), true, false);
7671 case SCI_CHARPOSITIONFROMPOINT:
7672 return PositionFromLocation(Point(wParam, lParam), false, true);
7674 case SCI_CHARPOSITIONFROMPOINTCLOSE:
7675 return PositionFromLocation(Point(wParam, lParam), true, true);
7677 case SCI_GOTOLINE:
7678 GoToLine(wParam);
7679 break;
7681 case SCI_GOTOPOS:
7682 SetEmptySelection(wParam);
7683 EnsureCaretVisible();
7684 break;
7686 case SCI_GETCURLINE: {
7687 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
7688 int lineStart = pdoc->LineStart(lineCurrentPos);
7689 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
7690 if (lParam == 0) {
7691 return 1 + lineEnd - lineStart;
7693 PLATFORM_ASSERT(wParam > 0);
7694 char *ptr = CharPtrFromSPtr(lParam);
7695 unsigned int iPlace = 0;
7696 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
7697 ptr[iPlace++] = pdoc->CharAt(iChar);
7699 ptr[iPlace] = '\0';
7700 return sel.MainCaret() - lineStart;
7703 case SCI_GETENDSTYLED:
7704 return pdoc->GetEndStyled();
7706 case SCI_GETEOLMODE:
7707 return pdoc->eolMode;
7709 case SCI_SETEOLMODE:
7710 pdoc->eolMode = wParam;
7711 break;
7713 case SCI_STARTSTYLING:
7714 pdoc->StartStyling(wParam, static_cast<char>(lParam));
7715 break;
7717 case SCI_SETSTYLING:
7718 pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
7719 break;
7721 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
7722 if (lParam == 0)
7723 return 0;
7724 pdoc->SetStyles(wParam, CharPtrFromSPtr(lParam));
7725 break;
7727 case SCI_SETBUFFEREDDRAW:
7728 bufferedDraw = wParam != 0;
7729 break;
7731 case SCI_GETBUFFEREDDRAW:
7732 return bufferedDraw;
7734 case SCI_GETTWOPHASEDRAW:
7735 return twoPhaseDraw;
7737 case SCI_SETTWOPHASEDRAW:
7738 twoPhaseDraw = wParam != 0;
7739 InvalidateStyleRedraw();
7740 break;
7742 case SCI_SETFONTQUALITY:
7743 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
7744 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
7745 InvalidateStyleRedraw();
7746 break;
7748 case SCI_GETFONTQUALITY:
7749 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
7751 case SCI_SETTABWIDTH:
7752 if (wParam > 0) {
7753 pdoc->tabInChars = wParam;
7754 if (pdoc->indentInChars == 0)
7755 pdoc->actualIndentInChars = pdoc->tabInChars;
7757 InvalidateStyleRedraw();
7758 break;
7760 case SCI_GETTABWIDTH:
7761 return pdoc->tabInChars;
7763 case SCI_SETINDENT:
7764 pdoc->indentInChars = wParam;
7765 if (pdoc->indentInChars != 0)
7766 pdoc->actualIndentInChars = pdoc->indentInChars;
7767 else
7768 pdoc->actualIndentInChars = pdoc->tabInChars;
7769 InvalidateStyleRedraw();
7770 break;
7772 case SCI_GETINDENT:
7773 return pdoc->indentInChars;
7775 case SCI_SETUSETABS:
7776 pdoc->useTabs = wParam != 0;
7777 InvalidateStyleRedraw();
7778 break;
7780 case SCI_GETUSETABS:
7781 return pdoc->useTabs;
7783 case SCI_SETLINEINDENTATION:
7784 pdoc->SetLineIndentation(wParam, lParam);
7785 break;
7787 case SCI_GETLINEINDENTATION:
7788 return pdoc->GetLineIndentation(wParam);
7790 case SCI_GETLINEINDENTPOSITION:
7791 return pdoc->GetLineIndentPosition(wParam);
7793 case SCI_SETTABINDENTS:
7794 pdoc->tabIndents = wParam != 0;
7795 break;
7797 case SCI_GETTABINDENTS:
7798 return pdoc->tabIndents;
7800 case SCI_SETBACKSPACEUNINDENTS:
7801 pdoc->backspaceUnindents = wParam != 0;
7802 break;
7804 case SCI_GETBACKSPACEUNINDENTS:
7805 return pdoc->backspaceUnindents;
7807 case SCI_SETMOUSEDWELLTIME:
7808 dwellDelay = wParam;
7809 ticksToDwell = dwellDelay;
7810 break;
7812 case SCI_GETMOUSEDWELLTIME:
7813 return dwellDelay;
7815 case SCI_WORDSTARTPOSITION:
7816 return pdoc->ExtendWordSelect(wParam, -1, lParam != 0);
7818 case SCI_WORDENDPOSITION:
7819 return pdoc->ExtendWordSelect(wParam, 1, lParam != 0);
7821 case SCI_SETWRAPMODE:
7822 switch (wParam) {
7823 case SC_WRAP_WORD:
7824 wrapState = eWrapWord;
7825 break;
7826 case SC_WRAP_CHAR:
7827 wrapState = eWrapChar;
7828 break;
7829 default:
7830 wrapState = eWrapNone;
7831 break;
7833 xOffset = 0;
7834 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7835 InvalidateStyleRedraw();
7836 ReconfigureScrollBars();
7837 break;
7839 case SCI_GETWRAPMODE:
7840 return wrapState;
7842 case SCI_SETWRAPVISUALFLAGS:
7843 if (wrapVisualFlags != static_cast<int>(wParam)) {
7844 wrapVisualFlags = wParam;
7845 InvalidateStyleRedraw();
7846 ReconfigureScrollBars();
7848 break;
7850 case SCI_GETWRAPVISUALFLAGS:
7851 return wrapVisualFlags;
7853 case SCI_SETWRAPVISUALFLAGSLOCATION:
7854 wrapVisualFlagsLocation = wParam;
7855 InvalidateStyleRedraw();
7856 break;
7858 case SCI_GETWRAPVISUALFLAGSLOCATION:
7859 return wrapVisualFlagsLocation;
7861 case SCI_SETWRAPSTARTINDENT:
7862 if (wrapVisualStartIndent != static_cast<int>(wParam)) {
7863 wrapVisualStartIndent = wParam;
7864 InvalidateStyleRedraw();
7865 ReconfigureScrollBars();
7867 break;
7869 case SCI_GETWRAPSTARTINDENT:
7870 return wrapVisualStartIndent;
7872 case SCI_SETWRAPINDENTMODE:
7873 if (wrapIndentMode != static_cast<int>(wParam)) {
7874 wrapIndentMode = wParam;
7875 InvalidateStyleRedraw();
7876 ReconfigureScrollBars();
7878 break;
7880 case SCI_GETWRAPINDENTMODE:
7881 return wrapIndentMode;
7883 case SCI_SETLAYOUTCACHE:
7884 llc.SetLevel(wParam);
7885 break;
7887 case SCI_GETLAYOUTCACHE:
7888 return llc.GetLevel();
7890 case SCI_SETPOSITIONCACHE:
7891 posCache.SetSize(wParam);
7892 break;
7894 case SCI_GETPOSITIONCACHE:
7895 return posCache.GetSize();
7897 case SCI_SETSCROLLWIDTH:
7898 PLATFORM_ASSERT(wParam > 0);
7899 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
7900 lineWidthMaxSeen = 0;
7901 scrollWidth = wParam;
7902 SetScrollBars();
7904 break;
7906 case SCI_GETSCROLLWIDTH:
7907 return scrollWidth;
7909 case SCI_SETSCROLLWIDTHTRACKING:
7910 trackLineWidth = wParam != 0;
7911 break;
7913 case SCI_GETSCROLLWIDTHTRACKING:
7914 return trackLineWidth;
7916 case SCI_LINESJOIN:
7917 LinesJoin();
7918 break;
7920 case SCI_LINESSPLIT:
7921 LinesSplit(wParam);
7922 break;
7924 case SCI_TEXTWIDTH:
7925 PLATFORM_ASSERT(wParam < vs.stylesSize);
7926 PLATFORM_ASSERT(lParam);
7927 return TextWidth(wParam, CharPtrFromSPtr(lParam));
7929 case SCI_TEXTHEIGHT:
7930 return vs.lineHeight;
7932 case SCI_SETENDATLASTLINE:
7933 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
7934 if (endAtLastLine != (wParam != 0)) {
7935 endAtLastLine = wParam != 0;
7936 SetScrollBars();
7938 break;
7940 case SCI_GETENDATLASTLINE:
7941 return endAtLastLine;
7943 case SCI_SETCARETSTICKY:
7944 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
7945 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
7946 caretSticky = wParam;
7948 break;
7950 case SCI_GETCARETSTICKY:
7951 return caretSticky;
7953 case SCI_TOGGLECARETSTICKY:
7954 caretSticky = !caretSticky;
7955 break;
7957 case SCI_GETCOLUMN:
7958 return pdoc->GetColumn(wParam);
7960 case SCI_FINDCOLUMN:
7961 return pdoc->FindColumn(wParam, lParam);
7963 case SCI_SETHSCROLLBAR :
7964 if (horizontalScrollBarVisible != (wParam != 0)) {
7965 horizontalScrollBarVisible = wParam != 0;
7966 SetScrollBars();
7967 ReconfigureScrollBars();
7969 break;
7971 case SCI_GETHSCROLLBAR:
7972 return horizontalScrollBarVisible;
7974 case SCI_SETVSCROLLBAR:
7975 if (verticalScrollBarVisible != (wParam != 0)) {
7976 verticalScrollBarVisible = wParam != 0;
7977 SetScrollBars();
7978 ReconfigureScrollBars();
7980 break;
7982 case SCI_GETVSCROLLBAR:
7983 return verticalScrollBarVisible;
7985 case SCI_SETINDENTATIONGUIDES:
7986 vs.viewIndentationGuides = IndentView(wParam);
7987 Redraw();
7988 break;
7990 case SCI_GETINDENTATIONGUIDES:
7991 return vs.viewIndentationGuides;
7993 case SCI_SETHIGHLIGHTGUIDE:
7994 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
7995 highlightGuideColumn = wParam;
7996 Redraw();
7998 break;
8000 case SCI_GETHIGHLIGHTGUIDE:
8001 return highlightGuideColumn;
8003 case SCI_GETLINEENDPOSITION:
8004 return pdoc->LineEnd(wParam);
8006 case SCI_SETCODEPAGE:
8007 if (ValidCodePage(wParam)) {
8008 pdoc->dbcsCodePage = wParam;
8009 InvalidateStyleRedraw();
8011 break;
8013 case SCI_GETCODEPAGE:
8014 return pdoc->dbcsCodePage;
8016 #ifdef INCLUDE_DEPRECATED_FEATURES
8017 case SCI_SETUSEPALETTE:
8018 InvalidateStyleRedraw();
8019 break;
8021 case SCI_GETUSEPALETTE:
8022 return 0;
8023 #endif
8025 // Marker definition and setting
8026 case SCI_MARKERDEFINE:
8027 if (wParam <= MARKER_MAX) {
8028 vs.markers[wParam].markType = lParam;
8029 vs.CalcLargestMarkerHeight();
8031 InvalidateStyleData();
8032 RedrawSelMargin();
8033 break;
8035 case SCI_MARKERSYMBOLDEFINED:
8036 if (wParam <= MARKER_MAX)
8037 return vs.markers[wParam].markType;
8038 else
8039 return 0;
8041 case SCI_MARKERSETFORE:
8042 if (wParam <= MARKER_MAX)
8043 vs.markers[wParam].fore = ColourDesired(lParam);
8044 InvalidateStyleData();
8045 RedrawSelMargin();
8046 break;
8047 case SCI_MARKERSETBACKSELECTED:
8048 if (wParam <= MARKER_MAX)
8049 vs.markers[wParam].backSelected = ColourDesired(lParam);
8050 InvalidateStyleData();
8051 RedrawSelMargin();
8052 break;
8053 case SCI_MARKERENABLEHIGHLIGHT:
8054 highlightDelimiter.isEnabled = wParam == 1;
8055 RedrawSelMargin();
8056 break;
8057 case SCI_MARKERSETBACK:
8058 if (wParam <= MARKER_MAX)
8059 vs.markers[wParam].back = ColourDesired(lParam);
8060 InvalidateStyleData();
8061 RedrawSelMargin();
8062 break;
8063 case SCI_MARKERSETALPHA:
8064 if (wParam <= MARKER_MAX)
8065 vs.markers[wParam].alpha = lParam;
8066 InvalidateStyleRedraw();
8067 break;
8068 case SCI_MARKERADD: {
8069 int markerID = pdoc->AddMark(wParam, lParam);
8070 return markerID;
8072 case SCI_MARKERADDSET:
8073 if (lParam != 0)
8074 pdoc->AddMarkSet(wParam, lParam);
8075 break;
8077 case SCI_MARKERDELETE:
8078 pdoc->DeleteMark(wParam, lParam);
8079 break;
8081 case SCI_MARKERDELETEALL:
8082 pdoc->DeleteAllMarks(static_cast<int>(wParam));
8083 break;
8085 case SCI_MARKERGET:
8086 return pdoc->GetMark(wParam);
8088 case SCI_MARKERNEXT:
8089 return pdoc->MarkerNext(wParam, lParam);
8091 case SCI_MARKERPREVIOUS: {
8092 for (int iLine = wParam; iLine >= 0; iLine--) {
8093 if ((pdoc->GetMark(iLine) & lParam) != 0)
8094 return iLine;
8097 return -1;
8099 case SCI_MARKERDEFINEPIXMAP:
8100 if (wParam <= MARKER_MAX) {
8101 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
8102 vs.CalcLargestMarkerHeight();
8104 InvalidateStyleData();
8105 RedrawSelMargin();
8106 break;
8108 case SCI_RGBAIMAGESETWIDTH:
8109 sizeRGBAImage.x = wParam;
8110 break;
8112 case SCI_RGBAIMAGESETHEIGHT:
8113 sizeRGBAImage.y = wParam;
8114 break;
8116 case SCI_MARKERDEFINERGBAIMAGE:
8117 if (wParam <= MARKER_MAX) {
8118 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, reinterpret_cast<unsigned char *>(lParam));
8119 vs.CalcLargestMarkerHeight();
8121 InvalidateStyleData();
8122 RedrawSelMargin();
8123 break;
8125 case SCI_SETMARGINTYPEN:
8126 if (ValidMargin(wParam)) {
8127 vs.ms[wParam].style = lParam;
8128 InvalidateStyleRedraw();
8130 break;
8132 case SCI_GETMARGINTYPEN:
8133 if (ValidMargin(wParam))
8134 return vs.ms[wParam].style;
8135 else
8136 return 0;
8138 case SCI_SETMARGINWIDTHN:
8139 if (ValidMargin(wParam)) {
8140 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
8141 if (vs.ms[wParam].width != lParam) {
8142 vs.ms[wParam].width = lParam;
8143 InvalidateStyleRedraw();
8146 break;
8148 case SCI_GETMARGINWIDTHN:
8149 if (ValidMargin(wParam))
8150 return vs.ms[wParam].width;
8151 else
8152 return 0;
8154 case SCI_SETMARGINMASKN:
8155 if (ValidMargin(wParam)) {
8156 vs.ms[wParam].mask = lParam;
8157 InvalidateStyleRedraw();
8159 break;
8161 case SCI_GETMARGINMASKN:
8162 if (ValidMargin(wParam))
8163 return vs.ms[wParam].mask;
8164 else
8165 return 0;
8167 case SCI_SETMARGINSENSITIVEN:
8168 if (ValidMargin(wParam)) {
8169 vs.ms[wParam].sensitive = lParam != 0;
8170 InvalidateStyleRedraw();
8172 break;
8174 case SCI_GETMARGINSENSITIVEN:
8175 if (ValidMargin(wParam))
8176 return vs.ms[wParam].sensitive ? 1 : 0;
8177 else
8178 return 0;
8180 case SCI_SETMARGINCURSORN:
8181 if (ValidMargin(wParam))
8182 vs.ms[wParam].cursor = lParam;
8183 break;
8185 case SCI_GETMARGINCURSORN:
8186 if (ValidMargin(wParam))
8187 return vs.ms[wParam].cursor;
8188 else
8189 return 0;
8191 case SCI_STYLECLEARALL:
8192 vs.ClearStyles();
8193 InvalidateStyleRedraw();
8194 break;
8196 case SCI_STYLESETFORE:
8197 case SCI_STYLESETBACK:
8198 case SCI_STYLESETBOLD:
8199 case SCI_STYLESETWEIGHT:
8200 case SCI_STYLESETITALIC:
8201 case SCI_STYLESETEOLFILLED:
8202 case SCI_STYLESETSIZE:
8203 case SCI_STYLESETSIZEFRACTIONAL:
8204 case SCI_STYLESETFONT:
8205 case SCI_STYLESETUNDERLINE:
8206 case SCI_STYLESETCASE:
8207 case SCI_STYLESETCHARACTERSET:
8208 case SCI_STYLESETVISIBLE:
8209 case SCI_STYLESETCHANGEABLE:
8210 case SCI_STYLESETHOTSPOT:
8211 StyleSetMessage(iMessage, wParam, lParam);
8212 break;
8214 case SCI_STYLEGETFORE:
8215 case SCI_STYLEGETBACK:
8216 case SCI_STYLEGETBOLD:
8217 case SCI_STYLEGETWEIGHT:
8218 case SCI_STYLEGETITALIC:
8219 case SCI_STYLEGETEOLFILLED:
8220 case SCI_STYLEGETSIZE:
8221 case SCI_STYLEGETSIZEFRACTIONAL:
8222 case SCI_STYLEGETFONT:
8223 case SCI_STYLEGETUNDERLINE:
8224 case SCI_STYLEGETCASE:
8225 case SCI_STYLEGETCHARACTERSET:
8226 case SCI_STYLEGETVISIBLE:
8227 case SCI_STYLEGETCHANGEABLE:
8228 case SCI_STYLEGETHOTSPOT:
8229 return StyleGetMessage(iMessage, wParam, lParam);
8231 case SCI_STYLERESETDEFAULT:
8232 vs.ResetDefaultStyle();
8233 InvalidateStyleRedraw();
8234 break;
8235 case SCI_SETSTYLEBITS:
8236 vs.EnsureStyle((1 << wParam) - 1);
8237 pdoc->SetStylingBits(wParam);
8238 break;
8240 case SCI_GETSTYLEBITS:
8241 return pdoc->stylingBits;
8243 case SCI_SETLINESTATE:
8244 return pdoc->SetLineState(wParam, lParam);
8246 case SCI_GETLINESTATE:
8247 return pdoc->GetLineState(wParam);
8249 case SCI_GETMAXLINESTATE:
8250 return pdoc->GetMaxLineState();
8252 case SCI_GETCARETLINEVISIBLE:
8253 return vs.showCaretLineBackground;
8254 case SCI_SETCARETLINEVISIBLE:
8255 vs.showCaretLineBackground = wParam != 0;
8256 InvalidateStyleRedraw();
8257 break;
8258 case SCI_GETCARETLINEBACK:
8259 return vs.caretLineBackground.AsLong();
8260 case SCI_SETCARETLINEBACK:
8261 vs.caretLineBackground = wParam;
8262 InvalidateStyleRedraw();
8263 break;
8264 case SCI_GETCARETLINEBACKALPHA:
8265 return vs.caretLineAlpha;
8266 case SCI_SETCARETLINEBACKALPHA:
8267 vs.caretLineAlpha = wParam;
8268 InvalidateStyleRedraw();
8269 break;
8271 // Folding messages
8273 case SCI_VISIBLEFROMDOCLINE:
8274 return cs.DisplayFromDoc(wParam);
8276 case SCI_DOCLINEFROMVISIBLE:
8277 return cs.DocFromDisplay(wParam);
8279 case SCI_WRAPCOUNT:
8280 return WrapCount(wParam);
8282 case SCI_SETFOLDLEVEL: {
8283 int prev = pdoc->SetLevel(wParam, lParam);
8284 if (prev != lParam)
8285 RedrawSelMargin();
8286 return prev;
8289 case SCI_GETFOLDLEVEL:
8290 return pdoc->GetLevel(wParam);
8292 case SCI_GETLASTCHILD:
8293 return pdoc->GetLastChild(wParam, lParam);
8295 case SCI_GETFOLDPARENT:
8296 return pdoc->GetFoldParent(wParam);
8298 case SCI_SHOWLINES:
8299 cs.SetVisible(wParam, lParam, true);
8300 SetScrollBars();
8301 Redraw();
8302 break;
8304 case SCI_HIDELINES:
8305 if (wParam > 0)
8306 cs.SetVisible(wParam, lParam, false);
8307 SetScrollBars();
8308 Redraw();
8309 break;
8311 case SCI_GETLINEVISIBLE:
8312 return cs.GetVisible(wParam);
8314 case SCI_GETALLLINESVISIBLE:
8315 return cs.HiddenLines() ? 0 : 1;
8317 case SCI_SETFOLDEXPANDED:
8318 if (cs.SetExpanded(wParam, lParam != 0)) {
8319 RedrawSelMargin();
8321 break;
8323 case SCI_GETFOLDEXPANDED:
8324 return cs.GetExpanded(wParam);
8326 case SCI_SETFOLDFLAGS:
8327 foldFlags = wParam;
8328 Redraw();
8329 break;
8331 case SCI_TOGGLEFOLD:
8332 ToggleContraction(wParam);
8333 break;
8335 case SCI_CONTRACTEDFOLDNEXT:
8336 return ContractedFoldNext(wParam);
8338 case SCI_ENSUREVISIBLE:
8339 EnsureLineVisible(wParam, false);
8340 break;
8342 case SCI_ENSUREVISIBLEENFORCEPOLICY:
8343 EnsureLineVisible(wParam, true);
8344 break;
8346 case SCI_SEARCHANCHOR:
8347 SearchAnchor();
8348 break;
8350 case SCI_SEARCHNEXT:
8351 case SCI_SEARCHPREV:
8352 return SearchText(iMessage, wParam, lParam);
8354 case SCI_SETXCARETPOLICY:
8355 caretXPolicy = wParam;
8356 caretXSlop = lParam;
8357 break;
8359 case SCI_SETYCARETPOLICY:
8360 caretYPolicy = wParam;
8361 caretYSlop = lParam;
8362 break;
8364 case SCI_SETVISIBLEPOLICY:
8365 visiblePolicy = wParam;
8366 visibleSlop = lParam;
8367 break;
8369 case SCI_LINESONSCREEN:
8370 return LinesOnScreen();
8372 case SCI_SETSELFORE:
8373 vs.selforeset = wParam != 0;
8374 vs.selforeground = ColourDesired(lParam);
8375 vs.selAdditionalForeground = ColourDesired(lParam);
8376 InvalidateStyleRedraw();
8377 break;
8379 case SCI_SETSELBACK:
8380 vs.selbackset = wParam != 0;
8381 vs.selbackground = ColourDesired(lParam);
8382 vs.selAdditionalBackground = ColourDesired(lParam);
8383 InvalidateStyleRedraw();
8384 break;
8386 case SCI_SETSELALPHA:
8387 vs.selAlpha = wParam;
8388 vs.selAdditionalAlpha = wParam;
8389 InvalidateStyleRedraw();
8390 break;
8392 case SCI_GETSELALPHA:
8393 return vs.selAlpha;
8395 case SCI_GETSELEOLFILLED:
8396 return vs.selEOLFilled;
8398 case SCI_SETSELEOLFILLED:
8399 vs.selEOLFilled = wParam != 0;
8400 InvalidateStyleRedraw();
8401 break;
8403 case SCI_SETWHITESPACEFORE:
8404 vs.whitespaceForegroundSet = wParam != 0;
8405 vs.whitespaceForeground = ColourDesired(lParam);
8406 InvalidateStyleRedraw();
8407 break;
8409 case SCI_SETWHITESPACEBACK:
8410 vs.whitespaceBackgroundSet = wParam != 0;
8411 vs.whitespaceBackground = ColourDesired(lParam);
8412 InvalidateStyleRedraw();
8413 break;
8415 case SCI_SETCARETFORE:
8416 vs.caretcolour = ColourDesired(wParam);
8417 InvalidateStyleRedraw();
8418 break;
8420 case SCI_GETCARETFORE:
8421 return vs.caretcolour.AsLong();
8423 case SCI_SETCARETSTYLE:
8424 if (wParam <= CARETSTYLE_BLOCK)
8425 vs.caretStyle = wParam;
8426 else
8427 /* Default to the line caret */
8428 vs.caretStyle = CARETSTYLE_LINE;
8429 InvalidateStyleRedraw();
8430 break;
8432 case SCI_GETCARETSTYLE:
8433 return vs.caretStyle;
8435 case SCI_SETCARETWIDTH:
8436 if (static_cast<int>(wParam) <= 0)
8437 vs.caretWidth = 0;
8438 else if (wParam >= 3)
8439 vs.caretWidth = 3;
8440 else
8441 vs.caretWidth = wParam;
8442 InvalidateStyleRedraw();
8443 break;
8445 case SCI_GETCARETWIDTH:
8446 return vs.caretWidth;
8448 case SCI_ASSIGNCMDKEY:
8449 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8450 Platform::HighShortFromLong(wParam), lParam);
8451 break;
8453 case SCI_CLEARCMDKEY:
8454 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8455 Platform::HighShortFromLong(wParam), SCI_NULL);
8456 break;
8458 case SCI_CLEARALLCMDKEYS:
8459 kmap.Clear();
8460 break;
8462 case SCI_INDICSETSTYLE:
8463 if (wParam <= INDIC_MAX) {
8464 vs.indicators[wParam].style = lParam;
8465 InvalidateStyleRedraw();
8467 break;
8469 case SCI_INDICGETSTYLE:
8470 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
8472 case SCI_INDICSETFORE:
8473 if (wParam <= INDIC_MAX) {
8474 vs.indicators[wParam].fore = ColourDesired(lParam);
8475 InvalidateStyleRedraw();
8477 break;
8479 case SCI_INDICGETFORE:
8480 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.AsLong() : 0;
8482 case SCI_INDICSETUNDER:
8483 if (wParam <= INDIC_MAX) {
8484 vs.indicators[wParam].under = lParam != 0;
8485 InvalidateStyleRedraw();
8487 break;
8489 case SCI_INDICGETUNDER:
8490 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
8492 case SCI_INDICSETALPHA:
8493 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8494 vs.indicators[wParam].fillAlpha = lParam;
8495 InvalidateStyleRedraw();
8497 break;
8499 case SCI_INDICGETALPHA:
8500 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
8502 case SCI_INDICSETOUTLINEALPHA:
8503 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8504 vs.indicators[wParam].outlineAlpha = lParam;
8505 InvalidateStyleRedraw();
8507 break;
8509 case SCI_INDICGETOUTLINEALPHA:
8510 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
8512 case SCI_SETINDICATORCURRENT:
8513 pdoc->decorations.SetCurrentIndicator(wParam);
8514 break;
8515 case SCI_GETINDICATORCURRENT:
8516 return pdoc->decorations.GetCurrentIndicator();
8517 case SCI_SETINDICATORVALUE:
8518 pdoc->decorations.SetCurrentValue(wParam);
8519 break;
8520 case SCI_GETINDICATORVALUE:
8521 return pdoc->decorations.GetCurrentValue();
8523 case SCI_INDICATORFILLRANGE:
8524 pdoc->DecorationFillRange(wParam, pdoc->decorations.GetCurrentValue(), lParam);
8525 break;
8527 case SCI_INDICATORCLEARRANGE:
8528 pdoc->DecorationFillRange(wParam, 0, lParam);
8529 break;
8531 case SCI_INDICATORALLONFOR:
8532 return pdoc->decorations.AllOnFor(wParam);
8534 case SCI_INDICATORVALUEAT:
8535 return pdoc->decorations.ValueAt(wParam, lParam);
8537 case SCI_INDICATORSTART:
8538 return pdoc->decorations.Start(wParam, lParam);
8540 case SCI_INDICATOREND:
8541 return pdoc->decorations.End(wParam, lParam);
8543 case SCI_LINEDOWN:
8544 case SCI_LINEDOWNEXTEND:
8545 case SCI_PARADOWN:
8546 case SCI_PARADOWNEXTEND:
8547 case SCI_LINEUP:
8548 case SCI_LINEUPEXTEND:
8549 case SCI_PARAUP:
8550 case SCI_PARAUPEXTEND:
8551 case SCI_CHARLEFT:
8552 case SCI_CHARLEFTEXTEND:
8553 case SCI_CHARRIGHT:
8554 case SCI_CHARRIGHTEXTEND:
8555 case SCI_WORDLEFT:
8556 case SCI_WORDLEFTEXTEND:
8557 case SCI_WORDRIGHT:
8558 case SCI_WORDRIGHTEXTEND:
8559 case SCI_WORDLEFTEND:
8560 case SCI_WORDLEFTENDEXTEND:
8561 case SCI_WORDRIGHTEND:
8562 case SCI_WORDRIGHTENDEXTEND:
8563 case SCI_HOME:
8564 case SCI_HOMEEXTEND:
8565 case SCI_LINEEND:
8566 case SCI_LINEENDEXTEND:
8567 case SCI_HOMEWRAP:
8568 case SCI_HOMEWRAPEXTEND:
8569 case SCI_LINEENDWRAP:
8570 case SCI_LINEENDWRAPEXTEND:
8571 case SCI_DOCUMENTSTART:
8572 case SCI_DOCUMENTSTARTEXTEND:
8573 case SCI_DOCUMENTEND:
8574 case SCI_DOCUMENTENDEXTEND:
8575 case SCI_SCROLLTOSTART:
8576 case SCI_SCROLLTOEND:
8578 case SCI_STUTTEREDPAGEUP:
8579 case SCI_STUTTEREDPAGEUPEXTEND:
8580 case SCI_STUTTEREDPAGEDOWN:
8581 case SCI_STUTTEREDPAGEDOWNEXTEND:
8583 case SCI_PAGEUP:
8584 case SCI_PAGEUPEXTEND:
8585 case SCI_PAGEDOWN:
8586 case SCI_PAGEDOWNEXTEND:
8587 case SCI_EDITTOGGLEOVERTYPE:
8588 case SCI_CANCEL:
8589 case SCI_DELETEBACK:
8590 case SCI_TAB:
8591 case SCI_BACKTAB:
8592 case SCI_NEWLINE:
8593 case SCI_FORMFEED:
8594 case SCI_VCHOME:
8595 case SCI_VCHOMEEXTEND:
8596 case SCI_VCHOMEWRAP:
8597 case SCI_VCHOMEWRAPEXTEND:
8598 case SCI_ZOOMIN:
8599 case SCI_ZOOMOUT:
8600 case SCI_DELWORDLEFT:
8601 case SCI_DELWORDRIGHT:
8602 case SCI_DELWORDRIGHTEND:
8603 case SCI_DELLINELEFT:
8604 case SCI_DELLINERIGHT:
8605 case SCI_LINECOPY:
8606 case SCI_LINECUT:
8607 case SCI_LINEDELETE:
8608 case SCI_LINETRANSPOSE:
8609 case SCI_LINEDUPLICATE:
8610 case SCI_LOWERCASE:
8611 case SCI_UPPERCASE:
8612 case SCI_LINESCROLLDOWN:
8613 case SCI_LINESCROLLUP:
8614 case SCI_WORDPARTLEFT:
8615 case SCI_WORDPARTLEFTEXTEND:
8616 case SCI_WORDPARTRIGHT:
8617 case SCI_WORDPARTRIGHTEXTEND:
8618 case SCI_DELETEBACKNOTLINE:
8619 case SCI_HOMEDISPLAY:
8620 case SCI_HOMEDISPLAYEXTEND:
8621 case SCI_LINEENDDISPLAY:
8622 case SCI_LINEENDDISPLAYEXTEND:
8623 case SCI_LINEDOWNRECTEXTEND:
8624 case SCI_LINEUPRECTEXTEND:
8625 case SCI_CHARLEFTRECTEXTEND:
8626 case SCI_CHARRIGHTRECTEXTEND:
8627 case SCI_HOMERECTEXTEND:
8628 case SCI_VCHOMERECTEXTEND:
8629 case SCI_LINEENDRECTEXTEND:
8630 case SCI_PAGEUPRECTEXTEND:
8631 case SCI_PAGEDOWNRECTEXTEND:
8632 case SCI_SELECTIONDUPLICATE:
8633 return KeyCommand(iMessage);
8635 case SCI_BRACEHIGHLIGHT:
8636 SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
8637 break;
8639 case SCI_BRACEHIGHLIGHTINDICATOR:
8640 if (lParam >= 0 && lParam <= INDIC_MAX) {
8641 vs.braceHighlightIndicatorSet = wParam != 0;
8642 vs.braceHighlightIndicator = lParam;
8644 break;
8646 case SCI_BRACEBADLIGHT:
8647 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
8648 break;
8650 case SCI_BRACEBADLIGHTINDICATOR:
8651 if (lParam >= 0 && lParam <= INDIC_MAX) {
8652 vs.braceBadLightIndicatorSet = wParam != 0;
8653 vs.braceBadLightIndicator = lParam;
8655 break;
8657 case SCI_BRACEMATCH:
8658 // wParam is position of char to find brace for,
8659 // lParam is maximum amount of text to restyle to find it
8660 return pdoc->BraceMatch(wParam, lParam);
8662 case SCI_GETVIEWEOL:
8663 return vs.viewEOL;
8665 case SCI_SETVIEWEOL:
8666 vs.viewEOL = wParam != 0;
8667 InvalidateStyleRedraw();
8668 break;
8670 case SCI_SETZOOM:
8671 vs.zoomLevel = wParam;
8672 InvalidateStyleRedraw();
8673 NotifyZoom();
8674 break;
8676 case SCI_GETZOOM:
8677 return vs.zoomLevel;
8679 case SCI_GETEDGECOLUMN:
8680 return theEdge;
8682 case SCI_SETEDGECOLUMN:
8683 theEdge = wParam;
8684 InvalidateStyleRedraw();
8685 break;
8687 case SCI_GETEDGEMODE:
8688 return vs.edgeState;
8690 case SCI_SETEDGEMODE:
8691 vs.edgeState = wParam;
8692 InvalidateStyleRedraw();
8693 break;
8695 case SCI_GETEDGECOLOUR:
8696 return vs.edgecolour.AsLong();
8698 case SCI_SETEDGECOLOUR:
8699 vs.edgecolour = ColourDesired(wParam);
8700 InvalidateStyleRedraw();
8701 break;
8703 case SCI_GETDOCPOINTER:
8704 return reinterpret_cast<sptr_t>(pdoc);
8706 case SCI_SETDOCPOINTER:
8707 CancelModes();
8708 SetDocPointer(reinterpret_cast<Document *>(lParam));
8709 return 0;
8711 case SCI_CREATEDOCUMENT: {
8712 Document *doc = new Document();
8713 if (doc) {
8714 doc->AddRef();
8716 return reinterpret_cast<sptr_t>(doc);
8719 case SCI_ADDREFDOCUMENT:
8720 (reinterpret_cast<Document *>(lParam))->AddRef();
8721 break;
8723 case SCI_RELEASEDOCUMENT:
8724 (reinterpret_cast<Document *>(lParam))->Release();
8725 break;
8727 case SCI_CREATELOADER: {
8728 Document *doc = new Document();
8729 if (doc) {
8730 doc->AddRef();
8731 doc->Allocate(wParam);
8732 doc->SetUndoCollection(false);
8734 return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
8737 case SCI_SETMODEVENTMASK:
8738 modEventMask = wParam;
8739 return 0;
8741 case SCI_GETMODEVENTMASK:
8742 return modEventMask;
8744 case SCI_CONVERTEOLS:
8745 pdoc->ConvertLineEnds(wParam);
8746 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
8747 return 0;
8749 case SCI_SETLENGTHFORENCODE:
8750 lengthForEncode = wParam;
8751 return 0;
8753 case SCI_SELECTIONISRECTANGLE:
8754 return sel.selType == Selection::selRectangle ? 1 : 0;
8756 case SCI_SETSELECTIONMODE: {
8757 switch (wParam) {
8758 case SC_SEL_STREAM:
8759 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
8760 sel.selType = Selection::selStream;
8761 break;
8762 case SC_SEL_RECTANGLE:
8763 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
8764 sel.selType = Selection::selRectangle;
8765 break;
8766 case SC_SEL_LINES:
8767 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
8768 sel.selType = Selection::selLines;
8769 break;
8770 case SC_SEL_THIN:
8771 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
8772 sel.selType = Selection::selThin;
8773 break;
8774 default:
8775 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
8776 sel.selType = Selection::selStream;
8778 InvalidateSelection(sel.RangeMain(), true);
8780 case SCI_GETSELECTIONMODE:
8781 switch (sel.selType) {
8782 case Selection::selStream:
8783 return SC_SEL_STREAM;
8784 case Selection::selRectangle:
8785 return SC_SEL_RECTANGLE;
8786 case Selection::selLines:
8787 return SC_SEL_LINES;
8788 case Selection::selThin:
8789 return SC_SEL_THIN;
8790 default: // ?!
8791 return SC_SEL_STREAM;
8793 case SCI_GETLINESELSTARTPOSITION:
8794 case SCI_GETLINESELENDPOSITION: {
8795 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(wParam)),
8796 SelectionPosition(pdoc->LineEnd(wParam)));
8797 for (size_t r=0; r<sel.Count(); r++) {
8798 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
8799 if (portion.start.IsValid()) {
8800 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
8803 return INVALID_POSITION;
8806 case SCI_SETOVERTYPE:
8807 inOverstrike = wParam != 0;
8808 break;
8810 case SCI_GETOVERTYPE:
8811 return inOverstrike ? 1 : 0;
8813 case SCI_SETFOCUS:
8814 SetFocusState(wParam != 0);
8815 break;
8817 case SCI_GETFOCUS:
8818 return hasFocus;
8820 case SCI_SETSTATUS:
8821 errorStatus = wParam;
8822 break;
8824 case SCI_GETSTATUS:
8825 return errorStatus;
8827 case SCI_SETMOUSEDOWNCAPTURES:
8828 mouseDownCaptures = wParam != 0;
8829 break;
8831 case SCI_GETMOUSEDOWNCAPTURES:
8832 return mouseDownCaptures;
8834 case SCI_SETCURSOR:
8835 cursorMode = wParam;
8836 DisplayCursor(Window::cursorText);
8837 break;
8839 case SCI_GETCURSOR:
8840 return cursorMode;
8842 case SCI_SETCONTROLCHARSYMBOL:
8843 controlCharSymbol = wParam;
8844 break;
8846 case SCI_GETCONTROLCHARSYMBOL:
8847 return controlCharSymbol;
8849 case SCI_STARTRECORD:
8850 recordingMacro = true;
8851 return 0;
8853 case SCI_STOPRECORD:
8854 recordingMacro = false;
8855 return 0;
8857 case SCI_MOVECARETINSIDEVIEW:
8858 MoveCaretInsideView();
8859 break;
8861 case SCI_SETFOLDMARGINCOLOUR:
8862 vs.foldmarginColourSet = wParam != 0;
8863 vs.foldmarginColour = ColourDesired(lParam);
8864 InvalidateStyleRedraw();
8865 break;
8867 case SCI_SETFOLDMARGINHICOLOUR:
8868 vs.foldmarginHighlightColourSet = wParam != 0;
8869 vs.foldmarginHighlightColour = ColourDesired(lParam);
8870 InvalidateStyleRedraw();
8871 break;
8873 case SCI_SETHOTSPOTACTIVEFORE:
8874 vs.hotspotForegroundSet = wParam != 0;
8875 vs.hotspotForeground = ColourDesired(lParam);
8876 InvalidateStyleRedraw();
8877 break;
8879 case SCI_GETHOTSPOTACTIVEFORE:
8880 return vs.hotspotForeground.AsLong();
8882 case SCI_SETHOTSPOTACTIVEBACK:
8883 vs.hotspotBackgroundSet = wParam != 0;
8884 vs.hotspotBackground = ColourDesired(lParam);
8885 InvalidateStyleRedraw();
8886 break;
8888 case SCI_GETHOTSPOTACTIVEBACK:
8889 return vs.hotspotBackground.AsLong();
8891 case SCI_SETHOTSPOTACTIVEUNDERLINE:
8892 vs.hotspotUnderline = wParam != 0;
8893 InvalidateStyleRedraw();
8894 break;
8896 case SCI_GETHOTSPOTACTIVEUNDERLINE:
8897 return vs.hotspotUnderline ? 1 : 0;
8899 case SCI_SETHOTSPOTSINGLELINE:
8900 vs.hotspotSingleLine = wParam != 0;
8901 InvalidateStyleRedraw();
8902 break;
8904 case SCI_GETHOTSPOTSINGLELINE:
8905 return vs.hotspotSingleLine ? 1 : 0;
8907 case SCI_SETPASTECONVERTENDINGS:
8908 convertPastes = wParam != 0;
8909 break;
8911 case SCI_GETPASTECONVERTENDINGS:
8912 return convertPastes ? 1 : 0;
8914 case SCI_GETCHARACTERPOINTER:
8915 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
8917 case SCI_GETRANGEPOINTER:
8918 return reinterpret_cast<sptr_t>(pdoc->RangePointer(wParam, lParam));
8920 case SCI_GETGAPPOSITION:
8921 return pdoc->GapPosition();
8923 case SCI_SETEXTRAASCENT:
8924 vs.extraAscent = wParam;
8925 InvalidateStyleRedraw();
8926 break;
8928 case SCI_GETEXTRAASCENT:
8929 return vs.extraAscent;
8931 case SCI_SETEXTRADESCENT:
8932 vs.extraDescent = wParam;
8933 InvalidateStyleRedraw();
8934 break;
8936 case SCI_GETEXTRADESCENT:
8937 return vs.extraDescent;
8939 case SCI_MARGINSETSTYLEOFFSET:
8940 vs.marginStyleOffset = wParam;
8941 InvalidateStyleRedraw();
8942 break;
8944 case SCI_MARGINGETSTYLEOFFSET:
8945 return vs.marginStyleOffset;
8947 case SCI_SETMARGINOPTIONS:
8948 marginOptions = wParam;
8949 break;
8951 case SCI_GETMARGINOPTIONS:
8952 return marginOptions;
8954 case SCI_MARGINSETTEXT:
8955 pdoc->MarginSetText(wParam, CharPtrFromSPtr(lParam));
8956 break;
8958 case SCI_MARGINGETTEXT: {
8959 const StyledText st = pdoc->MarginStyledText(wParam);
8960 if (lParam) {
8961 if (st.text)
8962 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
8963 else
8964 strcpy(CharPtrFromSPtr(lParam), "");
8966 return st.length;
8969 case SCI_MARGINSETSTYLE:
8970 pdoc->MarginSetStyle(wParam, lParam);
8971 break;
8973 case SCI_MARGINGETSTYLE: {
8974 const StyledText st = pdoc->MarginStyledText(wParam);
8975 return st.style;
8978 case SCI_MARGINSETSTYLES:
8979 pdoc->MarginSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
8980 break;
8982 case SCI_MARGINGETSTYLES: {
8983 const StyledText st = pdoc->MarginStyledText(wParam);
8984 if (lParam) {
8985 if (st.styles)
8986 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
8987 else
8988 strcpy(CharPtrFromSPtr(lParam), "");
8990 return st.styles ? st.length : 0;
8993 case SCI_MARGINTEXTCLEARALL:
8994 pdoc->MarginClearAll();
8995 break;
8997 case SCI_ANNOTATIONSETTEXT:
8998 pdoc->AnnotationSetText(wParam, CharPtrFromSPtr(lParam));
8999 break;
9001 case SCI_ANNOTATIONGETTEXT: {
9002 const StyledText st = pdoc->AnnotationStyledText(wParam);
9003 if (lParam) {
9004 if (st.text)
9005 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
9006 else
9007 strcpy(CharPtrFromSPtr(lParam), "");
9009 return st.length;
9012 case SCI_ANNOTATIONGETSTYLE: {
9013 const StyledText st = pdoc->AnnotationStyledText(wParam);
9014 return st.style;
9017 case SCI_ANNOTATIONSETSTYLE:
9018 pdoc->AnnotationSetStyle(wParam, lParam);
9019 break;
9021 case SCI_ANNOTATIONSETSTYLES:
9022 pdoc->AnnotationSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
9023 break;
9025 case SCI_ANNOTATIONGETSTYLES: {
9026 const StyledText st = pdoc->AnnotationStyledText(wParam);
9027 if (lParam) {
9028 if (st.styles)
9029 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
9030 else
9031 strcpy(CharPtrFromSPtr(lParam), "");
9033 return st.styles ? st.length : 0;
9036 case SCI_ANNOTATIONGETLINES:
9037 return pdoc->AnnotationLines(wParam);
9039 case SCI_ANNOTATIONCLEARALL:
9040 pdoc->AnnotationClearAll();
9041 break;
9043 case SCI_ANNOTATIONSETVISIBLE:
9044 SetAnnotationVisible(wParam);
9045 break;
9047 case SCI_ANNOTATIONGETVISIBLE:
9048 return vs.annotationVisible;
9050 case SCI_ANNOTATIONSETSTYLEOFFSET:
9051 vs.annotationStyleOffset = wParam;
9052 InvalidateStyleRedraw();
9053 break;
9055 case SCI_ANNOTATIONGETSTYLEOFFSET:
9056 return vs.annotationStyleOffset;
9058 case SCI_ADDUNDOACTION:
9059 pdoc->AddUndoAction(wParam, lParam & UNDO_MAY_COALESCE);
9060 break;
9062 case SCI_SETMULTIPLESELECTION:
9063 multipleSelection = wParam != 0;
9064 InvalidateCaret();
9065 break;
9067 case SCI_GETMULTIPLESELECTION:
9068 return multipleSelection;
9070 case SCI_SETADDITIONALSELECTIONTYPING:
9071 additionalSelectionTyping = wParam != 0;
9072 InvalidateCaret();
9073 break;
9075 case SCI_GETADDITIONALSELECTIONTYPING:
9076 return additionalSelectionTyping;
9078 case SCI_SETMULTIPASTE:
9079 multiPasteMode = wParam;
9080 break;
9082 case SCI_GETMULTIPASTE:
9083 return multiPasteMode;
9085 case SCI_SETADDITIONALCARETSBLINK:
9086 additionalCaretsBlink = wParam != 0;
9087 InvalidateCaret();
9088 break;
9090 case SCI_GETADDITIONALCARETSBLINK:
9091 return additionalCaretsBlink;
9093 case SCI_SETADDITIONALCARETSVISIBLE:
9094 additionalCaretsVisible = wParam != 0;
9095 InvalidateCaret();
9096 break;
9098 case SCI_GETADDITIONALCARETSVISIBLE:
9099 return additionalCaretsVisible;
9101 case SCI_GETSELECTIONS:
9102 return sel.Count();
9104 case SCI_CLEARSELECTIONS:
9105 sel.Clear();
9106 Redraw();
9107 break;
9109 case SCI_SETSELECTION:
9110 sel.SetSelection(SelectionRange(wParam, lParam));
9111 Redraw();
9112 break;
9114 case SCI_ADDSELECTION:
9115 sel.AddSelection(SelectionRange(wParam, lParam));
9116 Redraw();
9117 break;
9119 case SCI_SETMAINSELECTION:
9120 sel.SetMain(wParam);
9121 Redraw();
9122 break;
9124 case SCI_GETMAINSELECTION:
9125 return sel.Main();
9127 case SCI_SETSELECTIONNCARET:
9128 sel.Range(wParam).caret.SetPosition(lParam);
9129 Redraw();
9130 break;
9132 case SCI_GETSELECTIONNCARET:
9133 return sel.Range(wParam).caret.Position();
9135 case SCI_SETSELECTIONNANCHOR:
9136 sel.Range(wParam).anchor.SetPosition(lParam);
9137 Redraw();
9138 break;
9139 case SCI_GETSELECTIONNANCHOR:
9140 return sel.Range(wParam).anchor.Position();
9142 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
9143 sel.Range(wParam).caret.SetVirtualSpace(lParam);
9144 Redraw();
9145 break;
9147 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
9148 return sel.Range(wParam).caret.VirtualSpace();
9150 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
9151 sel.Range(wParam).anchor.SetVirtualSpace(lParam);
9152 Redraw();
9153 break;
9155 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
9156 return sel.Range(wParam).anchor.VirtualSpace();
9158 case SCI_SETSELECTIONNSTART:
9159 sel.Range(wParam).anchor.SetPosition(lParam);
9160 Redraw();
9161 break;
9163 case SCI_GETSELECTIONNSTART:
9164 return sel.Range(wParam).Start().Position();
9166 case SCI_SETSELECTIONNEND:
9167 sel.Range(wParam).caret.SetPosition(lParam);
9168 Redraw();
9169 break;
9171 case SCI_GETSELECTIONNEND:
9172 return sel.Range(wParam).End().Position();
9174 case SCI_SETRECTANGULARSELECTIONCARET:
9175 if (!sel.IsRectangular())
9176 sel.Clear();
9177 sel.selType = Selection::selRectangle;
9178 sel.Rectangular().caret.SetPosition(wParam);
9179 SetRectangularRange();
9180 Redraw();
9181 break;
9183 case SCI_GETRECTANGULARSELECTIONCARET:
9184 return sel.Rectangular().caret.Position();
9186 case SCI_SETRECTANGULARSELECTIONANCHOR:
9187 if (!sel.IsRectangular())
9188 sel.Clear();
9189 sel.selType = Selection::selRectangle;
9190 sel.Rectangular().anchor.SetPosition(wParam);
9191 SetRectangularRange();
9192 Redraw();
9193 break;
9195 case SCI_GETRECTANGULARSELECTIONANCHOR:
9196 return sel.Rectangular().anchor.Position();
9198 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9199 if (!sel.IsRectangular())
9200 sel.Clear();
9201 sel.selType = Selection::selRectangle;
9202 sel.Rectangular().caret.SetVirtualSpace(wParam);
9203 SetRectangularRange();
9204 Redraw();
9205 break;
9207 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9208 return sel.Rectangular().caret.VirtualSpace();
9210 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9211 if (!sel.IsRectangular())
9212 sel.Clear();
9213 sel.selType = Selection::selRectangle;
9214 sel.Rectangular().anchor.SetVirtualSpace(wParam);
9215 SetRectangularRange();
9216 Redraw();
9217 break;
9219 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9220 return sel.Rectangular().anchor.VirtualSpace();
9222 case SCI_SETVIRTUALSPACEOPTIONS:
9223 virtualSpaceOptions = wParam;
9224 break;
9226 case SCI_GETVIRTUALSPACEOPTIONS:
9227 return virtualSpaceOptions;
9229 case SCI_SETADDITIONALSELFORE:
9230 vs.selAdditionalForeground = ColourDesired(wParam);
9231 InvalidateStyleRedraw();
9232 break;
9234 case SCI_SETADDITIONALSELBACK:
9235 vs.selAdditionalBackground = ColourDesired(wParam);
9236 InvalidateStyleRedraw();
9237 break;
9239 case SCI_SETADDITIONALSELALPHA:
9240 vs.selAdditionalAlpha = wParam;
9241 InvalidateStyleRedraw();
9242 break;
9244 case SCI_GETADDITIONALSELALPHA:
9245 return vs.selAdditionalAlpha;
9247 case SCI_SETADDITIONALCARETFORE:
9248 vs.additionalCaretColour = ColourDesired(wParam);
9249 InvalidateStyleRedraw();
9250 break;
9252 case SCI_GETADDITIONALCARETFORE:
9253 return vs.additionalCaretColour.AsLong();
9255 case SCI_ROTATESELECTION:
9256 sel.RotateMain();
9257 InvalidateSelection(sel.RangeMain(), true);
9258 break;
9260 case SCI_SWAPMAINANCHORCARET:
9261 InvalidateSelection(sel.RangeMain());
9262 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
9263 break;
9265 case SCI_CHANGELEXERSTATE:
9266 pdoc->ChangeLexerState(wParam, lParam);
9267 break;
9269 case SCI_SETIDENTIFIER:
9270 SetCtrlID(wParam);
9271 break;
9273 case SCI_GETIDENTIFIER:
9274 return GetCtrlID();
9276 case SCI_SETTECHNOLOGY:
9277 // No action by default
9278 break;
9280 case SCI_GETTECHNOLOGY:
9281 return technology;
9283 case SCI_COUNTCHARACTERS:
9284 return pdoc->CountCharacters(wParam, lParam);
9286 default:
9287 return DefWndProc(iMessage, wParam, lParam);
9289 //Platform::DebugPrintf("end wnd proc\n");
9290 return 0l;