applied backgroundcolors.patch
[TortoiseGit.git] / ext / scintilla / src / Editor.cxx
blobb87ce7002106a7feb3015d9ac8f38dc0eee66591
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;
107 scaleRGBAImage = 100;
109 printMagnification = 0;
110 printColourMode = SC_PRINT_NORMAL;
111 printWrapState = eWrapWord;
112 cursorMode = SC_CURSORNORMAL;
113 controlCharSymbol = 0; /* Draw the control characters */
115 hasFocus = false;
116 hideSelection = false;
117 inOverstrike = false;
118 errorStatus = 0;
119 mouseDownCaptures = true;
121 bufferedDraw = true;
122 twoPhaseDraw = true;
124 lastClickTime = 0;
125 dwellDelay = SC_TIME_FOREVER;
126 ticksToDwell = SC_TIME_FOREVER;
127 dwelling = false;
128 ptMouseLast.x = 0;
129 ptMouseLast.y = 0;
130 inDragDrop = ddNone;
131 dropWentOutside = false;
132 posDrag = SelectionPosition(invalidPosition);
133 posDrop = SelectionPosition(invalidPosition);
134 hotSpotClickPos = INVALID_POSITION;
135 selectionType = selChar;
137 lastXChosen = 0;
138 lineAnchorPos = 0;
139 originalAnchorPos = 0;
140 wordSelectAnchorStartPos = 0;
141 wordSelectAnchorEndPos = 0;
142 wordSelectInitialCaretPos = -1;
144 primarySelection = true;
146 caretXPolicy = CARET_SLOP | CARET_EVEN;
147 caretXSlop = 50;
149 caretYPolicy = CARET_EVEN;
150 caretYSlop = 0;
152 visiblePolicy = 0;
153 visibleSlop = 0;
155 searchAnchor = 0;
157 xOffset = 0;
158 xCaretMargin = 50;
159 horizontalScrollBarVisible = true;
160 scrollWidth = 2000;
161 trackLineWidth = false;
162 lineWidthMaxSeen = 0;
163 verticalScrollBarVisible = true;
164 endAtLastLine = true;
165 caretSticky = SC_CARETSTICKY_OFF;
166 marginOptions = SC_MARGINOPTION_NONE;
167 multipleSelection = false;
168 additionalSelectionTyping = false;
169 multiPasteMode = SC_MULTIPASTE_ONCE;
170 additionalCaretsBlink = true;
171 additionalCaretsVisible = true;
172 virtualSpaceOptions = SCVS_NONE;
174 pixmapLine = 0;
175 pixmapSelMargin = 0;
176 pixmapSelPattern = 0;
177 pixmapIndentGuide = 0;
178 pixmapIndentGuideHighlight = 0;
180 targetStart = 0;
181 targetEnd = 0;
182 searchFlags = 0;
184 topLine = 0;
185 posTopLine = 0;
187 lengthForEncode = -1;
189 needUpdateUI = 0;
190 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
191 braces[0] = invalidPosition;
192 braces[1] = invalidPosition;
193 bracesMatchStyle = STYLE_BRACEBAD;
194 highlightGuideColumn = 0;
196 theEdge = 0;
198 paintState = notPainting;
199 willRedrawAll = false;
201 modEventMask = SC_MODEVENTMASKALL;
203 pdoc = new Document();
204 pdoc->AddRef();
205 pdoc->AddWatcher(this, 0);
207 recordingMacro = false;
208 foldFlags = 0;
210 wrapState = eWrapNone;
211 wrapWidth = LineLayout::wrapWidthInfinite;
212 wrapStart = wrapLineLarge;
213 wrapEnd = wrapLineLarge;
214 wrapVisualFlags = 0;
215 wrapVisualFlagsLocation = 0;
216 wrapVisualStartIndent = 0;
217 wrapIndentMode = SC_WRAPINDENT_FIXED;
219 convertPastes = true;
221 marginNumberPadding = 3;
222 ctrlCharPadding = 3; // +3 For a blank on front and rounded edge each side
223 lastSegItalicsOffset = 2;
225 hsStart = -1;
226 hsEnd = -1;
228 llc.SetLevel(LineLayoutCache::llcCaret);
229 posCache.SetSize(0x400);
232 Editor::~Editor() {
233 pdoc->RemoveWatcher(this, 0);
234 pdoc->Release();
235 pdoc = 0;
236 DropGraphics(true);
239 void Editor::Finalise() {
240 SetIdle(false);
241 CancelModes();
244 void Editor::DropGraphics(bool freeObjects) {
245 if (freeObjects) {
246 delete pixmapLine;
247 pixmapLine = 0;
248 delete pixmapSelMargin;
249 pixmapSelMargin = 0;
250 delete pixmapSelPattern;
251 pixmapSelPattern = 0;
252 delete pixmapIndentGuide;
253 pixmapIndentGuide = 0;
254 delete pixmapIndentGuideHighlight;
255 pixmapIndentGuideHighlight = 0;
256 } else {
257 if (pixmapLine)
258 pixmapLine->Release();
259 if (pixmapSelMargin)
260 pixmapSelMargin->Release();
261 if (pixmapSelPattern)
262 pixmapSelPattern->Release();
263 if (pixmapIndentGuide)
264 pixmapIndentGuide->Release();
265 if (pixmapIndentGuideHighlight)
266 pixmapIndentGuideHighlight->Release();
270 void Editor::AllocateGraphics() {
271 if (!pixmapLine)
272 pixmapLine = Surface::Allocate(technology);
273 if (!pixmapSelMargin)
274 pixmapSelMargin = Surface::Allocate(technology);
275 if (!pixmapSelPattern)
276 pixmapSelPattern = Surface::Allocate(technology);
277 if (!pixmapIndentGuide)
278 pixmapIndentGuide = Surface::Allocate(technology);
279 if (!pixmapIndentGuideHighlight)
280 pixmapIndentGuideHighlight = Surface::Allocate(technology);
283 void Editor::InvalidateStyleData() {
284 stylesValid = false;
285 vs.technology = technology;
286 DropGraphics(false);
287 AllocateGraphics();
288 llc.Invalidate(LineLayout::llInvalid);
289 posCache.Clear();
292 void Editor::InvalidateStyleRedraw() {
293 NeedWrapping();
294 InvalidateStyleData();
295 Redraw();
298 void Editor::RefreshStyleData() {
299 if (!stylesValid) {
300 stylesValid = true;
301 AutoSurface surface(this);
302 if (surface) {
303 vs.Refresh(*surface);
305 SetScrollBars();
306 SetRectangularRange();
310 PRectangle Editor::GetClientRectangle() {
311 return wMain.GetClientPosition();
314 PRectangle Editor::GetTextRectangle() {
315 PRectangle rc = GetClientRectangle();
316 rc.left += vs.fixedColumnWidth;
317 rc.right -= vs.rightMarginWidth;
318 return rc;
321 int Editor::LinesOnScreen() {
322 PRectangle rcClient = GetClientRectangle();
323 int htClient = rcClient.bottom - rcClient.top;
324 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
325 return htClient / vs.lineHeight;
328 int Editor::LinesToScroll() {
329 int retVal = LinesOnScreen() - 1;
330 if (retVal < 1)
331 return 1;
332 else
333 return retVal;
336 int Editor::MaxScrollPos() {
337 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
338 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
339 int retVal = cs.LinesDisplayed();
340 if (endAtLastLine) {
341 retVal -= LinesOnScreen();
342 } else {
343 retVal--;
345 if (retVal < 0) {
346 return 0;
347 } else {
348 return retVal;
352 const char *ControlCharacterString(unsigned char ch) {
353 const char *reps[] = {
354 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
355 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
356 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
357 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
359 if (ch < (sizeof(reps) / sizeof(reps[0]))) {
360 return reps[ch];
361 } else {
362 return "BAD";
367 * Convenience class to ensure LineLayout objects are always disposed.
369 class AutoLineLayout {
370 LineLayoutCache &llc;
371 LineLayout *ll;
372 AutoLineLayout &operator=(const AutoLineLayout &);
373 public:
374 AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
375 ~AutoLineLayout() {
376 llc.Dispose(ll);
377 ll = 0;
379 LineLayout *operator->() const {
380 return ll;
382 operator LineLayout *() const {
383 return ll;
385 void Set(LineLayout *ll_) {
386 llc.Dispose(ll);
387 ll = ll_;
391 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
392 if (sp.Position() < 0) {
393 return SelectionPosition(0);
394 } else if (sp.Position() > pdoc->Length()) {
395 return SelectionPosition(pdoc->Length());
396 } else {
397 // If not at end of line then set offset to 0
398 if (!pdoc->IsLineEndPosition(sp.Position()))
399 sp.SetVirtualSpace(0);
400 return sp;
404 Point Editor::LocationFromPosition(SelectionPosition pos) {
405 Point pt;
406 RefreshStyleData();
407 if (pos.Position() == INVALID_POSITION)
408 return pt;
409 int line = pdoc->LineFromPosition(pos.Position());
410 int lineVisible = cs.DisplayFromDoc(line);
411 //Platform::DebugPrintf("line=%d\n", line);
412 AutoSurface surface(this);
413 AutoLineLayout ll(llc, RetrieveLineLayout(line));
414 if (surface && ll) {
415 // -1 because of adding in for visible lines in following loop.
416 pt.y = (lineVisible - topLine - 1) * vs.lineHeight;
417 pt.x = 0;
418 unsigned int posLineStart = pdoc->LineStart(line);
419 LayoutLine(line, surface, vs, ll, wrapWidth);
420 int posInLine = pos.Position() - posLineStart;
421 // In case of very long line put x at arbitrary large position
422 if (posInLine > ll->maxLineLength) {
423 pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->LineStart(ll->lines)];
426 for (int subLine = 0; subLine < ll->lines; subLine++) {
427 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
428 pt.x = ll->positions[posInLine] - ll->positions[ll->LineStart(subLine)];
429 if (ll->wrapIndent != 0) {
430 int lineStart = ll->LineStart(subLine);
431 if (lineStart != 0) // Wrapped
432 pt.x += ll->wrapIndent;
435 if (posInLine >= ll->LineStart(subLine)) {
436 pt.y += vs.lineHeight;
439 pt.x += vs.fixedColumnWidth - xOffset;
441 pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
442 return pt;
445 Point Editor::LocationFromPosition(int pos) {
446 return LocationFromPosition(SelectionPosition(pos));
449 int Editor::XFromPosition(int pos) {
450 Point pt = LocationFromPosition(pos);
451 return pt.x - vs.fixedColumnWidth + xOffset;
454 int Editor::XFromPosition(SelectionPosition sp) {
455 Point pt = LocationFromPosition(sp);
456 return pt.x - vs.fixedColumnWidth + xOffset;
459 int Editor::LineFromLocation(Point pt) {
460 return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
463 void Editor::SetTopLine(int topLineNew) {
464 if (topLine != topLineNew) {
465 topLine = topLineNew;
466 ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
468 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
471 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
472 RefreshStyleData();
473 if (canReturnInvalid) {
474 PRectangle rcClient = GetTextRectangle();
475 if (!rcClient.Contains(pt))
476 return SelectionPosition(INVALID_POSITION);
477 if (pt.x < vs.fixedColumnWidth)
478 return SelectionPosition(INVALID_POSITION);
479 if (pt.y < 0)
480 return SelectionPosition(INVALID_POSITION);
482 pt.x = pt.x - vs.fixedColumnWidth + xOffset;
483 int visibleLine = pt.y / vs.lineHeight + topLine;
484 if (pt.y < 0) { // Division rounds towards 0
485 visibleLine = (static_cast<int>(pt.y) - (vs.lineHeight - 1)) / vs.lineHeight + topLine;
487 if (!canReturnInvalid && (visibleLine < 0))
488 visibleLine = 0;
489 int lineDoc = cs.DocFromDisplay(visibleLine);
490 if (canReturnInvalid && (lineDoc < 0))
491 return SelectionPosition(INVALID_POSITION);
492 if (lineDoc >= pdoc->LinesTotal())
493 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length());
494 unsigned int posLineStart = pdoc->LineStart(lineDoc);
495 SelectionPosition retVal(canReturnInvalid ? INVALID_POSITION : static_cast<int>(posLineStart));
496 AutoSurface surface(this);
497 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
498 if (surface && ll) {
499 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
500 int lineStartSet = cs.DisplayFromDoc(lineDoc);
501 int subLine = visibleLine - lineStartSet;
502 if (subLine < ll->lines) {
503 int lineStart = ll->LineStart(subLine);
504 int lineEnd = ll->LineLastVisible(subLine);
505 XYPOSITION subLineStart = ll->positions[lineStart];
507 if (ll->wrapIndent != 0) {
508 if (lineStart != 0) // Wrapped
509 pt.x -= ll->wrapIndent;
511 int i = ll->FindBefore(pt.x + subLineStart, lineStart, lineEnd);
512 while (i < lineEnd) {
513 if (charPosition) {
514 if ((pt.x + subLineStart) < (ll->positions[i + 1])) {
515 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
517 } else {
518 if ((pt.x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
519 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
522 i++;
524 if (virtualSpace) {
525 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
526 int spaceOffset = (pt.x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) /
527 spaceWidth;
528 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
529 } else if (canReturnInvalid) {
530 if (pt.x < (ll->positions[lineEnd] - subLineStart)) {
531 return SelectionPosition(pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1));
533 } else {
534 return SelectionPosition(lineEnd + posLineStart);
537 if (!canReturnInvalid)
538 return SelectionPosition(ll->numCharsInLine + posLineStart);
540 return retVal;
543 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
544 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
548 * Find the document position corresponding to an x coordinate on a particular document line.
549 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
551 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
552 RefreshStyleData();
553 if (lineDoc >= pdoc->LinesTotal())
554 return SelectionPosition(pdoc->Length());
555 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
556 AutoSurface surface(this);
557 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
558 int retVal = 0;
559 if (surface && ll) {
560 unsigned int posLineStart = pdoc->LineStart(lineDoc);
561 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
562 int subLine = 0;
563 int lineStart = ll->LineStart(subLine);
564 int lineEnd = ll->LineLastVisible(subLine);
565 XYPOSITION subLineStart = ll->positions[lineStart];
566 XYPOSITION newX = x;
568 if (ll->wrapIndent != 0) {
569 if (lineStart != 0) // Wrapped
570 newX -= ll->wrapIndent;
572 int i = ll->FindBefore(newX + subLineStart, lineStart, lineEnd);
573 while (i < lineEnd) {
574 if ((newX + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
575 retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
576 return SelectionPosition(retVal);
578 i++;
580 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
581 int spaceOffset = (newX + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) / spaceWidth;
582 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
584 return SelectionPosition(retVal);
587 int Editor::PositionFromLineX(int lineDoc, int x) {
588 return SPositionFromLineX(lineDoc, x).Position();
592 * If painting then abandon the painting because a wider redraw is needed.
593 * @return true if calling code should stop drawing.
595 bool Editor::AbandonPaint() {
596 if ((paintState == painting) && !paintingAllText) {
597 paintState = paintAbandoned;
599 return paintState == paintAbandoned;
602 void Editor::RedrawRect(PRectangle rc) {
603 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
605 // Clip the redraw rectangle into the client area
606 PRectangle rcClient = GetClientRectangle();
607 if (rc.top < rcClient.top)
608 rc.top = rcClient.top;
609 if (rc.bottom > rcClient.bottom)
610 rc.bottom = rcClient.bottom;
611 if (rc.left < rcClient.left)
612 rc.left = rcClient.left;
613 if (rc.right > rcClient.right)
614 rc.right = rcClient.right;
616 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
617 wMain.InvalidateRectangle(rc);
621 void Editor::Redraw() {
622 //Platform::DebugPrintf("Redraw all\n");
623 PRectangle rcClient = GetClientRectangle();
624 wMain.InvalidateRectangle(rcClient);
625 //wMain.InvalidateAll();
628 void Editor::RedrawSelMargin(int line, bool allAfter) {
629 if (!AbandonPaint()) {
630 if (vs.maskInLine) {
631 Redraw();
632 } else {
633 PRectangle rcSelMargin = GetClientRectangle();
634 rcSelMargin.right = vs.fixedColumnWidth;
635 if (line != -1) {
636 int position = pdoc->LineStart(line);
637 PRectangle rcLine = RectangleFromRange(position, position);
639 // Inflate line rectangle if there are image markers with height larger than line height
640 if (vs.largestMarkerHeight > vs.lineHeight) {
641 int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
642 rcLine.top -= delta;
643 rcLine.bottom += delta;
644 if (rcLine.top < rcSelMargin.top)
645 rcLine.top = rcSelMargin.top;
646 if (rcLine.bottom > rcSelMargin.bottom)
647 rcLine.bottom = rcSelMargin.bottom;
650 rcSelMargin.top = rcLine.top;
651 if (!allAfter)
652 rcSelMargin.bottom = rcLine.bottom;
654 wMain.InvalidateRectangle(rcSelMargin);
659 PRectangle Editor::RectangleFromRange(int start, int end) {
660 int minPos = start;
661 if (minPos > end)
662 minPos = end;
663 int maxPos = start;
664 if (maxPos < end)
665 maxPos = end;
666 int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos));
667 int lineDocMax = pdoc->LineFromPosition(maxPos);
668 int maxLine = cs.DisplayFromDoc(lineDocMax) + cs.GetHeight(lineDocMax) - 1;
669 PRectangle rcClient = GetTextRectangle();
670 PRectangle rc;
671 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
672 rc.left = vs.fixedColumnWidth - leftTextOverlap;
673 rc.top = (minLine - topLine) * vs.lineHeight;
674 if (rc.top < 0)
675 rc.top = 0;
676 rc.right = rcClient.right;
677 rc.bottom = (maxLine - topLine + 1) * vs.lineHeight;
678 // Ensure PRectangle is within 16 bit space
679 rc.top = Platform::Clamp(rc.top, -32000, 32000);
680 rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000);
682 return rc;
685 void Editor::InvalidateRange(int start, int end) {
686 RedrawRect(RectangleFromRange(start, end));
689 int Editor::CurrentPosition() {
690 return sel.MainCaret();
693 bool Editor::SelectionEmpty() {
694 return sel.Empty();
697 SelectionPosition Editor::SelectionStart() {
698 return sel.RangeMain().Start();
701 SelectionPosition Editor::SelectionEnd() {
702 return sel.RangeMain().End();
705 void Editor::SetRectangularRange() {
706 if (sel.IsRectangular()) {
707 int xAnchor = XFromPosition(sel.Rectangular().anchor);
708 int xCaret = XFromPosition(sel.Rectangular().caret);
709 if (sel.selType == Selection::selThin) {
710 xCaret = xAnchor;
712 int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
713 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
714 int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
715 for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
716 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
717 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
718 range.ClearVirtualSpace();
719 if (line == lineAnchorRect)
720 sel.SetSelection(range);
721 else
722 sel.AddSelectionWithoutTrim(range);
727 void Editor::ThinRectangularRange() {
728 if (sel.IsRectangular()) {
729 sel.selType = Selection::selThin;
730 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
731 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
732 } else {
733 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
735 SetRectangularRange();
739 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
740 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
741 invalidateWholeSelection = true;
743 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
744 // +1 for lastAffected ensures caret repainted
745 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
746 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
747 if (invalidateWholeSelection) {
748 for (size_t r=0; r<sel.Count(); r++) {
749 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
750 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
751 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
752 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
755 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
756 InvalidateRange(firstAffected, lastAffected);
759 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
760 currentPos_ = ClampPositionIntoDocument(currentPos_);
761 anchor_ = ClampPositionIntoDocument(anchor_);
762 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
763 /* For Line selection - ensure the anchor and caret are always
764 at the beginning and end of the region lines. */
765 if (sel.selType == Selection::selLines) {
766 if (currentPos_ > anchor_) {
767 anchor_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
768 currentPos_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
769 } else {
770 currentPos_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
771 anchor_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
774 SelectionRange rangeNew(currentPos_, anchor_);
775 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
776 InvalidateSelection(rangeNew);
778 sel.RangeMain() = rangeNew;
779 SetRectangularRange();
780 ClaimSelection();
782 if (highlightDelimiter.NeedsDrawing(currentLine)) {
783 RedrawSelMargin();
787 void Editor::SetSelection(int currentPos_, int anchor_) {
788 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
791 // Just move the caret on the main selection
792 void Editor::SetSelection(SelectionPosition currentPos_) {
793 currentPos_ = ClampPositionIntoDocument(currentPos_);
794 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
795 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
796 InvalidateSelection(SelectionRange(currentPos_));
798 if (sel.IsRectangular()) {
799 sel.Rectangular() =
800 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
801 SetRectangularRange();
802 } else {
803 sel.RangeMain() =
804 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
806 ClaimSelection();
808 if (highlightDelimiter.NeedsDrawing(currentLine)) {
809 RedrawSelMargin();
813 void Editor::SetSelection(int currentPos_) {
814 SetSelection(SelectionPosition(currentPos_));
817 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
818 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
819 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
820 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
821 InvalidateSelection(rangeNew);
823 sel.Clear();
824 sel.RangeMain() = rangeNew;
825 SetRectangularRange();
826 ClaimSelection();
828 if (highlightDelimiter.NeedsDrawing(currentLine)) {
829 RedrawSelMargin();
833 void Editor::SetEmptySelection(int currentPos_) {
834 SetEmptySelection(SelectionPosition(currentPos_));
837 bool Editor::RangeContainsProtected(int start, int end) const {
838 if (vs.ProtectionActive()) {
839 if (start > end) {
840 int t = start;
841 start = end;
842 end = t;
844 int mask = pdoc->stylingBitsMask;
845 for (int pos = start; pos < end; pos++) {
846 if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())
847 return true;
850 return false;
853 bool Editor::SelectionContainsProtected() {
854 for (size_t r=0; r<sel.Count(); r++) {
855 if (RangeContainsProtected(sel.Range(r).Start().Position(),
856 sel.Range(r).End().Position())) {
857 return true;
860 return false;
864 * Asks document to find a good position and then moves out of any invisible positions.
866 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
867 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
870 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
871 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
872 if (posMoved != pos.Position())
873 pos.SetPosition(posMoved);
874 if (vs.ProtectionActive()) {
875 int mask = pdoc->stylingBitsMask;
876 if (moveDir > 0) {
877 if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()) {
878 while ((pos.Position() < pdoc->Length()) &&
879 (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()))
880 pos.Add(1);
882 } else if (moveDir < 0) {
883 if (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()) {
884 while ((pos.Position() > 0) &&
885 (vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()))
886 pos.Add(-1);
890 return pos;
893 int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
894 bool simpleCaret = (sel.Count() == 1) && sel.Empty();
895 SelectionPosition spCaret = sel.Last();
897 int delta = newPos.Position() - sel.MainCaret();
898 newPos = ClampPositionIntoDocument(newPos);
899 newPos = MovePositionOutsideChar(newPos, delta);
900 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
901 // Can't turn into multiple selection so clear additional selections
902 InvalidateSelection(SelectionRange(newPos), true);
903 SelectionRange rangeMain = sel.RangeMain();
904 sel.SetSelection(rangeMain);
906 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
907 // Switching to rectangular
908 SelectionRange rangeMain = sel.RangeMain();
909 sel.Clear();
910 sel.Rectangular() = rangeMain;
912 if (selt != Selection::noSel) {
913 sel.selType = selt;
915 if (selt != Selection::noSel || sel.MoveExtends()) {
916 SetSelection(newPos);
917 } else {
918 SetEmptySelection(newPos);
920 ShowCaretAtCurrentPosition();
922 int currentLine = pdoc->LineFromPosition(newPos.Position());
923 if (ensureVisible) {
924 // In case in need of wrapping to ensure DisplayFromDoc works.
925 if (currentLine >= wrapStart)
926 WrapLines(true, -1);
927 XYScrollPosition newXY = XYScrollToMakeVisible(true, true, true);
928 if (simpleCaret && (newXY.xOffset == xOffset)) {
929 // simple vertical scroll then invalidate
930 ScrollTo(newXY.topLine);
931 InvalidateSelection(SelectionRange(spCaret), true);
932 } else {
933 SetXYScroll(newXY);
937 if (highlightDelimiter.NeedsDrawing(currentLine)) {
938 RedrawSelMargin();
940 return 0;
943 int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
944 return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
947 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
948 pos = ClampPositionIntoDocument(pos);
949 pos = MovePositionOutsideChar(pos, moveDir);
950 int lineDoc = pdoc->LineFromPosition(pos.Position());
951 if (cs.GetVisible(lineDoc)) {
952 return pos;
953 } else {
954 int lineDisplay = cs.DisplayFromDoc(lineDoc);
955 if (moveDir > 0) {
956 // lineDisplay is already line before fold as lines in fold use display line of line after fold
957 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
958 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
959 } else {
960 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
961 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
966 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
967 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
970 Point Editor::PointMainCaret() {
971 return LocationFromPosition(sel.Range(sel.Main()).caret);
975 * Choose the x position that the caret will try to stick to
976 * as it moves up and down.
978 void Editor::SetLastXChosen() {
979 Point pt = PointMainCaret();
980 lastXChosen = pt.x + xOffset;
983 void Editor::ScrollTo(int line, bool moveThumb) {
984 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
985 if (topLineNew != topLine) {
986 // Try to optimise small scrolls
987 #ifndef UNDER_CE
988 int linesToMove = topLine - topLineNew;
989 bool performBlit = (abs(linesToMove) <= 10) && (paintState == notPainting);
990 willRedrawAll = !performBlit;
991 #endif
992 SetTopLine(topLineNew);
993 // Optimize by styling the view as this will invalidate any needed area
994 // which could abort the initial paint if discovered later.
995 StyleToPositionInView(PositionAfterArea(GetClientRectangle()));
996 #ifndef UNDER_CE
997 // Perform redraw rather than scroll if many lines would be redrawn anyway.
998 if (performBlit) {
999 ScrollText(linesToMove);
1000 } else {
1001 Redraw();
1003 willRedrawAll = false;
1004 #else
1005 Redraw();
1006 #endif
1007 if (moveThumb) {
1008 SetVerticalScrollPos();
1013 void Editor::ScrollText(int /* linesToMove */) {
1014 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
1015 Redraw();
1018 void Editor::HorizontalScrollTo(int xPos) {
1019 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
1020 if (xPos < 0)
1021 xPos = 0;
1022 if ((wrapState == eWrapNone) && (xOffset != xPos)) {
1023 xOffset = xPos;
1024 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1025 SetHorizontalScrollPos();
1026 RedrawRect(GetClientRectangle());
1030 void Editor::VerticalCentreCaret() {
1031 int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
1032 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1033 int newTop = lineDisplay - (LinesOnScreen() / 2);
1034 if (topLine != newTop) {
1035 SetTopLine(newTop > 0 ? newTop : 0);
1036 RedrawRect(GetClientRectangle());
1040 // Avoid 64 bit compiler warnings.
1041 // Scintilla does not support text buffers larger than 2**31
1042 static int istrlen(const char *s) {
1043 return static_cast<int>(strlen(s));
1046 void Editor::MoveSelectedLines(int lineDelta) {
1048 // if selection doesn't start at the beginning of the line, set the new start
1049 int selectionStart = SelectionStart().Position();
1050 int startLine = pdoc->LineFromPosition(selectionStart);
1051 int beginningOfStartLine = pdoc->LineStart(startLine);
1052 selectionStart = beginningOfStartLine;
1054 // if selection doesn't end at the beginning of a line greater than that of the start,
1055 // then set it at the beginning of the next one
1056 int selectionEnd = SelectionEnd().Position();
1057 int endLine = pdoc->LineFromPosition(selectionEnd);
1058 int beginningOfEndLine = pdoc->LineStart(endLine);
1059 bool appendEol = false;
1060 if (selectionEnd > beginningOfEndLine
1061 || selectionStart == selectionEnd) {
1062 selectionEnd = pdoc->LineStart(endLine + 1);
1063 appendEol = (selectionEnd == pdoc->Length() && pdoc->LineFromPosition(selectionEnd) == endLine);
1066 // if there's nowhere for the selection to move
1067 // (i.e. at the beginning going up or at the end going down),
1068 // stop it right there!
1069 if ((selectionStart == 0 && lineDelta < 0)
1070 || (selectionEnd == pdoc->Length() && lineDelta > 0)
1071 || selectionStart == selectionEnd) {
1072 return;
1075 UndoGroup ug(pdoc);
1077 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
1078 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
1079 ClearSelection();
1080 selectionEnd = CurrentPosition();
1082 SetSelection(selectionStart, selectionEnd);
1084 SelectionText selectedText;
1085 CopySelectionRange(&selectedText);
1087 int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
1088 Point currentLocation = LocationFromPosition(CurrentPosition());
1089 int currentLine = LineFromLocation(currentLocation);
1091 if (appendEol)
1092 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
1093 ClearSelection();
1095 const char *eol = StringFromEOLMode(pdoc->eolMode);
1096 if (currentLine + lineDelta >= pdoc->LinesTotal())
1097 pdoc->InsertCString(pdoc->Length(), eol);
1098 GoToLine(currentLine + lineDelta);
1100 pdoc->InsertCString(CurrentPosition(), selectedText.s);
1101 if (appendEol) {
1102 pdoc->InsertCString(CurrentPosition() + selectionLength, eol);
1103 selectionLength += istrlen(eol);
1105 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1108 void Editor::MoveSelectedLinesUp() {
1109 MoveSelectedLines(-1);
1112 void Editor::MoveSelectedLinesDown() {
1113 MoveSelectedLines(1);
1116 void Editor::MoveCaretInsideView(bool ensureVisible) {
1117 PRectangle rcClient = GetTextRectangle();
1118 Point pt = PointMainCaret();
1119 if (pt.y < rcClient.top) {
1120 MovePositionTo(SPositionFromLocation(
1121 Point(lastXChosen - xOffset, rcClient.top),
1122 false, false, UserVirtualSpace()),
1123 Selection::noSel, ensureVisible);
1124 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1125 int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
1126 MovePositionTo(SPositionFromLocation(
1127 Point(lastXChosen - xOffset, rcClient.top + yOfLastLineFullyDisplayed),
1128 false, false, UserVirtualSpace()),
1129 Selection::noSel, ensureVisible);
1133 int Editor::DisplayFromPosition(int pos) {
1134 int lineDoc = pdoc->LineFromPosition(pos);
1135 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1136 AutoSurface surface(this);
1137 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
1138 if (surface && ll) {
1139 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
1140 unsigned int posLineStart = pdoc->LineStart(lineDoc);
1141 int posInLine = pos - posLineStart;
1142 lineDisplay--; // To make up for first increment ahead.
1143 for (int subLine = 0; subLine < ll->lines; subLine++) {
1144 if (posInLine >= ll->LineStart(subLine)) {
1145 lineDisplay++;
1149 return lineDisplay;
1153 * Ensure the caret is reasonably visible in context.
1155 Caret policy in SciTE
1157 If slop is set, we can define a slop value.
1158 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1159 This zone is defined as a number of pixels near the vertical margins,
1160 and as a number of lines near the horizontal margins.
1161 By keeping the caret away from the edges, it is seen within its context,
1162 so it is likely that the identifier that the caret is on can be completely seen,
1163 and that the current line is seen with some of the lines following it which are
1164 often dependent on that line.
1166 If strict is set, the policy is enforced... strictly.
1167 The caret is centred on the display if slop is not set,
1168 and cannot go in the UZ if slop is set.
1170 If jumps is set, the display is moved more energetically
1171 so the caret can move in the same direction longer before the policy is applied again.
1172 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1174 If even is not set, instead of having symmetrical UZs,
1175 the left and bottom UZs are extended up to right and top UZs respectively.
1176 This way, we favour the displaying of useful information: the begining of lines,
1177 where most code reside, and the lines after the caret, eg. the body of a function.
1179 | | | | |
1180 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1181 | | | | | visibility or going into the UZ) display is...
1182 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1183 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1184 0 | 0 | 0 | 1 | Yes | moved by one position
1185 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1186 0 | 0 | 1 | 1 | Yes | centred on the caret
1187 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1188 0 | 1 | - | 1 | No, caret is always centred | -
1189 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1190 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1191 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1192 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1193 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1194 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1195 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1198 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const bool useMargin, const bool vert, const bool horiz) {
1199 PRectangle rcClient = GetTextRectangle();
1200 const SelectionPosition posCaret = posDrag.IsValid() ? posDrag : sel.RangeMain().caret;
1201 const Point pt = LocationFromPosition(posCaret);
1202 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1203 const int lineCaret = DisplayFromPosition(posCaret.Position());
1205 XYScrollPosition newXY(xOffset, topLine);
1207 // Vertical positioning
1208 if (vert && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1209 const int linesOnScreen = LinesOnScreen();
1210 const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1211 const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
1212 const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
1213 const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
1214 const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
1216 // It should be possible to scroll the window to show the caret,
1217 // but this fails to remove the caret on GTK+
1218 if (bSlop) { // A margin is defined
1219 int yMoveT, yMoveB;
1220 if (bStrict) {
1221 int yMarginT, yMarginB;
1222 if (!useMargin) {
1223 // In drag mode, avoid moves
1224 // otherwise, a double click will select several lines.
1225 yMarginT = yMarginB = 0;
1226 } else {
1227 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1228 // a maximum of slightly less than half the heigth of the text area.
1229 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1230 if (bEven) {
1231 yMarginB = yMarginT;
1232 } else {
1233 yMarginB = linesOnScreen - yMarginT - 1;
1236 yMoveT = yMarginT;
1237 if (bEven) {
1238 if (bJump) {
1239 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1241 yMoveB = yMoveT;
1242 } else {
1243 yMoveB = linesOnScreen - yMoveT - 1;
1245 if (lineCaret < topLine + yMarginT) {
1246 // Caret goes too high
1247 newXY.topLine = lineCaret - yMoveT;
1248 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1249 // Caret goes too low
1250 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1252 } else { // Not strict
1253 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1254 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1255 if (bEven) {
1256 yMoveB = yMoveT;
1257 } else {
1258 yMoveB = linesOnScreen - yMoveT - 1;
1260 if (lineCaret < topLine) {
1261 // Caret goes too high
1262 newXY.topLine = lineCaret - yMoveT;
1263 } else if (lineCaret > topLine + linesOnScreen - 1) {
1264 // Caret goes too low
1265 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1268 } else { // No slop
1269 if (!bStrict && !bJump) {
1270 // Minimal move
1271 if (lineCaret < topLine) {
1272 // Caret goes too high
1273 newXY.topLine = lineCaret;
1274 } else if (lineCaret > topLine + linesOnScreen - 1) {
1275 // Caret goes too low
1276 if (bEven) {
1277 newXY.topLine = lineCaret - linesOnScreen + 1;
1278 } else {
1279 newXY.topLine = lineCaret;
1282 } else { // Strict or going out of display
1283 if (bEven) {
1284 // Always center caret
1285 newXY.topLine = lineCaret - halfScreen;
1286 } else {
1287 // Always put caret on top of display
1288 newXY.topLine = lineCaret;
1292 newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
1295 // Horizontal positioning
1296 if (horiz && (wrapState == eWrapNone)) {
1297 const int halfScreen = Platform::Maximum(rcClient.Width() - 4, 4) / 2;
1298 const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
1299 const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
1300 const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
1301 const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
1303 if (bSlop) { // A margin is defined
1304 int xMoveL, xMoveR;
1305 if (bStrict) {
1306 int xMarginL, xMarginR;
1307 if (!useMargin) {
1308 // In drag mode, avoid moves unless very near of the margin
1309 // otherwise, a simple click will select text.
1310 xMarginL = xMarginR = 2;
1311 } else {
1312 // xMargin must equal to caretXSlop, with a minimum of 2 and
1313 // a maximum of slightly less than half the width of the text area.
1314 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1315 if (bEven) {
1316 xMarginL = xMarginR;
1317 } else {
1318 xMarginL = rcClient.Width() - xMarginR - 4;
1321 if (bJump && bEven) {
1322 // Jump is used only in even mode
1323 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1324 } else {
1325 xMoveL = xMoveR = 0; // Not used, avoid a warning
1327 if (pt.x < rcClient.left + xMarginL) {
1328 // Caret is on the left of the display
1329 if (bJump && bEven) {
1330 newXY.xOffset -= xMoveL;
1331 } else {
1332 // Move just enough to allow to display the caret
1333 newXY.xOffset -= (rcClient.left + xMarginL) - pt.x;
1335 } else if (pt.x >= rcClient.right - xMarginR) {
1336 // Caret is on the right of the display
1337 if (bJump && bEven) {
1338 newXY.xOffset += xMoveR;
1339 } else {
1340 // Move just enough to allow to display the caret
1341 newXY.xOffset += pt.x - (rcClient.right - xMarginR) + 1;
1344 } else { // Not strict
1345 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1346 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1347 if (bEven) {
1348 xMoveL = xMoveR;
1349 } else {
1350 xMoveL = rcClient.Width() - xMoveR - 4;
1352 if (pt.x < rcClient.left) {
1353 // Caret is on the left of the display
1354 newXY.xOffset -= xMoveL;
1355 } else if (pt.x >= rcClient.right) {
1356 // Caret is on the right of the display
1357 newXY.xOffset += xMoveR;
1360 } else { // No slop
1361 if (bStrict ||
1362 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1363 // Strict or going out of display
1364 if (bEven) {
1365 // Center caret
1366 newXY.xOffset += pt.x - rcClient.left - halfScreen;
1367 } else {
1368 // Put caret on right
1369 newXY.xOffset += pt.x - rcClient.right + 1;
1371 } else {
1372 // Move just enough to allow to display the caret
1373 if (pt.x < rcClient.left) {
1374 // Caret is on the left of the display
1375 if (bEven) {
1376 newXY.xOffset -= rcClient.left - pt.x;
1377 } else {
1378 newXY.xOffset += pt.x - rcClient.right + 1;
1380 } else if (pt.x >= rcClient.right) {
1381 // Caret is on the right of the display
1382 newXY.xOffset += pt.x - rcClient.right + 1;
1386 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1387 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1388 newXY.xOffset = pt.x + xOffset - rcClient.left;
1389 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1390 newXY.xOffset = pt.x + xOffset - rcClient.right + 1;
1391 if (vs.caretStyle == CARETSTYLE_BLOCK) {
1392 // Ensure we can see a good portion of the block caret
1393 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1396 if (newXY.xOffset < 0) {
1397 newXY.xOffset = 0;
1401 return newXY;
1404 void Editor::SetXYScroll(XYScrollPosition newXY) {
1405 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1406 if (newXY.topLine != topLine) {
1407 SetTopLine(newXY.topLine);
1408 SetVerticalScrollPos();
1410 if (newXY.xOffset != xOffset) {
1411 xOffset = newXY.xOffset;
1412 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1413 if (newXY.xOffset > 0) {
1414 PRectangle rcText = GetTextRectangle();
1415 if (horizontalScrollBarVisible &&
1416 rcText.Width() + xOffset > scrollWidth) {
1417 scrollWidth = xOffset + rcText.Width();
1418 SetScrollBars();
1421 SetHorizontalScrollPos();
1423 Redraw();
1424 UpdateSystemCaret();
1428 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1429 SetXYScroll(XYScrollToMakeVisible(useMargin, vert, horiz));
1432 void Editor::ShowCaretAtCurrentPosition() {
1433 if (hasFocus) {
1434 caret.active = true;
1435 caret.on = true;
1436 SetTicking(true);
1437 } else {
1438 caret.active = false;
1439 caret.on = false;
1441 InvalidateCaret();
1444 void Editor::DropCaret() {
1445 caret.active = false;
1446 InvalidateCaret();
1449 void Editor::InvalidateCaret() {
1450 if (posDrag.IsValid()) {
1451 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1452 } else {
1453 for (size_t r=0; r<sel.Count(); r++) {
1454 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1457 UpdateSystemCaret();
1460 void Editor::UpdateSystemCaret() {
1463 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1464 docLineStart = Platform::Clamp(docLineStart, 0, pdoc->LinesTotal());
1465 if (wrapStart > docLineStart) {
1466 wrapStart = docLineStart;
1467 llc.Invalidate(LineLayout::llPositions);
1469 if (wrapEnd < docLineEnd) {
1470 wrapEnd = docLineEnd;
1472 wrapEnd = Platform::Clamp(wrapEnd, 0, pdoc->LinesTotal());
1473 // Wrap lines during idle.
1474 if ((wrapState != eWrapNone) && (wrapEnd != wrapStart)) {
1475 SetIdle(true);
1479 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1480 AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
1481 int linesWrapped = 1;
1482 if (ll) {
1483 LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
1484 linesWrapped = ll->lines;
1486 return cs.SetHeight(lineToWrap, linesWrapped +
1487 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1490 // Check if wrapping needed and perform any needed wrapping.
1491 // fullwrap: if true, all lines which need wrapping will be done,
1492 // in this single call.
1493 // priorityWrapLineStart: If greater than or equal to zero, all lines starting from
1494 // here to 1 page + 100 lines past will be wrapped (even if there are
1495 // more lines under wrapping process in idle).
1496 // If it is neither fullwrap, nor priorityWrap, then 1 page + 100 lines will be
1497 // wrapped, if there are any wrapping going on in idle. (Generally this
1498 // condition is called only from idler).
1499 // Return true if wrapping occurred.
1500 bool Editor::WrapLines(bool fullWrap, int priorityWrapLineStart) {
1501 // If there are any pending wraps, do them during idle if possible.
1502 int linesInOneCall = LinesOnScreen() + 100;
1503 if (priorityWrapLineStart >= 0) {
1504 // Using DocFromDisplay() here may result in chicken and egg problem in certain corner cases,
1505 // which will hopefully be handled by added 100 lines. If some lines are still missed, idle wrapping will catch on.
1506 int docLinesInOneCall = cs.DocFromDisplay(topLine + LinesOnScreen() + 100) - cs.DocFromDisplay(topLine);
1507 linesInOneCall = Platform::Maximum(linesInOneCall, docLinesInOneCall);
1509 if (wrapState != eWrapNone) {
1510 if (wrapStart < wrapEnd) {
1511 if (!SetIdle(true)) {
1512 // Idle processing not supported so full wrap required.
1513 fullWrap = true;
1516 if (!fullWrap && priorityWrapLineStart >= 0 &&
1517 // .. and if the paint window is outside pending wraps
1518 (((priorityWrapLineStart + linesInOneCall) < wrapStart) ||
1519 (priorityWrapLineStart > wrapEnd))) {
1520 // No priority wrap pending
1521 return false;
1524 int goodTopLine = topLine;
1525 bool wrapOccurred = false;
1526 if (wrapStart <= pdoc->LinesTotal()) {
1527 if (wrapState == eWrapNone) {
1528 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1529 wrapWidth = LineLayout::wrapWidthInfinite;
1530 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1531 cs.SetHeight(lineDoc, 1 +
1532 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1534 wrapOccurred = true;
1536 wrapStart = wrapLineLarge;
1537 wrapEnd = wrapLineLarge;
1538 } else {
1539 if (wrapEnd >= pdoc->LinesTotal())
1540 wrapEnd = pdoc->LinesTotal();
1541 //ElapsedTime et;
1542 int lineDocTop = cs.DocFromDisplay(topLine);
1543 int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1544 PRectangle rcTextArea = GetClientRectangle();
1545 rcTextArea.left = vs.fixedColumnWidth;
1546 rcTextArea.right -= vs.rightMarginWidth;
1547 wrapWidth = rcTextArea.Width();
1548 RefreshStyleData();
1549 AutoSurface surface(this);
1550 if (surface) {
1551 bool priorityWrap = false;
1552 int lastLineToWrap = wrapEnd;
1553 int lineToWrap = wrapStart;
1554 if (!fullWrap) {
1555 if (priorityWrapLineStart >= 0) {
1556 // This is a priority wrap.
1557 lineToWrap = priorityWrapLineStart;
1558 lastLineToWrap = priorityWrapLineStart + linesInOneCall;
1559 priorityWrap = true;
1560 } else {
1561 // This is idle wrap.
1562 lastLineToWrap = wrapStart + linesInOneCall;
1564 if (lastLineToWrap >= wrapEnd)
1565 lastLineToWrap = wrapEnd;
1566 } // else do a fullWrap.
1568 // Ensure all lines being wrapped are styled.
1569 pdoc->EnsureStyledTo(pdoc->LineEnd(lastLineToWrap));
1571 // Platform::DebugPrintf("Wraplines: full = %d, priorityStart = %d (wrapping: %d to %d)\n", fullWrap, priorityWrapLineStart, lineToWrap, lastLineToWrap);
1572 // Platform::DebugPrintf("Pending wraps: %d to %d\n", wrapStart, wrapEnd);
1573 while (lineToWrap < lastLineToWrap) {
1574 if (WrapOneLine(surface, lineToWrap)) {
1575 wrapOccurred = true;
1577 lineToWrap++;
1579 if (!priorityWrap)
1580 wrapStart = lineToWrap;
1581 // If wrapping is done, bring it to resting position
1582 if (wrapStart >= wrapEnd) {
1583 wrapStart = wrapLineLarge;
1584 wrapEnd = wrapLineLarge;
1587 goodTopLine = cs.DisplayFromDoc(lineDocTop);
1588 if (subLineTop < cs.GetHeight(lineDocTop))
1589 goodTopLine += subLineTop;
1590 else
1591 goodTopLine += cs.GetHeight(lineDocTop);
1592 //double durWrap = et.Duration(true);
1593 //Platform::DebugPrintf("Wrap:%9.6g \n", durWrap);
1596 if (wrapOccurred) {
1597 SetScrollBars();
1598 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1599 SetVerticalScrollPos();
1601 return wrapOccurred;
1604 void Editor::LinesJoin() {
1605 if (!RangeContainsProtected(targetStart, targetEnd)) {
1606 UndoGroup ug(pdoc);
1607 bool prevNonWS = true;
1608 for (int pos = targetStart; pos < targetEnd; pos++) {
1609 if (IsEOLChar(pdoc->CharAt(pos))) {
1610 targetEnd -= pdoc->LenChar(pos);
1611 pdoc->DelChar(pos);
1612 if (prevNonWS) {
1613 // Ensure at least one space separating previous lines
1614 pdoc->InsertChar(pos, ' ');
1615 targetEnd++;
1617 } else {
1618 prevNonWS = pdoc->CharAt(pos) != ' ';
1624 const char *Editor::StringFromEOLMode(int eolMode) {
1625 if (eolMode == SC_EOL_CRLF) {
1626 return "\r\n";
1627 } else if (eolMode == SC_EOL_CR) {
1628 return "\r";
1629 } else {
1630 return "\n";
1634 void Editor::LinesSplit(int pixelWidth) {
1635 if (!RangeContainsProtected(targetStart, targetEnd)) {
1636 if (pixelWidth == 0) {
1637 PRectangle rcText = GetTextRectangle();
1638 pixelWidth = rcText.Width();
1640 int lineStart = pdoc->LineFromPosition(targetStart);
1641 int lineEnd = pdoc->LineFromPosition(targetEnd);
1642 const char *eol = StringFromEOLMode(pdoc->eolMode);
1643 UndoGroup ug(pdoc);
1644 for (int line = lineStart; line <= lineEnd; line++) {
1645 AutoSurface surface(this);
1646 AutoLineLayout ll(llc, RetrieveLineLayout(line));
1647 if (surface && ll) {
1648 unsigned int posLineStart = pdoc->LineStart(line);
1649 LayoutLine(line, surface, vs, ll, pixelWidth);
1650 for (int subLine = 1; subLine < ll->lines; subLine++) {
1651 pdoc->InsertCString(
1652 static_cast<int>(posLineStart + (subLine - 1) * strlen(eol) +
1653 ll->LineStart(subLine)),
1654 eol);
1655 targetEnd += static_cast<int>(strlen(eol));
1658 lineEnd = pdoc->LineFromPosition(targetEnd);
1663 int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) {
1664 if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
1665 return markerDefault;
1666 return markerCheck;
1669 bool ValidStyledText(ViewStyle &vs, size_t styleOffset, const StyledText &st) {
1670 if (st.multipleStyles) {
1671 for (size_t iStyle=0; iStyle<st.length; iStyle++) {
1672 if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
1673 return false;
1675 } else {
1676 if (!vs.ValidStyle(styleOffset + st.style))
1677 return false;
1679 return true;
1682 static int WidthStyledText(Surface *surface, ViewStyle &vs, int styleOffset,
1683 const char *text, const unsigned char *styles, size_t len) {
1684 int width = 0;
1685 size_t start = 0;
1686 while (start < len) {
1687 size_t style = styles[start];
1688 size_t endSegment = start;
1689 while ((endSegment+1 < len) && (static_cast<size_t>(styles[endSegment+1]) == style))
1690 endSegment++;
1691 width += surface->WidthText(vs.styles[style+styleOffset].font, text + start,
1692 static_cast<int>(endSegment - start + 1));
1693 start = endSegment + 1;
1695 return width;
1698 static int WidestLineWidth(Surface *surface, ViewStyle &vs, int styleOffset, const StyledText &st) {
1699 int widthMax = 0;
1700 size_t start = 0;
1701 while (start < st.length) {
1702 size_t lenLine = st.LineLength(start);
1703 int widthSubLine;
1704 if (st.multipleStyles) {
1705 widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
1706 } else {
1707 widthSubLine = surface->WidthText(vs.styles[styleOffset + st.style].font,
1708 st.text + start, static_cast<int>(lenLine));
1710 if (widthSubLine > widthMax)
1711 widthMax = widthSubLine;
1712 start += lenLine + 1;
1714 return widthMax;
1717 void DrawStyledText(Surface *surface, ViewStyle &vs, int styleOffset, PRectangle rcText, int ascent,
1718 const StyledText &st, size_t start, size_t length) {
1720 if (st.multipleStyles) {
1721 int x = rcText.left;
1722 size_t i = 0;
1723 while (i < length) {
1724 size_t end = i;
1725 int style = st.styles[i + start];
1726 while (end < length-1 && st.styles[start+end+1] == style)
1727 end++;
1728 style += styleOffset;
1729 int width = surface->WidthText(vs.styles[style].font,
1730 st.text + start + i, static_cast<int>(end - i + 1));
1731 PRectangle rcSegment = rcText;
1732 rcSegment.left = x;
1733 rcSegment.right = x + width + 1;
1734 surface->DrawTextNoClip(rcSegment, vs.styles[style].font,
1735 ascent, st.text + start + i,
1736 static_cast<int>(end - i + 1),
1737 vs.styles[style].fore,
1738 vs.styles[style].back);
1739 x += width;
1740 i = end + 1;
1742 } else {
1743 size_t style = st.style + styleOffset;
1744 surface->DrawTextNoClip(rcText, vs.styles[style].font,
1745 rcText.top + vs.maxAscent, st.text + start,
1746 static_cast<int>(length),
1747 vs.styles[style].fore,
1748 vs.styles[style].back);
1752 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1753 if (vs.fixedColumnWidth == 0)
1754 return;
1756 PRectangle rcMargin = GetClientRectangle();
1757 rcMargin.right = vs.fixedColumnWidth;
1759 if (!rc.Intersects(rcMargin))
1760 return;
1762 Surface *surface;
1763 if (bufferedDraw) {
1764 surface = pixmapSelMargin;
1765 } else {
1766 surface = surfWindow;
1769 // Clip vertically to paint area to avoid drawing line numbers
1770 if (rcMargin.bottom > rc.bottom)
1771 rcMargin.bottom = rc.bottom;
1772 if (rcMargin.top < rc.top)
1773 rcMargin.top = rc.top;
1775 PRectangle rcSelMargin = rcMargin;
1776 rcSelMargin.right = rcMargin.left;
1778 for (int margin = 0; margin < vs.margins; margin++) {
1779 if (vs.ms[margin].width > 0) {
1781 rcSelMargin.left = rcSelMargin.right;
1782 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
1784 if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
1785 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
1786 // Required because of special way brush is created for selection margin
1787 surface->FillRectangle(rcSelMargin, *pixmapSelPattern);
1788 else {
1789 ColourDesired colour;
1790 switch (vs.ms[margin].style) {
1791 case SC_MARGIN_BACK:
1792 colour = vs.styles[STYLE_DEFAULT].back;
1793 break;
1794 case SC_MARGIN_FORE:
1795 colour = vs.styles[STYLE_DEFAULT].fore;
1796 break;
1797 default:
1798 colour = vs.styles[STYLE_LINENUMBER].back;
1799 break;
1801 surface->FillRectangle(rcSelMargin, colour);
1803 } else {
1804 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back);
1807 const int lineStartPaint = rcMargin.top / vs.lineHeight;
1808 int visibleLine = topLine + lineStartPaint;
1809 int yposScreen = lineStartPaint * vs.lineHeight;
1810 // Work out whether the top line is whitespace located after a
1811 // lessening of fold level which implies a 'fold tail' but which should not
1812 // be displayed until the last of a sequence of whitespace.
1813 bool needWhiteClosure = false;
1814 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1815 int level = pdoc->GetLevel(cs.DocFromDisplay(visibleLine));
1816 if (level & SC_FOLDLEVELWHITEFLAG) {
1817 int lineBack = cs.DocFromDisplay(visibleLine);
1818 int levelPrev = level;
1819 while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
1820 lineBack--;
1821 levelPrev = pdoc->GetLevel(lineBack);
1823 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
1824 if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK))
1825 needWhiteClosure = true;
1828 if (highlightDelimiter.isEnabled) {
1829 int lastLine = cs.DocFromDisplay(topLine + LinesOnScreen()) + 1;
1830 pdoc->GetHighlightDelimiters(highlightDelimiter, pdoc->LineFromPosition(CurrentPosition()), lastLine);
1834 // Old code does not know about new markers needed to distinguish all cases
1835 int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
1836 SC_MARKNUM_FOLDEROPEN);
1837 int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
1838 SC_MARKNUM_FOLDER);
1840 while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rcMargin.bottom) {
1842 PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed());
1843 int lineDoc = cs.DocFromDisplay(visibleLine);
1844 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
1845 bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
1846 bool lastSubLine = visibleLine == (cs.DisplayFromDoc(lineDoc + 1) - 1);
1848 int marks = pdoc->GetMark(lineDoc);
1849 if (!firstSubLine)
1850 marks = 0;
1852 bool headWithTail = false;
1854 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1855 // Decide which fold indicator should be displayed
1856 int level = pdoc->GetLevel(lineDoc);
1857 int levelNext = pdoc->GetLevel(lineDoc + 1);
1858 int levelNum = level & SC_FOLDLEVELNUMBERMASK;
1859 int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
1860 if (level & SC_FOLDLEVELHEADERFLAG) {
1861 if (firstSubLine) {
1862 if (levelNum < levelNextNum) {
1863 if (cs.GetExpanded(lineDoc)) {
1864 if (levelNum == SC_FOLDLEVELBASE)
1865 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
1866 else
1867 marks |= 1 << folderOpenMid;
1868 } else {
1869 if (levelNum == SC_FOLDLEVELBASE)
1870 marks |= 1 << SC_MARKNUM_FOLDER;
1871 else
1872 marks |= 1 << folderEnd;
1874 } else if (levelNum > SC_FOLDLEVELBASE) {
1875 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1877 } else {
1878 if (levelNum < levelNextNum) {
1879 if (cs.GetExpanded(lineDoc)) {
1880 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1881 } else if (levelNum > SC_FOLDLEVELBASE) {
1882 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1884 } else if (levelNum > SC_FOLDLEVELBASE) {
1885 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1888 needWhiteClosure = false;
1889 int firstFollowupLine = cs.DocFromDisplay(cs.DisplayFromDoc(lineDoc + 1));
1890 int firstFollowupLineLevel = pdoc->GetLevel(firstFollowupLine);
1891 int secondFollowupLineLevelNum = pdoc->GetLevel(firstFollowupLine + 1) & SC_FOLDLEVELNUMBERMASK;
1892 if (!cs.GetExpanded(lineDoc)) {
1893 if ((firstFollowupLineLevel & SC_FOLDLEVELWHITEFLAG) &&
1894 (levelNum > secondFollowupLineLevelNum))
1895 needWhiteClosure = true;
1897 if (highlightDelimiter.IsFoldBlockHighlighted(firstFollowupLine))
1898 headWithTail = true;
1900 } else if (level & SC_FOLDLEVELWHITEFLAG) {
1901 if (needWhiteClosure) {
1902 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1903 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1904 } else if (levelNextNum > SC_FOLDLEVELBASE) {
1905 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1906 needWhiteClosure = false;
1907 } else {
1908 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1909 needWhiteClosure = false;
1911 } else if (levelNum > SC_FOLDLEVELBASE) {
1912 if (levelNextNum < levelNum) {
1913 if (levelNextNum > SC_FOLDLEVELBASE) {
1914 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1915 } else {
1916 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1918 } else {
1919 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1922 } else if (levelNum > SC_FOLDLEVELBASE) {
1923 if (levelNextNum < levelNum) {
1924 needWhiteClosure = false;
1925 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1926 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1927 needWhiteClosure = true;
1928 } else if (lastSubLine) {
1929 if (levelNextNum > SC_FOLDLEVELBASE) {
1930 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1931 } else {
1932 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1934 } else {
1935 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1937 } else {
1938 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1943 marks &= vs.ms[margin].mask;
1945 PRectangle rcMarker = rcSelMargin;
1946 rcMarker.top = yposScreen;
1947 rcMarker.bottom = yposScreen + vs.lineHeight;
1948 if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
1949 if (firstSubLine) {
1950 char number[100];
1951 sprintf(number, "%d", lineDoc + 1);
1952 if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
1953 int lev = pdoc->GetLevel(lineDoc);
1954 sprintf(number, "%c%c %03X %03X",
1955 (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
1956 (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
1957 lev & SC_FOLDLEVELNUMBERMASK,
1958 lev >> 16
1961 PRectangle rcNumber = rcMarker;
1962 // Right justify
1963 XYPOSITION width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
1964 XYPOSITION xpos = rcNumber.right - width - marginNumberPadding;
1965 rcNumber.left = xpos;
1966 surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font,
1967 rcNumber.top + vs.maxAscent, number, istrlen(number),
1968 vs.styles[STYLE_LINENUMBER].fore,
1969 vs.styles[STYLE_LINENUMBER].back);
1970 } else if (wrapVisualFlags & SC_WRAPVISUALFLAG_MARGIN) {
1971 PRectangle rcWrapMarker = rcMarker;
1972 rcWrapMarker.right -= 3;
1973 rcWrapMarker.left = rcWrapMarker.right - vs.styles[STYLE_LINENUMBER].aveCharWidth;
1974 DrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
1976 } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
1977 if (firstSubLine) {
1978 const StyledText stMargin = pdoc->MarginStyledText(lineDoc);
1979 if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
1980 surface->FillRectangle(rcMarker,
1981 vs.styles[stMargin.StyleAt(0)+vs.marginStyleOffset].back);
1982 if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
1983 int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
1984 rcMarker.left = rcMarker.right - width - 3;
1986 DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, rcMarker.top + vs.maxAscent,
1987 stMargin, 0, stMargin.length);
1992 if (marks) {
1993 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1994 if (marks & 1) {
1995 LineMarker::typeOfFold tFold = LineMarker::undefined;
1996 if ((vs.ms[margin].mask & SC_MASK_FOLDERS) && highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) {
1997 if (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) {
1998 tFold = LineMarker::body;
1999 } else if (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) {
2000 if (firstSubLine) {
2001 tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head;
2002 } else {
2003 if (cs.GetExpanded(lineDoc) || headWithTail) {
2004 tFold = LineMarker::body;
2005 } else {
2006 tFold = LineMarker::undefined;
2009 } else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) {
2010 tFold = LineMarker::tail;
2013 vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font, tFold, vs.ms[margin].style);
2015 marks >>= 1;
2019 visibleLine++;
2020 yposScreen += vs.lineHeight;
2025 PRectangle rcBlankMargin = rcMargin;
2026 rcBlankMargin.left = rcSelMargin.right;
2027 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back);
2029 if (bufferedDraw) {
2030 surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *pixmapSelMargin);
2034 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
2035 int ydiff = (rcTab.bottom - rcTab.top) / 2;
2036 int xhead = rcTab.right - 1 - ydiff;
2037 if (xhead <= rcTab.left) {
2038 ydiff -= rcTab.left - xhead - 1;
2039 xhead = rcTab.left - 1;
2041 if ((rcTab.left + 2) < (rcTab.right - 1))
2042 surface->MoveTo(rcTab.left + 2, ymid);
2043 else
2044 surface->MoveTo(rcTab.right - 1, ymid);
2045 surface->LineTo(rcTab.right - 1, ymid);
2046 surface->LineTo(xhead, ymid - ydiff);
2047 surface->MoveTo(rcTab.right - 1, ymid);
2048 surface->LineTo(xhead, ymid + ydiff);
2051 LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
2052 int posLineStart = pdoc->LineStart(lineNumber);
2053 int posLineEnd = pdoc->LineStart(lineNumber + 1);
2054 PLATFORM_ASSERT(posLineEnd >= posLineStart);
2055 int lineCaret = pdoc->LineFromPosition(sel.MainCaret());
2056 return llc.Retrieve(lineNumber, lineCaret,
2057 posLineEnd - posLineStart, pdoc->GetStyleClock(),
2058 LinesOnScreen() + 1, pdoc->LinesTotal());
2061 bool BadUTF(const char *s, int len, int &trailBytes) {
2062 // For the rules: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
2063 if (trailBytes) {
2064 trailBytes--;
2065 return false;
2067 int utf8status = UTF8Classify(reinterpret_cast<const unsigned char *>(s), len);
2068 if (utf8status & UTF8MaskInvalid) {
2069 return true;
2070 } else {
2071 trailBytes = (utf8status & UTF8MaskWidth) - 1;
2072 return false;
2077 * Fill in the LineLayout data for the given line.
2078 * Copy the given @a line and its styles from the document into local arrays.
2079 * Also determine the x position at which each character starts.
2081 void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) {
2082 if (!ll)
2083 return;
2085 PLATFORM_ASSERT(line < pdoc->LinesTotal());
2086 PLATFORM_ASSERT(ll->chars != NULL);
2087 int posLineStart = pdoc->LineStart(line);
2088 int posLineEnd = pdoc->LineStart(line + 1);
2089 // If the line is very long, limit the treatment to a length that should fit in the viewport
2090 if (posLineEnd > (posLineStart + ll->maxLineLength)) {
2091 posLineEnd = posLineStart + ll->maxLineLength;
2093 if (ll->validity == LineLayout::llCheckTextAndStyle) {
2094 int lineLength = posLineEnd - posLineStart;
2095 if (!vstyle.viewEOL) {
2096 lineLength = pdoc->LineEnd(line) - posLineStart;
2098 if (lineLength == ll->numCharsInLine) {
2099 // See if chars, styles, indicators, are all the same
2100 bool allSame = true;
2101 const int styleMask = pdoc->stylingBitsMask;
2102 // Check base line layout
2103 char styleByte = 0;
2104 int numCharsInLine = 0;
2105 while (numCharsInLine < lineLength) {
2106 int charInDoc = numCharsInLine + posLineStart;
2107 char chDoc = pdoc->CharAt(charInDoc);
2108 styleByte = pdoc->StyleAt(charInDoc);
2109 allSame = allSame &&
2110 (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte & styleMask));
2111 allSame = allSame &&
2112 (ll->indicators[numCharsInLine] == static_cast<char>(styleByte & ~styleMask));
2113 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
2114 allSame = allSame &&
2115 (ll->chars[numCharsInLine] == chDoc);
2116 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
2117 allSame = allSame &&
2118 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
2119 else // Style::caseUpper
2120 allSame = allSame &&
2121 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
2122 numCharsInLine++;
2124 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
2125 if (allSame) {
2126 ll->validity = LineLayout::llPositions;
2127 } else {
2128 ll->validity = LineLayout::llInvalid;
2130 } else {
2131 ll->validity = LineLayout::llInvalid;
2134 if (ll->validity == LineLayout::llInvalid) {
2135 ll->widthLine = LineLayout::wrapWidthInfinite;
2136 ll->lines = 1;
2137 if (vstyle.edgeState == EDGE_BACKGROUND) {
2138 ll->edgeColumn = pdoc->FindColumn(line, theEdge);
2139 if (ll->edgeColumn >= posLineStart) {
2140 ll->edgeColumn -= posLineStart;
2142 } else {
2143 ll->edgeColumn = -1;
2146 char styleByte;
2147 const int styleMask = pdoc->stylingBitsMask;
2148 ll->styleBitsSet = 0;
2149 // Fill base line layout
2150 const int lineLength = posLineEnd - posLineStart;
2151 pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
2152 pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
2153 int numCharsBeforeEOL = pdoc->LineEnd(line) - posLineStart;
2154 const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
2155 for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
2156 styleByte = ll->styles[styleInLine];
2157 ll->styleBitsSet |= styleByte;
2158 ll->styles[styleInLine] = static_cast<char>(styleByte & styleMask);
2159 ll->indicators[styleInLine] = static_cast<char>(styleByte & ~styleMask);
2161 styleByte = static_cast<char>(((lineLength > 0) ? ll->styles[lineLength-1] : 0) & styleMask);
2162 if (vstyle.someStylesForceCase) {
2163 for (int charInLine = 0; charInLine<lineLength; charInLine++) {
2164 char chDoc = ll->chars[charInLine];
2165 if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
2166 ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
2167 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
2168 ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
2171 ll->xHighlightGuide = 0;
2172 // Extra element at the end of the line to hold end x position and act as
2173 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
2174 ll->styles[numCharsInLine] = styleByte; // For eolFilled
2175 ll->indicators[numCharsInLine] = 0;
2177 // Layout the line, determining the position of each character,
2178 // with an extra element at the end for the end of the line.
2179 int startseg = 0; // Start of the current segment, in char. number
2180 XYACCUMULATOR startsegx = 0; // Start of the current segment, in pixels
2181 ll->positions[0] = 0;
2182 XYPOSITION tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
2183 bool lastSegItalics = false;
2184 Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
2186 XYPOSITION ctrlCharWidth[32] = {0};
2187 bool isControlNext = IsControlCharacter(ll->chars[0]);
2188 int trailBytes = 0;
2189 bool isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars, numCharsInLine, trailBytes);
2190 for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
2191 bool isControl = isControlNext;
2192 isControlNext = IsControlCharacter(ll->chars[charInLine + 1]);
2193 bool isBadUTF = isBadUTFNext;
2194 isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars + charInLine + 1, numCharsInLine - charInLine - 1, trailBytes);
2195 if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) ||
2196 isControl || isControlNext || isBadUTF || isBadUTFNext) {
2197 ll->positions[startseg] = 0;
2198 if (vstyle.styles[ll->styles[charInLine]].visible) {
2199 if (isControl) {
2200 if (ll->chars[charInLine] == '\t') {
2201 ll->positions[charInLine + 1] =
2202 ((static_cast<int>((startsegx + 2) / tabWidth) + 1) * tabWidth) - startsegx;
2203 } else if (controlCharSymbol < 32) {
2204 if (ctrlCharWidth[ll->chars[charInLine]] == 0) {
2205 const char *ctrlChar = ControlCharacterString(ll->chars[charInLine]);
2206 ctrlCharWidth[ll->chars[charInLine]] =
2207 surface->WidthText(ctrlCharsFont, ctrlChar, istrlen(ctrlChar)) + ctrlCharPadding;
2209 ll->positions[charInLine + 1] = ctrlCharWidth[ll->chars[charInLine]];
2210 } else {
2211 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
2212 surface->MeasureWidths(ctrlCharsFont, cc, 1,
2213 ll->positions + startseg + 1);
2215 lastSegItalics = false;
2216 } else if (isBadUTF) {
2217 char hexits[4];
2218 sprintf(hexits, "x%2X", ll->chars[charInLine] & 0xff);
2219 ll->positions[charInLine + 1] =
2220 surface->WidthText(ctrlCharsFont, hexits, istrlen(hexits)) + 3;
2221 } else { // Regular character
2222 int lenSeg = charInLine - startseg + 1;
2223 if ((lenSeg == 1) && (' ' == ll->chars[startseg])) {
2224 lastSegItalics = false;
2225 // Over half the segments are single characters and of these about half are space characters.
2226 ll->positions[charInLine + 1] = vstyle.styles[ll->styles[charInLine]].spaceWidth;
2227 } else {
2228 lastSegItalics = vstyle.styles[ll->styles[charInLine]].italic;
2229 posCache.MeasureWidths(surface, vstyle, ll->styles[charInLine], ll->chars + startseg,
2230 lenSeg, ll->positions + startseg + 1, pdoc);
2233 } else { // invisible
2234 for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
2235 ll->positions[posToZero] = 0;
2238 for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
2239 ll->positions[posToIncrease] += startsegx;
2241 startsegx = ll->positions[charInLine + 1];
2242 startseg = charInLine + 1;
2245 // Small hack to make lines that end with italics not cut off the edge of the last character
2246 if ((startseg > 0) && lastSegItalics) {
2247 ll->positions[startseg] += lastSegItalicsOffset;
2249 ll->numCharsInLine = numCharsInLine;
2250 ll->numCharsBeforeEOL = numCharsBeforeEOL;
2251 ll->validity = LineLayout::llPositions;
2253 // Hard to cope when too narrow, so just assume there is space
2254 if (width < 20) {
2255 width = 20;
2257 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
2258 ll->widthLine = width;
2259 if (width == LineLayout::wrapWidthInfinite) {
2260 ll->lines = 1;
2261 } else if (width > ll->positions[ll->numCharsInLine]) {
2262 // Simple common case where line does not need wrapping.
2263 ll->lines = 1;
2264 } else {
2265 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2266 width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
2268 XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
2269 if (wrapIndentMode == SC_WRAPINDENT_INDENT) {
2270 wrapAddIndent = pdoc->IndentSize() * vstyle.spaceWidth;
2271 } else if (wrapIndentMode == SC_WRAPINDENT_FIXED) {
2272 wrapAddIndent = wrapVisualStartIndent * vstyle.aveCharWidth;
2274 ll->wrapIndent = wrapAddIndent;
2275 if (wrapIndentMode != SC_WRAPINDENT_FIXED)
2276 for (int i = 0; i < ll->numCharsInLine; i++) {
2277 if (!IsSpaceOrTab(ll->chars[i])) {
2278 ll->wrapIndent += ll->positions[i]; // Add line indent
2279 break;
2282 // Check for text width minimum
2283 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
2284 ll->wrapIndent = wrapAddIndent;
2285 // Check for wrapIndent minimum
2286 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
2287 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
2288 ll->lines = 0;
2289 // Calculate line start positions based upon width.
2290 int lastGoodBreak = 0;
2291 int lastLineStart = 0;
2292 XYACCUMULATOR startOffset = 0;
2293 int p = 0;
2294 while (p < ll->numCharsInLine) {
2295 if ((ll->positions[p + 1] - startOffset) >= width) {
2296 if (lastGoodBreak == lastLineStart) {
2297 // Try moving to start of last character
2298 if (p > 0) {
2299 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2300 - posLineStart;
2302 if (lastGoodBreak == lastLineStart) {
2303 // Ensure at least one character on line.
2304 lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
2305 - posLineStart;
2308 lastLineStart = lastGoodBreak;
2309 ll->lines++;
2310 ll->SetLineStart(ll->lines, lastGoodBreak);
2311 startOffset = ll->positions[lastGoodBreak];
2312 // take into account the space for start wrap mark and indent
2313 startOffset -= ll->wrapIndent;
2314 p = lastGoodBreak + 1;
2315 continue;
2317 if (p > 0) {
2318 if (wrapState == eWrapChar) {
2319 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2320 - posLineStart;
2321 p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
2322 continue;
2323 } else if (ll->styles[p] != ll->styles[p - 1]) {
2324 lastGoodBreak = p;
2325 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
2326 lastGoodBreak = p;
2329 p++;
2331 ll->lines++;
2333 ll->validity = LineLayout::llLines;
2337 ColourDesired Editor::SelectionBackground(ViewStyle &vsDraw, bool main) {
2338 return main ?
2339 (primarySelection ? vsDraw.selbackground : vsDraw.selbackground2) :
2340 vsDraw.selAdditionalBackground;
2343 ColourDesired Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
2344 ColourDesired background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) {
2345 if (inSelection == 1) {
2346 if (vsDraw.selbackset && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
2347 return SelectionBackground(vsDraw, true);
2349 } else if (inSelection == 2) {
2350 if (vsDraw.selbackset && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
2351 return SelectionBackground(vsDraw, false);
2353 } else {
2354 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
2355 (i >= ll->edgeColumn) &&
2356 (i < ll->numCharsBeforeEOL))
2357 return vsDraw.edgecolour;
2358 if (inHotspot && vsDraw.hotspotBackgroundSet)
2359 return vsDraw.hotspotBackground;
2361 if (overrideBackground && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
2362 return background;
2363 } else {
2364 return vsDraw.styles[styleMain].back;
2368 void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
2369 Point from(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
2370 PRectangle rcCopyArea(start + 1, rcSegment.top, start + 2, rcSegment.bottom);
2371 surface->Copy(rcCopyArea, from,
2372 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
2375 void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
2376 bool isEndMarker, ColourDesired wrapColour) {
2377 surface->PenColour(wrapColour);
2379 enum { xa = 1 }; // gap before start
2380 int w = rcPlace.right - rcPlace.left - xa - 1;
2382 bool xStraight = isEndMarker; // x-mirrored symbol for start marker
2383 bool yStraight = true;
2384 //bool yStraight= isEndMarker; // comment in for start marker y-mirrowed
2386 int x0 = xStraight ? rcPlace.left : rcPlace.right - 1;
2387 int y0 = yStraight ? rcPlace.top : rcPlace.bottom - 1;
2389 int dy = (rcPlace.bottom - rcPlace.top) / 5;
2390 int y = (rcPlace.bottom - rcPlace.top) / 2 + dy;
2392 struct Relative {
2393 Surface *surface;
2394 int xBase;
2395 int xDir;
2396 int yBase;
2397 int yDir;
2398 void MoveTo(int xRelative, int yRelative) {
2399 surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2401 void LineTo(int xRelative, int yRelative) {
2402 surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2405 Relative rel = {surface, x0, xStraight ? 1 : -1, y0, yStraight ? 1 : -1};
2407 // arrow head
2408 rel.MoveTo(xa, y);
2409 rel.LineTo(xa + 2*w / 3, y - dy);
2410 rel.MoveTo(xa, y);
2411 rel.LineTo(xa + 2*w / 3, y + dy);
2413 // arrow body
2414 rel.MoveTo(xa, y);
2415 rel.LineTo(xa + w, y);
2416 rel.LineTo(xa + w, y - 2 * dy);
2417 rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
2418 y - 2 * dy);
2421 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
2422 if (alpha != SC_ALPHA_NOALPHA) {
2423 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
2427 void DrawTextBlob(Surface *surface, ViewStyle &vsDraw, PRectangle rcSegment,
2428 const char *s, ColourDesired textBack, ColourDesired textFore, bool twoPhaseDraw) {
2429 if (!twoPhaseDraw) {
2430 surface->FillRectangle(rcSegment, textBack);
2432 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2433 int normalCharHeight = surface->Ascent(ctrlCharsFont) -
2434 surface->InternalLeading(ctrlCharsFont);
2435 PRectangle rcCChar = rcSegment;
2436 rcCChar.left = rcCChar.left + 1;
2437 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
2438 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
2439 PRectangle rcCentral = rcCChar;
2440 rcCentral.top++;
2441 rcCentral.bottom--;
2442 surface->FillRectangle(rcCentral, textFore);
2443 PRectangle rcChar = rcCChar;
2444 rcChar.left++;
2445 rcChar.right--;
2446 surface->DrawTextClipped(rcChar, ctrlCharsFont,
2447 rcSegment.top + vsDraw.maxAscent, s, istrlen(s),
2448 textBack, textFore);
2451 void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
2452 int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
2453 bool overrideBackground, ColourDesired background,
2454 bool drawWrapMarkEnd, ColourDesired wrapColour) {
2456 const int posLineStart = pdoc->LineStart(line);
2457 const int styleMask = pdoc->stylingBitsMask;
2458 PRectangle rcSegment = rcLine;
2460 const bool lastSubLine = subLine == (ll->lines - 1);
2461 XYPOSITION virtualSpace = 0;
2462 if (lastSubLine) {
2463 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
2464 virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * spaceWidth;
2466 XYPOSITION xEol = ll->positions[lineEnd] - subLineStart;
2468 // Fill the virtual space and show selections within it
2469 if (virtualSpace) {
2470 rcSegment.left = xEol + xStart;
2471 rcSegment.right = xEol + xStart + virtualSpace;
2472 surface->FillRectangle(rcSegment, overrideBackground ? background : vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2473 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
2474 SelectionSegment virtualSpaceRange(SelectionPosition(pdoc->LineEnd(line)), SelectionPosition(pdoc->LineEnd(line), sel.VirtualSpaceFor(pdoc->LineEnd(line))));
2475 for (size_t r=0; r<sel.Count(); r++) {
2476 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2477 if (alpha == SC_ALPHA_NOALPHA) {
2478 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
2479 if (!portion.Empty()) {
2480 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
2481 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
2482 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
2483 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
2484 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
2485 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == sel.Main()));
2492 int eolInSelection = 0;
2493 int alpha = SC_ALPHA_NOALPHA;
2494 if (!hideSelection) {
2495 int posAfterLineEnd = pdoc->LineStart(line + 1);
2496 eolInSelection = (subLine == (ll->lines - 1)) ? sel.InSelectionForEOL(posAfterLineEnd) : 0;
2497 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2500 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
2501 XYPOSITION blobsWidth = 0;
2502 if (lastSubLine) {
2503 for (int eolPos=ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
2504 rcSegment.left = xStart + ll->positions[eolPos] - subLineStart + virtualSpace;
2505 rcSegment.right = xStart + ll->positions[eolPos+1] - subLineStart + virtualSpace;
2506 blobsWidth += rcSegment.Width();
2507 const char *ctrlChar = ControlCharacterString(ll->chars[eolPos]);
2508 int styleMain = ll->styles[eolPos];
2509 ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, eolInSelection, false, styleMain, eolPos, ll);
2510 ColourDesired textFore = vsDraw.styles[styleMain].fore;
2511 if (eolInSelection && vsDraw.selforeset) {
2512 textFore = (eolInSelection == 1) ? vsDraw.selforeground : vsDraw.selAdditionalForeground;
2514 if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1)) {
2515 if (alpha == SC_ALPHA_NOALPHA) {
2516 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2517 } else {
2518 surface->FillRectangle(rcSegment, textBack);
2520 } else {
2521 surface->FillRectangle(rcSegment, textBack);
2523 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
2524 if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2525 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2530 // Draw the eol-is-selected rectangle
2531 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
2532 rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
2534 if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2535 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2536 } else {
2537 if (overrideBackground) {
2538 surface->FillRectangle(rcSegment, background);
2539 } else if (line < pdoc->LinesTotal() - 1) {
2540 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2541 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2542 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2543 } else {
2544 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
2546 if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2547 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2551 // Fill the remainder of the line
2552 rcSegment.left = rcSegment.right;
2553 if (rcSegment.left < rcLine.left)
2554 rcSegment.left = rcLine.left;
2555 rcSegment.right = rcLine.right;
2557 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2558 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2559 } else {
2560 if (overrideBackground) {
2561 surface->FillRectangle(rcSegment, background);
2562 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2563 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2564 } else {
2565 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
2567 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2568 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2572 if (drawWrapMarkEnd) {
2573 PRectangle rcPlace = rcSegment;
2575 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
2576 rcPlace.left = xEol + xStart + virtualSpace;
2577 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2578 } else {
2579 // rcLine is clipped to text area
2580 rcPlace.right = rcLine.right;
2581 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2583 DrawWrapMarker(surface, rcPlace, true, wrapColour);
2587 void Editor::DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, ViewStyle &vsDraw,
2588 int xStart, PRectangle rcLine, LineLayout *ll, int subLine) {
2589 const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
2590 PRectangle rcIndic(
2591 ll->positions[startPos] + xStart - subLineStart,
2592 rcLine.top + vsDraw.maxAscent,
2593 ll->positions[endPos] + xStart - subLineStart,
2594 rcLine.top + vsDraw.maxAscent + 3);
2595 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine);
2598 void Editor::DrawIndicators(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2599 PRectangle rcLine, LineLayout *ll, int subLine, int lineEnd, bool under) {
2600 // Draw decorators
2601 const int posLineStart = pdoc->LineStart(line);
2602 const int lineStart = ll->LineStart(subLine);
2603 const int posLineEnd = posLineStart + lineEnd;
2605 if (!under) {
2606 // Draw indicators
2607 // foreach indicator...
2608 for (int indicnum = 0, mask = 1 << pdoc->stylingBits; mask < 0x100; indicnum++) {
2609 if (!(mask & ll->styleBitsSet)) {
2610 mask <<= 1;
2611 continue;
2613 int startPos = -1;
2614 // foreach style pos in line...
2615 for (int indicPos = lineStart; indicPos <= lineEnd; indicPos++) {
2616 // look for starts...
2617 if (startPos < 0) {
2618 // NOT in indicator run, looking for START
2619 if (indicPos < lineEnd && (ll->indicators[indicPos] & mask))
2620 startPos = indicPos;
2622 // ... or ends
2623 if (startPos >= 0) {
2624 // IN indicator run, looking for END
2625 if (indicPos >= lineEnd || !(ll->indicators[indicPos] & mask)) {
2626 // AT end of indicator run, DRAW it!
2627 DrawIndicator(indicnum, startPos, indicPos, surface, vsDraw, xStart, rcLine, ll, subLine);
2628 // RESET control var
2629 startPos = -1;
2633 mask <<= 1;
2637 for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
2638 if (under == vsDraw.indicators[deco->indicator].under) {
2639 int startPos = posLineStart + lineStart;
2640 if (!deco->rs.ValueAt(startPos)) {
2641 startPos = deco->rs.EndRun(startPos);
2643 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
2644 int endPos = deco->rs.EndRun(startPos);
2645 if (endPos > posLineEnd)
2646 endPos = posLineEnd;
2647 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
2648 surface, vsDraw, xStart, rcLine, ll, subLine);
2649 startPos = deco->rs.EndRun(endPos);
2654 // Use indicators to highlight matching braces
2655 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
2656 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
2657 int braceIndicator = (bracesMatchStyle == STYLE_BRACELIGHT) ? vs.braceHighlightIndicator : vs.braceBadLightIndicator;
2658 if (under == vsDraw.indicators[braceIndicator].under) {
2659 Range rangeLine(posLineStart + lineStart, posLineEnd);
2660 if (rangeLine.ContainsCharacter(braces[0])) {
2661 int braceOffset = braces[0] - posLineStart;
2662 if (braceOffset < ll->numCharsInLine) {
2663 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2666 if (rangeLine.ContainsCharacter(braces[1])) {
2667 int braceOffset = braces[1] - posLineStart;
2668 if (braceOffset < ll->numCharsInLine) {
2669 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2676 void Editor::DrawAnnotation(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2677 PRectangle rcLine, LineLayout *ll, int subLine) {
2678 int indent = pdoc->GetLineIndentation(line) * vsDraw.spaceWidth;
2679 PRectangle rcSegment = rcLine;
2680 int annotationLine = subLine - ll->lines;
2681 const StyledText stAnnotation = pdoc->AnnotationStyledText(line);
2682 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
2683 surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
2684 if (vs.annotationVisible == ANNOTATION_BOXED) {
2685 // Only care about calculating width if need to draw box
2686 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
2687 widthAnnotation += vsDraw.spaceWidth * 2; // Margins
2688 rcSegment.left = xStart + indent;
2689 rcSegment.right = rcSegment.left + widthAnnotation;
2690 } else {
2691 rcSegment.left = xStart;
2693 const int annotationLines = pdoc->AnnotationLines(line);
2694 size_t start = 0;
2695 size_t lengthAnnotation = stAnnotation.LineLength(start);
2696 int lineInAnnotation = 0;
2697 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
2698 start += lengthAnnotation + 1;
2699 lengthAnnotation = stAnnotation.LineLength(start);
2700 lineInAnnotation++;
2702 PRectangle rcText = rcSegment;
2703 if (vs.annotationVisible == ANNOTATION_BOXED) {
2704 surface->FillRectangle(rcText,
2705 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
2706 rcText.left += vsDraw.spaceWidth;
2708 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText, rcText.top + vsDraw.maxAscent,
2709 stAnnotation, start, lengthAnnotation);
2710 if (vs.annotationVisible == ANNOTATION_BOXED) {
2711 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
2712 surface->MoveTo(rcSegment.left, rcSegment.top);
2713 surface->LineTo(rcSegment.left, rcSegment.bottom);
2714 surface->MoveTo(rcSegment.right, rcSegment.top);
2715 surface->LineTo(rcSegment.right, rcSegment.bottom);
2716 if (subLine == ll->lines) {
2717 surface->MoveTo(rcSegment.left, rcSegment.top);
2718 surface->LineTo(rcSegment.right, rcSegment.top);
2720 if (subLine == ll->lines+annotationLines-1) {
2721 surface->MoveTo(rcSegment.left, rcSegment.bottom - 1);
2722 surface->LineTo(rcSegment.right, rcSegment.bottom - 1);
2728 void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
2729 PRectangle rcLine, LineLayout *ll, int subLine) {
2731 PRectangle rcSegment = rcLine;
2733 // Using one font for all control characters so it can be controlled independently to ensure
2734 // the box goes around the characters tightly. Seems to be no way to work out what height
2735 // is taken by an individual character - internal leading gives varying results.
2736 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2738 // See if something overrides the line background color: Either if caret is on the line
2739 // and background color is set for that, or if a marker is defined that forces its background
2740 // color onto the line, or if a marker is defined but has no selection margin in which to
2741 // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
2742 // with the earlier taking precedence. When multiple markers cause background override,
2743 // the color for the highest numbered one is used.
2744 bool overrideBackground = false;
2745 ColourDesired background;
2746 if ((caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && (vsDraw.caretLineAlpha == SC_ALPHA_NOALPHA) && ll->containsCaret) {
2747 overrideBackground = true;
2748 background = vsDraw.caretLineBackground;
2750 if (!overrideBackground) {
2751 int marks = pdoc->GetMark(line);
2752 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
2753 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) &&
2754 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2755 background = vsDraw.markers[markBit].back;
2756 overrideBackground = true;
2758 marks >>= 1;
2761 if (!overrideBackground) {
2762 if (vsDraw.maskInLine) {
2763 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
2764 if (marksMasked) {
2765 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
2766 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY) &&
2767 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2768 overrideBackground = true;
2769 background = vsDraw.markers[markBit].back;
2771 marksMasked >>= 1;
2777 SCNotification scn = {0};
2778 scn.nmhdr.code = SCN_GETBKCOLOR;
2779 scn.line = line;
2780 scn.lParam = -1;
2781 NotifyParent(&scn);
2782 if (scn.lParam != -1)
2784 background = scn.lParam;
2785 overrideBackground = true;
2788 bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
2789 (!overrideBackground) && (vsDraw.whitespaceBackgroundSet);
2791 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2792 const XYPOSITION indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
2793 const XYPOSITION epsilon = 0.0001f; // A small nudge to avoid floating point precision issues
2795 int posLineStart = pdoc->LineStart(line);
2797 int startseg = ll->LineStart(subLine);
2798 XYACCUMULATOR subLineStart = ll->positions[startseg];
2799 if (subLine >= ll->lines) {
2800 DrawAnnotation(surface, vsDraw, line, xStart, rcLine, ll, subLine);
2801 return; // No further drawing
2803 int lineStart = 0;
2804 int lineEnd = 0;
2805 if (subLine < ll->lines) {
2806 lineStart = ll->LineStart(subLine);
2807 lineEnd = ll->LineStart(subLine + 1);
2808 if (subLine == ll->lines - 1) {
2809 lineEnd = ll->numCharsBeforeEOL;
2813 ColourDesired wrapColour = vsDraw.styles[STYLE_DEFAULT].fore;
2814 if (vsDraw.whitespaceForegroundSet)
2815 wrapColour = vsDraw.whitespaceForeground;
2817 bool drawWrapMarkEnd = false;
2819 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2820 if (subLine + 1 < ll->lines) {
2821 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
2825 if (ll->wrapIndent != 0) {
2827 bool continuedWrapLine = false;
2828 if (subLine < ll->lines) {
2829 continuedWrapLine = ll->LineStart(subLine) != 0;
2832 if (continuedWrapLine) {
2833 // draw continuation rect
2834 PRectangle rcPlace = rcSegment;
2836 rcPlace.left = ll->positions[startseg] + xStart - subLineStart;
2837 rcPlace.right = rcPlace.left + ll->wrapIndent;
2839 // default bgnd here..
2840 surface->FillRectangle(rcSegment, overrideBackground ? background :
2841 vsDraw.styles[STYLE_DEFAULT].back);
2843 // main line style would be below but this would be inconsistent with end markers
2844 // also would possibly not be the style at wrap point
2845 //int styleMain = ll->styles[lineStart];
2846 //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back);
2848 if (wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
2850 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
2851 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2852 else
2853 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2855 DrawWrapMarker(surface, rcPlace, false, wrapColour);
2858 xStart += static_cast<int>(ll->wrapIndent);
2862 bool selBackDrawn = vsDraw.selbackset &&
2863 ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA));
2865 // Does not take margin into account but not significant
2866 int xStartVisible = static_cast<int>(subLineStart) - xStart;
2868 ll->psel = &sel;
2870 BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, xStartVisible, selBackDrawn, pdoc);
2871 int next = bfBack.First();
2873 // Background drawing loop
2874 while (twoPhaseDraw && (next < lineEnd)) {
2876 startseg = next;
2877 next = bfBack.Next();
2878 int i = next - 1;
2879 int iDoc = i + posLineStart;
2881 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2882 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2883 // Only try to draw if really visible - enhances performance by not calling environment to
2884 // draw strings that are completely past the right side of the window.
2885 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2886 // Clip to line rectangle, since may have a huge position which will not work with some platforms
2887 if (rcSegment.left < rcLine.left)
2888 rcSegment.left = rcLine.left;
2889 if (rcSegment.right > rcLine.right)
2890 rcSegment.right = rcLine.right;
2892 int styleMain = ll->styles[i];
2893 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2894 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2895 ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2896 if (ll->chars[i] == '\t') {
2897 // Tab display
2898 if (drawWhitespaceBackground &&
2899 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2900 textBack = vsDraw.whitespaceBackground;
2901 surface->FillRectangle(rcSegment, textBack);
2902 } else if (IsControlCharacter(ll->chars[i])) {
2903 // Control character display
2904 inIndentation = false;
2905 surface->FillRectangle(rcSegment, textBack);
2906 } else {
2907 // Normal text display
2908 surface->FillRectangle(rcSegment, textBack);
2909 if (vsDraw.viewWhitespace != wsInvisible ||
2910 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
2911 for (int cpos = 0; cpos <= i - startseg; cpos++) {
2912 if (ll->chars[cpos + startseg] == ' ') {
2913 if (drawWhitespaceBackground &&
2914 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
2915 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
2916 rcSegment.top,
2917 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
2918 rcSegment.bottom);
2919 surface->FillRectangle(rcSpace, vsDraw.whitespaceBackground);
2921 } else {
2922 inIndentation = false;
2927 } else if (rcSegment.left > rcLine.right) {
2928 break;
2932 if (twoPhaseDraw) {
2933 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
2934 xStart, subLine, subLineStart, overrideBackground, background,
2935 drawWrapMarkEnd, wrapColour);
2938 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, true);
2940 if (vsDraw.edgeState == EDGE_LINE) {
2941 int edgeX = theEdge * vsDraw.spaceWidth;
2942 rcSegment.left = edgeX + xStart;
2943 if ((ll->wrapIndent != 0) && (lineStart != 0))
2944 rcSegment.left -= ll->wrapIndent;
2945 rcSegment.right = rcSegment.left + 1;
2946 surface->FillRectangle(rcSegment, vsDraw.edgecolour);
2949 // Draw underline mark as part of background if not transparent
2950 int marks = pdoc->GetMark(line);
2951 int markBit;
2952 for (markBit = 0; (markBit < 32) && marks; markBit++) {
2953 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
2954 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2955 PRectangle rcUnderline = rcLine;
2956 rcUnderline.top = rcUnderline.bottom - 2;
2957 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
2959 marks >>= 1;
2962 inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2963 // Foreground drawing loop
2964 BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, xStartVisible,
2965 ((!twoPhaseDraw && selBackDrawn) || vsDraw.selforeset), pdoc);
2966 next = bfFore.First();
2968 while (next < lineEnd) {
2970 startseg = next;
2971 next = bfFore.Next();
2972 int i = next - 1;
2974 int iDoc = i + posLineStart;
2976 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2977 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2978 // Only try to draw if really visible - enhances performance by not calling environment to
2979 // draw strings that are completely past the right side of the window.
2980 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2981 int styleMain = ll->styles[i];
2982 ColourDesired textFore = vsDraw.styles[styleMain].fore;
2983 Font &textFont = vsDraw.styles[styleMain].font;
2984 //hotspot foreground
2985 if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) {
2986 if (vsDraw.hotspotForegroundSet)
2987 textFore = vsDraw.hotspotForeground;
2989 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2990 if (inSelection && (vsDraw.selforeset)) {
2991 textFore = (inSelection == 1) ? vsDraw.selforeground : vsDraw.selAdditionalForeground;
2993 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2994 ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2995 if (ll->chars[i] == '\t') {
2996 // Tab display
2997 if (!twoPhaseDraw) {
2998 if (drawWhitespaceBackground &&
2999 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
3000 textBack = vsDraw.whitespaceBackground;
3001 surface->FillRectangle(rcSegment, textBack);
3003 if ((vsDraw.viewWhitespace != wsInvisible) ||
3004 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
3005 if (vsDraw.whitespaceForegroundSet)
3006 textFore = vsDraw.whitespaceForeground;
3007 surface->PenColour(textFore);
3009 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
3010 for (int indentCount = (ll->positions[i] + epsilon) / indentWidth;
3011 indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
3012 indentCount++) {
3013 if (indentCount > 0) {
3014 int xIndent = indentCount * indentWidth;
3015 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3016 (ll->xHighlightGuide == xIndent));
3020 if (vsDraw.viewWhitespace != wsInvisible) {
3021 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3022 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
3023 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
3024 DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
3027 } else if (IsControlCharacter(ll->chars[i])) {
3028 // Control character display
3029 inIndentation = false;
3030 if (controlCharSymbol < 32) {
3031 // Draw the character
3032 const char *ctrlChar = ControlCharacterString(ll->chars[i]);
3033 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
3034 } else {
3035 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
3036 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
3037 rcSegment.top + vsDraw.maxAscent,
3038 cc, 1, textBack, textFore);
3040 } else if ((i == startseg) && (static_cast<unsigned char>(ll->chars[i]) >= 0x80) && IsUnicodeMode()) {
3041 // A single byte >= 0x80 in UTF-8 is a bad byte and is displayed as its hex value
3042 char hexits[4];
3043 sprintf(hexits, "x%2X", ll->chars[i] & 0xff);
3044 DrawTextBlob(surface, vsDraw, rcSegment, hexits, textBack, textFore, twoPhaseDraw);
3045 } else {
3046 // Normal text display
3047 if (vsDraw.styles[styleMain].visible) {
3048 if (twoPhaseDraw) {
3049 surface->DrawTextTransparent(rcSegment, textFont,
3050 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
3051 i - startseg + 1, textFore);
3052 } else {
3053 surface->DrawTextNoClip(rcSegment, textFont,
3054 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
3055 i - startseg + 1, textFore, textBack);
3058 if (vsDraw.viewWhitespace != wsInvisible ||
3059 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
3060 for (int cpos = 0; cpos <= i - startseg; cpos++) {
3061 if (ll->chars[cpos + startseg] == ' ') {
3062 if (vsDraw.viewWhitespace != wsInvisible) {
3063 if (vsDraw.whitespaceForegroundSet)
3064 textFore = vsDraw.whitespaceForeground;
3065 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3066 XYPOSITION xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2;
3067 if (!twoPhaseDraw && drawWhitespaceBackground &&
3068 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
3069 textBack = vsDraw.whitespaceBackground;
3070 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
3071 rcSegment.top,
3072 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
3073 rcSegment.bottom);
3074 surface->FillRectangle(rcSpace, textBack);
3076 PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
3077 rcDot.right = rcDot.left + vs.whitespaceSize;
3078 rcDot.bottom = rcDot.top + vs.whitespaceSize;
3079 surface->FillRectangle(rcDot, textFore);
3082 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
3083 for (int indentCount = (ll->positions[cpos + startseg] + epsilon) / indentWidth;
3084 indentCount <= (ll->positions[cpos + startseg + 1] - epsilon) / indentWidth;
3085 indentCount++) {
3086 if (indentCount > 0) {
3087 int xIndent = indentCount * indentWidth;
3088 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3089 (ll->xHighlightGuide == xIndent));
3093 } else {
3094 inIndentation = false;
3099 if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd) {
3100 PRectangle rcUL = rcSegment;
3101 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3102 rcUL.bottom = rcUL.top + 1;
3103 if (vsDraw.hotspotForegroundSet)
3104 surface->FillRectangle(rcUL, vsDraw.hotspotForeground);
3105 else
3106 surface->FillRectangle(rcUL, textFore);
3107 } else if (vsDraw.styles[styleMain].underline) {
3108 PRectangle rcUL = rcSegment;
3109 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3110 rcUL.bottom = rcUL.top + 1;
3111 surface->FillRectangle(rcUL, textFore);
3113 } else if (rcSegment.left > rcLine.right) {
3114 break;
3117 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
3118 && (subLine == 0)) {
3119 int indentSpace = pdoc->GetLineIndentation(line);
3120 int xStartText = ll->positions[pdoc->GetLineIndentPosition(line) - posLineStart];
3122 // Find the most recent line with some text
3124 int lineLastWithText = line;
3125 while (lineLastWithText > Platform::Maximum(line-20, 0) && pdoc->IsWhiteLine(lineLastWithText)) {
3126 lineLastWithText--;
3128 if (lineLastWithText < line) {
3129 xStartText = 100000; // Don't limit to visible indentation on empty line
3130 // This line is empty, so use indentation of last line with text
3131 int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText);
3132 int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
3133 if (isFoldHeader) {
3134 // Level is one more level than parent
3135 indentLastWithText += pdoc->IndentSize();
3137 if (vsDraw.viewIndentationGuides == ivLookForward) {
3138 // In viLookForward mode, previous line only used if it is a fold header
3139 if (isFoldHeader) {
3140 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3142 } else { // viLookBoth
3143 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3147 int lineNextWithText = line;
3148 while (lineNextWithText < Platform::Minimum(line+20, pdoc->LinesTotal()) && pdoc->IsWhiteLine(lineNextWithText)) {
3149 lineNextWithText++;
3151 if (lineNextWithText > line) {
3152 xStartText = 100000; // Don't limit to visible indentation on empty line
3153 // This line is empty, so use indentation of first next line with text
3154 indentSpace = Platform::Maximum(indentSpace,
3155 pdoc->GetLineIndentation(lineNextWithText));
3158 for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) {
3159 int xIndent = indentPos * vsDraw.spaceWidth;
3160 if (xIndent < xStartText) {
3161 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3162 (ll->xHighlightGuide == xIndent));
3167 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, false);
3169 // End of the drawing of the current line
3170 if (!twoPhaseDraw) {
3171 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
3172 xStart, subLine, subLineStart, overrideBackground, background,
3173 drawWrapMarkEnd, wrapColour);
3175 if (!hideSelection && ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA))) {
3176 // For each selection draw
3177 int virtualSpaces = 0;
3178 if (subLine == (ll->lines - 1)) {
3179 virtualSpaces = sel.VirtualSpaceFor(pdoc->LineEnd(line));
3181 SelectionPosition posStart(posLineStart + lineStart);
3182 SelectionPosition posEnd(posLineStart + lineEnd, virtualSpaces);
3183 SelectionSegment virtualSpaceRange(posStart, posEnd);
3184 for (size_t r=0; r<sel.Count(); r++) {
3185 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
3186 if (alpha != SC_ALPHA_NOALPHA) {
3187 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
3188 if (!portion.Empty()) {
3189 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
3190 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
3191 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
3192 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3193 if ((portion.start.Position() - posLineStart) == lineStart && sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
3194 rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
3196 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
3197 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
3198 if (rcSegment.right > rcLine.left)
3199 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == sel.Main()), alpha);
3205 // Draw any translucent whole line states
3206 rcSegment = rcLine;
3207 if ((caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) {
3208 SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
3210 marks = pdoc->GetMark(line);
3211 for (markBit = 0; (markBit < 32) && marks; markBit++) {
3212 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
3213 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3214 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
3215 PRectangle rcUnderline = rcSegment;
3216 rcUnderline.top = rcUnderline.bottom - 2;
3217 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3219 marks >>= 1;
3221 if (vsDraw.maskInLine) {
3222 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
3223 if (marksMasked) {
3224 for (markBit = 0; (markBit < 32) && marksMasked; markBit++) {
3225 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
3226 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3228 marksMasked >>= 1;
3234 void Editor::DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine,
3235 int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) {
3237 int lineStart = ll->LineStart(subLine);
3238 int posBefore = posCaret;
3239 int posAfter = MovePositionOutsideChar(posCaret + 1, 1);
3240 int numCharsToDraw = posAfter - posCaret;
3242 // Work out where the starting and ending offsets are. We need to
3243 // see if the previous character shares horizontal space, such as a
3244 // glyph / combining character. If so we'll need to draw that too.
3245 int offsetFirstChar = offset;
3246 int offsetLastChar = offset + (posAfter - posCaret);
3247 while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
3248 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
3249 // The char does not share horizontal space
3250 break;
3252 // Char shares horizontal space, update the numChars to draw
3253 // Update posBefore to point to the prev char
3254 posBefore = MovePositionOutsideChar(posBefore - 1, -1);
3255 numCharsToDraw = posAfter - posBefore;
3256 offsetFirstChar = offset - (posCaret - posBefore);
3259 // See if the next character shares horizontal space, if so we'll
3260 // need to draw that too.
3261 if (offsetFirstChar < 0)
3262 offsetFirstChar = 0;
3263 numCharsToDraw = offsetLastChar - offsetFirstChar;
3264 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
3265 // Update posAfter to point to the 2nd next char, this is where
3266 // the next character ends, and 2nd next begins. We'll need
3267 // to compare these two
3268 posBefore = posAfter;
3269 posAfter = MovePositionOutsideChar(posAfter + 1, 1);
3270 offsetLastChar = offset + (posAfter - posCaret);
3271 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
3272 // The char does not share horizontal space
3273 break;
3275 // Char shares horizontal space, update the numChars to draw
3276 numCharsToDraw = offsetLastChar - offsetFirstChar;
3279 // We now know what to draw, update the caret drawing rectangle
3280 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
3281 rcCaret.right = ll->positions[offsetFirstChar+numCharsToDraw] - ll->positions[lineStart] + xStart;
3283 // Adjust caret position to take into account any word wrapping symbols.
3284 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3285 XYPOSITION wordWrapCharWidth = ll->wrapIndent;
3286 rcCaret.left += wordWrapCharWidth;
3287 rcCaret.right += wordWrapCharWidth;
3290 // This character is where the caret block is, we override the colours
3291 // (inversed) for drawing the caret here.
3292 int styleMain = ll->styles[offsetFirstChar];
3293 surface->DrawTextClipped(rcCaret, vsDraw.styles[styleMain].font,
3294 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
3295 numCharsToDraw, vsDraw.styles[styleMain].back,
3296 caretColour);
3299 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
3300 if (!pixmapSelPattern->Initialised()) {
3301 const int patternSize = 8;
3302 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3303 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
3304 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
3305 // way between the chrome colour and the chrome highlight colour making a nice transition
3306 // between the window chrome and the content area. And it works in low colour depths.
3307 PRectangle rcPattern(0, 0, patternSize, patternSize);
3309 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
3310 ColourDesired colourFMFill = vs.selbar;
3311 ColourDesired colourFMStripes = vs.selbarlight;
3313 if (!(vs.selbarlight == ColourDesired(0xff, 0xff, 0xff))) {
3314 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
3315 // (Typically, the highlight colour is white.)
3316 colourFMFill = vs.selbarlight;
3319 if (vs.foldmarginColourSet) {
3320 // override default fold margin colour
3321 colourFMFill = vs.foldmarginColour;
3323 if (vs.foldmarginHighlightColourSet) {
3324 // override default fold margin highlight colour
3325 colourFMStripes = vs.foldmarginHighlightColour;
3328 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
3329 for (int y = 0; y < patternSize; y++) {
3330 for (int x = y % 2; x < patternSize; x+=2) {
3331 PRectangle rcPixel(x, y, x+1, y+1);
3332 pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);
3337 if (!pixmapIndentGuide->Initialised()) {
3338 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
3339 pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3340 pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3341 PRectangle rcIG(0, 0, 1, vs.lineHeight);
3342 pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back);
3343 pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore);
3344 pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back);
3345 pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore);
3346 for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
3347 PRectangle rcPixel(0, stripe, 1, stripe+1);
3348 pixmapIndentGuide->FillRectangle(rcPixel, vs.styles[STYLE_INDENTGUIDE].fore);
3349 pixmapIndentGuideHighlight->FillRectangle(rcPixel, vs.styles[STYLE_BRACELIGHT].fore);
3353 if (bufferedDraw) {
3354 if (!pixmapLine->Initialised()) {
3355 PRectangle rcClient = GetClientRectangle();
3356 pixmapLine->InitPixMap(rcClient.Width(), vs.lineHeight,
3357 surfaceWindow, wMain.GetID());
3358 pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
3359 rcClient.Height(), surfaceWindow, wMain.GetID());
3364 void Editor::DrawCarets(Surface *surface, ViewStyle &vsDraw, int lineDoc, int xStart,
3365 PRectangle rcLine, LineLayout *ll, int subLine) {
3366 // When drag is active it is the only caret drawn
3367 bool drawDrag = posDrag.IsValid();
3368 if (hideSelection && !drawDrag)
3369 return;
3370 const int posLineStart = pdoc->LineStart(lineDoc);
3371 // For each selection draw
3372 for (size_t r=0; (r<sel.Count()) || drawDrag; r++) {
3373 const bool mainCaret = r == sel.Main();
3374 const SelectionPosition posCaret = (drawDrag ? posDrag : sel.Range(r).caret);
3375 const int offset = posCaret.Position() - posLineStart;
3376 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
3377 const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
3378 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
3379 XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
3380 if (ll->wrapIndent != 0) {
3381 int lineStart = ll->LineStart(subLine);
3382 if (lineStart != 0) // Wrapped
3383 xposCaret += ll->wrapIndent;
3385 bool caretBlinkState = (caret.active && caret.on) || (!additionalCaretsBlink && !mainCaret);
3386 bool caretVisibleState = additionalCaretsVisible || mainCaret;
3387 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
3388 ((posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
3389 bool caretAtEOF = false;
3390 bool caretAtEOL = false;
3391 bool drawBlockCaret = false;
3392 XYPOSITION widthOverstrikeCaret;
3393 int caretWidthOffset = 0;
3394 PRectangle rcCaret = rcLine;
3396 if (posCaret.Position() == pdoc->Length()) { // At end of document
3397 caretAtEOF = true;
3398 widthOverstrikeCaret = vsDraw.aveCharWidth;
3399 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
3400 caretAtEOL = true;
3401 widthOverstrikeCaret = vsDraw.aveCharWidth;
3402 } else {
3403 widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
3405 if (widthOverstrikeCaret < 3) // Make sure its visible
3406 widthOverstrikeCaret = 3;
3408 if (xposCaret > 0)
3409 caretWidthOffset = 1; // Move back so overlaps both character cells.
3410 xposCaret += xStart;
3411 if (posDrag.IsValid()) {
3412 /* Dragging text, use a line caret */
3413 rcCaret.left = xposCaret - caretWidthOffset;
3414 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3415 } else if (inOverstrike) {
3416 /* Overstrike (insert mode), use a modified bar caret */
3417 rcCaret.top = rcCaret.bottom - 2;
3418 rcCaret.left = xposCaret + 1;
3419 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
3420 } else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) {
3421 /* Block caret */
3422 rcCaret.left = xposCaret;
3423 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
3424 drawBlockCaret = true;
3425 rcCaret.right = xposCaret + widthOverstrikeCaret;
3426 } else {
3427 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
3429 } else {
3430 /* Line caret */
3431 rcCaret.left = xposCaret - caretWidthOffset;
3432 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3434 ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
3435 if (drawBlockCaret) {
3436 DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
3437 } else {
3438 surface->FillRectangle(rcCaret, caretColour);
3442 if (drawDrag)
3443 break;
3447 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
3448 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
3449 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
3450 AllocateGraphics();
3452 RefreshStyleData();
3453 if (paintState == paintAbandoned)
3454 return; // Scroll bars may have changed so need redraw
3455 RefreshPixMaps(surfaceWindow);
3457 StyleToPositionInView(PositionAfterArea(rcArea));
3459 PRectangle rcClient = GetClientRectangle();
3460 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
3461 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3463 int screenLinePaintFirst = rcArea.top / vs.lineHeight;
3465 int xStart = vs.fixedColumnWidth - xOffset;
3466 int ypos = 0;
3467 if (!bufferedDraw)
3468 ypos += screenLinePaintFirst * vs.lineHeight;
3469 int yposScreen = screenLinePaintFirst * vs.lineHeight;
3471 bool paintAbandonedByStyling = paintState == paintAbandoned;
3472 if (needUpdateUI) {
3473 NotifyUpdateUI();
3474 needUpdateUI = 0;
3476 RefreshStyleData();
3477 RefreshPixMaps(surfaceWindow);
3480 // Call priority lines wrap on a window of lines which are likely
3481 // to rendered with the following paint (that is wrap the visible
3482 // lines first).
3483 int startLineToWrap = cs.DocFromDisplay(topLine) - 5;
3484 if (startLineToWrap < 0)
3485 startLineToWrap = 0;
3486 if (WrapLines(false, startLineToWrap)) {
3487 // The wrapping process has changed the height of some lines so
3488 // abandon this paint for a complete repaint.
3489 if (AbandonPaint()) {
3490 return;
3492 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
3494 PLATFORM_ASSERT(pixmapSelPattern->Initialised());
3496 if (!bufferedDraw)
3497 surfaceWindow->SetClip(rcArea);
3499 if (paintState != paintAbandoned) {
3500 PaintSelMargin(surfaceWindow, rcArea);
3502 PRectangle rcRightMargin = rcClient;
3503 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
3504 if (rcArea.Intersects(rcRightMargin)) {
3505 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
3509 if (paintState == paintAbandoned) {
3510 // Either styling or NotifyUpdateUI noticed that painting is needed
3511 // outside the current painting rectangle
3512 //Platform::DebugPrintf("Abandoning paint\n");
3513 if (wrapState != eWrapNone) {
3514 if (paintAbandonedByStyling) {
3515 // Styling has spilled over a line end, such as occurs by starting a multiline
3516 // comment. The width of subsequent text may have changed, so rewrap.
3517 NeedWrapping(cs.DocFromDisplay(topLine));
3520 return;
3522 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
3524 // Allow text at start of line to overlap 1 pixel into the margin as this displays
3525 // serifs and italic stems for aliased text.
3526 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
3528 // Do the painting
3529 if (rcArea.right > vs.fixedColumnWidth - leftTextOverlap) {
3531 Surface *surface = surfaceWindow;
3532 if (bufferedDraw) {
3533 surface = pixmapLine;
3534 PLATFORM_ASSERT(pixmapLine->Initialised());
3536 surface->SetUnicodeMode(IsUnicodeMode());
3537 surface->SetDBCSMode(CodePage());
3539 int visibleLine = topLine + screenLinePaintFirst;
3541 SelectionPosition posCaret = sel.RangeMain().caret;
3542 if (posDrag.IsValid())
3543 posCaret = posDrag;
3544 int lineCaret = pdoc->LineFromPosition(posCaret.Position());
3546 PRectangle rcTextArea = rcClient;
3547 rcTextArea.left = vs.fixedColumnWidth;
3548 rcTextArea.right -= vs.rightMarginWidth;
3550 // Remove selection margin from drawing area so text will not be drawn
3551 // on it in unbuffered mode.
3552 if (!bufferedDraw) {
3553 PRectangle rcClipText = rcTextArea;
3554 rcClipText.left -= leftTextOverlap;
3555 surfaceWindow->SetClip(rcClipText);
3558 // Loop on visible lines
3559 //double durLayout = 0.0;
3560 //double durPaint = 0.0;
3561 //double durCopy = 0.0;
3562 //ElapsedTime etWhole;
3563 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
3564 AutoLineLayout ll(llc, 0);
3565 while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
3567 int lineDoc = cs.DocFromDisplay(visibleLine);
3568 // Only visible lines should be handled by the code within the loop
3569 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
3570 int lineStartSet = cs.DisplayFromDoc(lineDoc);
3571 int subLine = visibleLine - lineStartSet;
3573 // Copy this line and its styles from the document into local arrays
3574 // and determine the x position at which each character starts.
3575 //ElapsedTime et;
3576 if (lineDoc != lineDocPrevious) {
3577 ll.Set(0);
3578 ll.Set(RetrieveLineLayout(lineDoc));
3579 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
3580 lineDocPrevious = lineDoc;
3582 //durLayout += et.Duration(true);
3584 if (ll) {
3585 ll->containsCaret = lineDoc == lineCaret;
3586 if (hideSelection) {
3587 ll->containsCaret = false;
3590 GetHotSpotRange(ll->hsStart, ll->hsEnd);
3592 PRectangle rcLine = rcTextArea;
3593 rcLine.top = ypos;
3594 rcLine.bottom = ypos + vs.lineHeight;
3596 bool bracesIgnoreStyle = false;
3597 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
3598 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
3599 bracesIgnoreStyle = true;
3601 Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
3602 // Highlight the current braces if any
3603 ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
3604 highlightGuideColumn * vs.spaceWidth, bracesIgnoreStyle);
3606 if (leftTextOverlap && bufferedDraw) {
3607 PRectangle rcSpacer = rcLine;
3608 rcSpacer.right = rcSpacer.left;
3609 rcSpacer.left -= 1;
3610 surface->FillRectangle(rcSpacer, vs.styles[STYLE_DEFAULT].back);
3613 // Draw the line
3614 DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
3615 //durPaint += et.Duration(true);
3617 // Restore the previous styles for the brace highlights in case layout is in cache.
3618 ll->RestoreBracesHighlight(rangeLine, braces, bracesIgnoreStyle);
3620 bool expanded = cs.GetExpanded(lineDoc);
3621 const int level = pdoc->GetLevel(lineDoc);
3622 const int levelNext = pdoc->GetLevel(lineDoc + 1);
3623 if ((level & SC_FOLDLEVELHEADERFLAG) &&
3624 ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
3625 // Paint the line above the fold
3626 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
3628 (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
3629 PRectangle rcFoldLine = rcLine;
3630 rcFoldLine.bottom = rcFoldLine.top + 1;
3631 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore);
3633 // Paint the line below the fold
3634 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
3636 (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
3637 PRectangle rcFoldLine = rcLine;
3638 rcFoldLine.top = rcFoldLine.bottom - 1;
3639 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore);
3643 DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine);
3645 if (bufferedDraw) {
3646 Point from(vs.fixedColumnWidth-leftTextOverlap, 0);
3647 PRectangle rcCopyArea(vs.fixedColumnWidth-leftTextOverlap, yposScreen,
3648 rcClient.right - vs.rightMarginWidth, yposScreen + vs.lineHeight);
3649 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
3652 lineWidthMaxSeen = Platform::Maximum(
3653 lineWidthMaxSeen, ll->positions[ll->numCharsInLine]);
3654 //durCopy += et.Duration(true);
3657 if (!bufferedDraw) {
3658 ypos += vs.lineHeight;
3661 yposScreen += vs.lineHeight;
3662 visibleLine++;
3664 //gdk_flush();
3666 ll.Set(0);
3667 //if (durPaint < 0.00000001)
3668 // durPaint = 0.00000001;
3670 // Right column limit indicator
3671 PRectangle rcBeyondEOF = rcClient;
3672 rcBeyondEOF.left = vs.fixedColumnWidth;
3673 rcBeyondEOF.right = rcBeyondEOF.right - vs.rightMarginWidth;
3674 rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight;
3675 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
3676 surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back);
3677 if (vs.edgeState == EDGE_LINE) {
3678 int edgeX = theEdge * vs.spaceWidth;
3679 rcBeyondEOF.left = edgeX + xStart;
3680 rcBeyondEOF.right = rcBeyondEOF.left + 1;
3681 surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour);
3684 //Platform::DebugPrintf(
3685 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
3686 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
3687 NotifyPainted();
3691 // Space (3 space characters) between line numbers and text when printing.
3692 #define lineNumberPrintSpace " "
3694 ColourDesired InvertedLight(ColourDesired orig) {
3695 unsigned int r = orig.GetRed();
3696 unsigned int g = orig.GetGreen();
3697 unsigned int b = orig.GetBlue();
3698 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
3699 unsigned int il = 0xff - l;
3700 if (l == 0)
3701 return ColourDesired(0xff, 0xff, 0xff);
3702 r = r * il / l;
3703 g = g * il / l;
3704 b = b * il / l;
3705 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
3708 // This is mostly copied from the Paint method but with some things omitted
3709 // such as the margin markers, line numbers, selection and caret
3710 // Should be merged back into a combined Draw method.
3711 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
3712 if (!pfr)
3713 return 0;
3715 AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
3716 if (!surface)
3717 return 0;
3718 AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
3719 if (!surfaceMeasure) {
3720 return 0;
3723 // Can't use measurements cached for screen
3724 posCache.Clear();
3726 ViewStyle vsPrint(vs);
3727 vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
3729 // Modify the view style for printing as do not normally want any of the transient features to be printed
3730 // Printing supports only the line number margin.
3731 int lineNumberIndex = -1;
3732 for (int margin = 0; margin < ViewStyle::margins; margin++) {
3733 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
3734 lineNumberIndex = margin;
3735 } else {
3736 vsPrint.ms[margin].width = 0;
3739 vsPrint.fixedColumnWidth = 0;
3740 vsPrint.zoomLevel = printMagnification;
3741 // Don't show indentation guides
3742 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
3743 vsPrint.viewIndentationGuides = ivNone;
3744 // Don't show the selection when printing
3745 vsPrint.selbackset = false;
3746 vsPrint.selforeset = false;
3747 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
3748 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
3749 vsPrint.whitespaceBackgroundSet = false;
3750 vsPrint.whitespaceForegroundSet = false;
3751 vsPrint.showCaretLineBackground = false;
3752 vsPrint.alwaysShowCaretLineBackground = false;
3753 // Don't highlight matching braces using indicators
3754 vsPrint.braceHighlightIndicatorSet = false;
3755 vsPrint.braceBadLightIndicatorSet = false;
3757 // Set colours for printing according to users settings
3758 for (size_t sty = 0; sty < vsPrint.stylesSize; sty++) {
3759 if (printColourMode == SC_PRINT_INVERTLIGHT) {
3760 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
3761 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
3762 } else if (printColourMode == SC_PRINT_BLACKONWHITE) {
3763 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
3764 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3765 } else if (printColourMode == SC_PRINT_COLOURONWHITE) {
3766 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3767 } else if (printColourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
3768 if (sty <= STYLE_DEFAULT) {
3769 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3773 // White background for the line numbers
3774 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
3776 // Printing uses different margins, so reset screen margins
3777 vsPrint.leftMarginWidth = 0;
3778 vsPrint.rightMarginWidth = 0;
3780 vsPrint.Refresh(*surfaceMeasure);
3781 // Determining width must hapen after fonts have been realised in Refresh
3782 int lineNumberWidth = 0;
3783 if (lineNumberIndex >= 0) {
3784 lineNumberWidth = surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
3785 "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace));
3786 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
3787 vsPrint.Refresh(*surfaceMeasure); // Recalculate fixedColumnWidth
3790 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
3791 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
3792 if (linePrintLast < linePrintStart)
3793 linePrintLast = linePrintStart;
3794 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
3795 if (linePrintLast > linePrintMax)
3796 linePrintLast = linePrintMax;
3797 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
3798 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
3799 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
3800 int endPosPrint = pdoc->Length();
3801 if (linePrintLast < pdoc->LinesTotal())
3802 endPosPrint = pdoc->LineStart(linePrintLast + 1);
3804 // Ensure we are styled to where we are formatting.
3805 pdoc->EnsureStyledTo(endPosPrint);
3807 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
3808 int ypos = pfr->rc.top;
3810 int lineDoc = linePrintStart;
3812 int nPrintPos = pfr->chrg.cpMin;
3813 int visibleLine = 0;
3814 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
3815 if (printWrapState == eWrapNone)
3816 widthPrint = LineLayout::wrapWidthInfinite;
3818 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
3820 // When printing, the hdc and hdcTarget may be the same, so
3821 // changing the state of surfaceMeasure may change the underlying
3822 // state of surface. Therefore, any cached state is discarded before
3823 // using each surface.
3824 surfaceMeasure->FlushCachedState();
3826 // Copy this line and its styles from the document into local arrays
3827 // and determine the x position at which each character starts.
3828 LineLayout ll(pdoc->LineStart(lineDoc+1)-pdoc->LineStart(lineDoc)+1);
3829 LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
3831 ll.containsCaret = false;
3833 PRectangle rcLine;
3834 rcLine.left = pfr->rc.left;
3835 rcLine.top = ypos;
3836 rcLine.right = pfr->rc.right - 1;
3837 rcLine.bottom = ypos + vsPrint.lineHeight;
3839 // When document line is wrapped over multiple display lines, find where
3840 // to start printing from to ensure a particular position is on the first
3841 // line of the page.
3842 if (visibleLine == 0) {
3843 int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
3844 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
3845 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
3846 visibleLine = -iwl;
3850 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
3851 visibleLine = -(ll.lines - 1);
3855 if (draw && lineNumberWidth &&
3856 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
3857 (visibleLine >= 0)) {
3858 char number[100];
3859 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
3860 PRectangle rcNumber = rcLine;
3861 rcNumber.right = rcNumber.left + lineNumberWidth;
3862 // Right justify
3863 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
3864 vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
3865 surface->FlushCachedState();
3866 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
3867 ypos + vsPrint.maxAscent, number, istrlen(number),
3868 vsPrint.styles[STYLE_LINENUMBER].fore,
3869 vsPrint.styles[STYLE_LINENUMBER].back);
3872 // Draw the line
3873 surface->FlushCachedState();
3875 for (int iwl = 0; iwl < ll.lines; iwl++) {
3876 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
3877 if (visibleLine >= 0) {
3878 if (draw) {
3879 rcLine.top = ypos;
3880 rcLine.bottom = ypos + vsPrint.lineHeight;
3881 DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
3883 ypos += vsPrint.lineHeight;
3885 visibleLine++;
3886 if (iwl == ll.lines - 1)
3887 nPrintPos = pdoc->LineStart(lineDoc + 1);
3888 else
3889 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
3893 ++lineDoc;
3896 // Clear cache so measurements are not used for screen
3897 posCache.Clear();
3899 return nPrintPos;
3902 int Editor::TextWidth(int style, const char *text) {
3903 RefreshStyleData();
3904 AutoSurface surface(this);
3905 if (surface) {
3906 return surface->WidthText(vs.styles[style].font, text, istrlen(text));
3907 } else {
3908 return 1;
3912 // Empty method is overridden on GTK+ to show / hide scrollbars
3913 void Editor::ReconfigureScrollBars() {}
3915 void Editor::SetScrollBars() {
3916 RefreshStyleData();
3918 int nMax = MaxScrollPos();
3919 int nPage = LinesOnScreen();
3920 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
3921 if (modified) {
3922 DwellEnd(true);
3925 // TODO: ensure always showing as many lines as possible
3926 // May not be, if, for example, window made larger
3927 if (topLine > MaxScrollPos()) {
3928 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
3929 SetVerticalScrollPos();
3930 Redraw();
3932 if (modified) {
3933 if (!AbandonPaint())
3934 Redraw();
3936 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
3939 void Editor::ChangeSize() {
3940 DropGraphics(false);
3941 SetScrollBars();
3942 if (wrapState != eWrapNone) {
3943 PRectangle rcTextArea = GetClientRectangle();
3944 rcTextArea.left = vs.fixedColumnWidth;
3945 rcTextArea.right -= vs.rightMarginWidth;
3946 if (wrapWidth != rcTextArea.Width()) {
3947 NeedWrapping();
3948 Redraw();
3953 int Editor::InsertSpace(int position, unsigned int spaces) {
3954 if (spaces > 0) {
3955 std::string spaceText(spaces, ' ');
3956 pdoc->InsertString(position, spaceText.c_str(), spaces);
3957 position += spaces;
3959 return position;
3962 void Editor::AddChar(char ch) {
3963 char s[2];
3964 s[0] = ch;
3965 s[1] = '\0';
3966 AddCharUTF(s, 1);
3969 void Editor::FilterSelections() {
3970 if (!additionalSelectionTyping && (sel.Count() > 1)) {
3971 SelectionRange rangeOnly = sel.RangeMain();
3972 InvalidateSelection(rangeOnly, true);
3973 sel.SetSelection(rangeOnly);
3977 static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
3978 return *a < *b;
3981 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
3982 void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
3983 FilterSelections();
3985 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
3987 std::vector<SelectionRange *> selPtrs;
3988 for (size_t r = 0; r < sel.Count(); r++) {
3989 selPtrs.push_back(&sel.Range(r));
3991 std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
3993 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
3994 rit != selPtrs.rend(); ++rit) {
3995 SelectionRange *currentSel = *rit;
3996 if (!RangeContainsProtected(currentSel->Start().Position(),
3997 currentSel->End().Position())) {
3998 int positionInsert = currentSel->Start().Position();
3999 if (!currentSel->Empty()) {
4000 if (currentSel->Length()) {
4001 pdoc->DeleteChars(positionInsert, currentSel->Length());
4002 currentSel->ClearVirtualSpace();
4003 } else {
4004 // Range is all virtual so collapse to start of virtual space
4005 currentSel->MinimizeVirtualSpace();
4007 } else if (inOverstrike) {
4008 if (positionInsert < pdoc->Length()) {
4009 if (!IsEOLChar(pdoc->CharAt(positionInsert))) {
4010 pdoc->DelChar(positionInsert);
4011 currentSel->ClearVirtualSpace();
4015 positionInsert = InsertSpace(positionInsert, currentSel->caret.VirtualSpace());
4016 if (pdoc->InsertString(positionInsert, s, len)) {
4017 currentSel->caret.SetPosition(positionInsert + len);
4018 currentSel->anchor.SetPosition(positionInsert + len);
4020 currentSel->ClearVirtualSpace();
4021 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4022 if (wrapState != eWrapNone) {
4023 AutoSurface surface(this);
4024 if (surface) {
4025 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
4026 SetScrollBars();
4027 SetVerticalScrollPos();
4028 Redraw();
4035 if (wrapState != eWrapNone) {
4036 SetScrollBars();
4038 ThinRectangularRange();
4039 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4040 EnsureCaretVisible();
4041 // Avoid blinking during rapid typing:
4042 ShowCaretAtCurrentPosition();
4043 if ((caretSticky == SC_CARETSTICKY_OFF) ||
4044 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
4045 SetLastXChosen();
4048 if (treatAsDBCS) {
4049 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
4050 static_cast<unsigned char>(s[1]));
4051 } else {
4052 int byte = static_cast<unsigned char>(s[0]);
4053 if ((byte < 0xC0) || (1 == len)) {
4054 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
4055 // characters when not in UTF-8 mode.
4056 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
4057 // characters representing themselves.
4058 } else {
4059 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
4060 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
4061 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
4062 if (byte < 0xE0) {
4063 int byte2 = static_cast<unsigned char>(s[1]);
4064 if ((byte2 & 0xC0) == 0x80) {
4065 // Two-byte-character lead-byte followed by a trail-byte.
4066 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
4068 // A two-byte-character lead-byte not followed by trail-byte
4069 // represents itself.
4070 } else if (byte < 0xF0) {
4071 int byte2 = static_cast<unsigned char>(s[1]);
4072 int byte3 = static_cast<unsigned char>(s[2]);
4073 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
4074 // Three-byte-character lead byte followed by two trail bytes.
4075 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
4076 (byte3 & 0x3F));
4078 // A three-byte-character lead-byte not followed by two trail-bytes
4079 // represents itself.
4082 NotifyChar(byte);
4085 if (recordingMacro) {
4086 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
4090 void Editor::InsertPaste(SelectionPosition selStart, const char *text, int len) {
4091 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
4092 selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
4093 if (pdoc->InsertString(selStart.Position(), text, len)) {
4094 SetEmptySelection(selStart.Position() + len);
4096 } else {
4097 // SC_MULTIPASTE_EACH
4098 for (size_t r=0; r<sel.Count(); r++) {
4099 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4100 sel.Range(r).End().Position())) {
4101 int positionInsert = sel.Range(r).Start().Position();
4102 if (!sel.Range(r).Empty()) {
4103 if (sel.Range(r).Length()) {
4104 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
4105 sel.Range(r).ClearVirtualSpace();
4106 } else {
4107 // Range is all virtual so collapse to start of virtual space
4108 sel.Range(r).MinimizeVirtualSpace();
4111 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
4112 if (pdoc->InsertString(positionInsert, text, len)) {
4113 sel.Range(r).caret.SetPosition(positionInsert + len);
4114 sel.Range(r).anchor.SetPosition(positionInsert + len);
4116 sel.Range(r).ClearVirtualSpace();
4122 void Editor::ClearSelection(bool retainMultipleSelections) {
4123 if (!sel.IsRectangular() && !retainMultipleSelections)
4124 FilterSelections();
4125 UndoGroup ug(pdoc);
4126 for (size_t r=0; r<sel.Count(); r++) {
4127 if (!sel.Range(r).Empty()) {
4128 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4129 sel.Range(r).End().Position())) {
4130 pdoc->DeleteChars(sel.Range(r).Start().Position(),
4131 sel.Range(r).Length());
4132 sel.Range(r) = sel.Range(r).Start();
4136 ThinRectangularRange();
4137 sel.RemoveDuplicates();
4138 ClaimSelection();
4141 void Editor::ClearAll() {
4143 UndoGroup ug(pdoc);
4144 if (0 != pdoc->Length()) {
4145 pdoc->DeleteChars(0, pdoc->Length());
4147 if (!pdoc->IsReadOnly()) {
4148 cs.Clear();
4149 pdoc->AnnotationClearAll();
4150 pdoc->MarginClearAll();
4153 sel.Clear();
4154 SetTopLine(0);
4155 SetVerticalScrollPos();
4156 InvalidateStyleRedraw();
4159 void Editor::ClearDocumentStyle() {
4160 Decoration *deco = pdoc->decorations.root;
4161 while (deco) {
4162 // Save next in case deco deleted
4163 Decoration *decoNext = deco->next;
4164 if (deco->indicator < INDIC_CONTAINER) {
4165 pdoc->decorations.SetCurrentIndicator(deco->indicator);
4166 pdoc->DecorationFillRange(0, 0, pdoc->Length());
4168 deco = decoNext;
4170 pdoc->StartStyling(0, '\377');
4171 pdoc->SetStyleFor(pdoc->Length(), 0);
4172 cs.ShowAll();
4173 pdoc->ClearLevels();
4176 void Editor::CopyAllowLine() {
4177 SelectionText selectedText;
4178 CopySelectionRange(&selectedText, true);
4179 CopyToClipboard(selectedText);
4182 void Editor::Cut() {
4183 pdoc->CheckReadOnly();
4184 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
4185 Copy();
4186 ClearSelection();
4190 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
4191 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
4192 return;
4194 sel.Clear();
4195 sel.RangeMain() = SelectionRange(pos);
4196 int line = pdoc->LineFromPosition(sel.MainCaret());
4197 UndoGroup ug(pdoc);
4198 sel.RangeMain().caret = SelectionPosition(
4199 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
4200 int xInsert = XFromPosition(sel.RangeMain().caret);
4201 bool prevCr = false;
4202 while ((len > 0) && IsEOLChar(ptr[len-1]))
4203 len--;
4204 for (int i = 0; i < len; i++) {
4205 if (IsEOLChar(ptr[i])) {
4206 if ((ptr[i] == '\r') || (!prevCr))
4207 line++;
4208 if (line >= pdoc->LinesTotal()) {
4209 if (pdoc->eolMode != SC_EOL_LF)
4210 pdoc->InsertChar(pdoc->Length(), '\r');
4211 if (pdoc->eolMode != SC_EOL_CR)
4212 pdoc->InsertChar(pdoc->Length(), '\n');
4214 // Pad the end of lines with spaces if required
4215 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
4216 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
4217 while (XFromPosition(sel.MainCaret()) < xInsert) {
4218 pdoc->InsertChar(sel.MainCaret(), ' ');
4219 sel.RangeMain().caret.Add(1);
4222 prevCr = ptr[i] == '\r';
4223 } else {
4224 pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
4225 sel.RangeMain().caret.Add(1);
4226 prevCr = false;
4229 SetEmptySelection(pos);
4232 bool Editor::CanPaste() {
4233 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
4236 void Editor::Clear() {
4237 // If multiple selections, don't delete EOLS
4238 if (sel.Empty()) {
4239 bool singleVirtual = false;
4240 if ((sel.Count() == 1) &&
4241 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
4242 sel.RangeMain().Start().VirtualSpace()) {
4243 singleVirtual = true;
4245 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
4246 for (size_t r=0; r<sel.Count(); r++) {
4247 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
4248 if (sel.Range(r).Start().VirtualSpace()) {
4249 if (sel.Range(r).anchor < sel.Range(r).caret)
4250 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
4251 else
4252 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
4254 if ((sel.Count() == 1) || !IsEOLChar(pdoc->CharAt(sel.Range(r).caret.Position()))) {
4255 pdoc->DelChar(sel.Range(r).caret.Position());
4256 sel.Range(r).ClearVirtualSpace();
4257 } // else multiple selection so don't eat line ends
4258 } else {
4259 sel.Range(r).ClearVirtualSpace();
4262 } else {
4263 ClearSelection();
4265 sel.RemoveDuplicates();
4268 void Editor::SelectAll() {
4269 sel.Clear();
4270 SetSelection(0, pdoc->Length());
4271 Redraw();
4274 void Editor::Undo() {
4275 if (pdoc->CanUndo()) {
4276 InvalidateCaret();
4277 int newPos = pdoc->Undo();
4278 if (newPos >= 0)
4279 SetEmptySelection(newPos);
4280 EnsureCaretVisible();
4284 void Editor::Redo() {
4285 if (pdoc->CanRedo()) {
4286 int newPos = pdoc->Redo();
4287 if (newPos >= 0)
4288 SetEmptySelection(newPos);
4289 EnsureCaretVisible();
4293 void Editor::DelChar() {
4294 if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) {
4295 pdoc->DelChar(sel.MainCaret());
4297 // Avoid blinking during rapid typing:
4298 ShowCaretAtCurrentPosition();
4301 void Editor::DelCharBack(bool allowLineStartDeletion) {
4302 if (!sel.IsRectangular())
4303 FilterSelections();
4304 if (sel.IsRectangular())
4305 allowLineStartDeletion = false;
4306 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
4307 if (sel.Empty()) {
4308 for (size_t r=0; r<sel.Count(); r++) {
4309 if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
4310 if (sel.Range(r).caret.VirtualSpace()) {
4311 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
4312 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
4313 } else {
4314 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4315 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
4316 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
4317 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
4318 UndoGroup ugInner(pdoc, !ug.Needed());
4319 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4320 int indentationStep = pdoc->IndentSize();
4321 if (indentation % indentationStep == 0) {
4322 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
4323 } else {
4324 pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
4326 // SetEmptySelection
4327 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos),
4328 pdoc->GetLineIndentPosition(lineCurrentPos));
4329 } else {
4330 pdoc->DelCharBack(sel.Range(r).caret.Position());
4334 } else {
4335 sel.Range(r).ClearVirtualSpace();
4338 ThinRectangularRange();
4339 } else {
4340 ClearSelection();
4342 sel.RemoveDuplicates();
4343 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
4344 // Avoid blinking during rapid typing:
4345 ShowCaretAtCurrentPosition();
4348 void Editor::NotifyFocus(bool) {}
4350 void Editor::SetCtrlID(int identifier) {
4351 ctrlID = identifier;
4354 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
4355 SCNotification scn = {0};
4356 scn.nmhdr.code = SCN_STYLENEEDED;
4357 scn.position = endStyleNeeded;
4358 NotifyParent(scn);
4361 void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
4362 NotifyStyleToNeeded(endStyleNeeded);
4365 void Editor::NotifyLexerChanged(Document *, void *) {
4368 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
4369 errorStatus = status;
4372 void Editor::NotifyChar(int ch) {
4373 SCNotification scn = {0};
4374 scn.nmhdr.code = SCN_CHARADDED;
4375 scn.ch = ch;
4376 NotifyParent(scn);
4379 void Editor::NotifySavePoint(bool isSavePoint) {
4380 SCNotification scn = {0};
4381 if (isSavePoint) {
4382 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
4383 } else {
4384 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
4386 NotifyParent(scn);
4389 void Editor::NotifyModifyAttempt() {
4390 SCNotification scn = {0};
4391 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
4392 NotifyParent(scn);
4395 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
4396 SCNotification scn = {0};
4397 scn.nmhdr.code = SCN_DOUBLECLICK;
4398 scn.line = LineFromLocation(pt);
4399 scn.position = PositionFromLocation(pt, true);
4400 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4401 (alt ? SCI_ALT : 0);
4402 NotifyParent(scn);
4405 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
4406 SCNotification scn = {0};
4407 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
4408 scn.position = position;
4409 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4410 (alt ? SCI_ALT : 0);
4411 NotifyParent(scn);
4414 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
4415 SCNotification scn = {0};
4416 scn.nmhdr.code = SCN_HOTSPOTCLICK;
4417 scn.position = position;
4418 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4419 (alt ? SCI_ALT : 0);
4420 NotifyParent(scn);
4423 void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
4424 SCNotification scn = {0};
4425 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
4426 scn.position = position;
4427 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4428 (alt ? SCI_ALT : 0);
4429 NotifyParent(scn);
4432 void Editor::NotifyUpdateUI() {
4433 SCNotification scn = {0};
4434 scn.nmhdr.code = SCN_UPDATEUI;
4435 scn.updated = needUpdateUI;
4436 NotifyParent(scn);
4439 void Editor::NotifyPainted() {
4440 SCNotification scn = {0};
4441 scn.nmhdr.code = SCN_PAINTED;
4442 NotifyParent(scn);
4445 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
4446 int mask = pdoc->decorations.AllOnFor(position);
4447 if ((click && mask) || pdoc->decorations.clickNotified) {
4448 SCNotification scn = {0};
4449 pdoc->decorations.clickNotified = click;
4450 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
4451 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | (alt ? SCI_ALT : 0);
4452 scn.position = position;
4453 NotifyParent(scn);
4457 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
4458 int marginClicked = -1;
4459 int x = 0;
4460 for (int margin = 0; margin < ViewStyle::margins; margin++) {
4461 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
4462 marginClicked = margin;
4463 x += vs.ms[margin].width;
4465 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
4466 SCNotification scn = {0};
4467 scn.nmhdr.code = SCN_MARGINCLICK;
4468 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4469 (alt ? SCI_ALT : 0);
4470 scn.position = pdoc->LineStart(LineFromLocation(pt));
4471 scn.margin = marginClicked;
4472 NotifyParent(scn);
4473 return true;
4474 } else {
4475 return false;
4479 void Editor::NotifyNeedShown(int pos, int len) {
4480 SCNotification scn = {0};
4481 scn.nmhdr.code = SCN_NEEDSHOWN;
4482 scn.position = pos;
4483 scn.length = len;
4484 NotifyParent(scn);
4487 void Editor::NotifyDwelling(Point pt, bool state) {
4488 SCNotification scn = {0};
4489 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
4490 scn.position = PositionFromLocation(pt, true);
4491 scn.x = pt.x;
4492 scn.y = pt.y;
4493 NotifyParent(scn);
4496 void Editor::NotifyZoom() {
4497 SCNotification scn = {0};
4498 scn.nmhdr.code = SCN_ZOOM;
4499 NotifyParent(scn);
4502 // Notifications from document
4503 void Editor::NotifyModifyAttempt(Document *, void *) {
4504 //Platform::DebugPrintf("** Modify Attempt\n");
4505 NotifyModifyAttempt();
4508 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
4509 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
4510 NotifySavePoint(atSavePoint);
4513 void Editor::CheckModificationForWrap(DocModification mh) {
4514 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
4515 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4516 int lineDoc = pdoc->LineFromPosition(mh.position);
4517 int lines = Platform::Maximum(0, mh.linesAdded);
4518 if (wrapState != eWrapNone) {
4519 NeedWrapping(lineDoc, lineDoc + lines + 1);
4521 RefreshStyleData();
4522 // Fix up annotation heights
4523 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
4527 // Move a position so it is still after the same character as before the insertion.
4528 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
4529 if (position > startInsertion) {
4530 return position + length;
4532 return position;
4535 // Move a position so it is still after the same character as before the deletion if that
4536 // character is still present else after the previous surviving character.
4537 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
4538 if (position > startDeletion) {
4539 int endDeletion = startDeletion + length;
4540 if (position > endDeletion) {
4541 return position - length;
4542 } else {
4543 return startDeletion;
4545 } else {
4546 return position;
4550 void Editor::NotifyModified(Document *, DocModification mh, void *) {
4551 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
4552 if (paintState == painting) {
4553 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
4555 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
4556 if (paintState == painting) {
4557 CheckForChangeOutsidePaint(
4558 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
4559 } else {
4560 // Could check that change is before last visible line.
4561 Redraw();
4564 if (mh.modificationType & SC_MOD_LEXERSTATE) {
4565 if (paintState == painting) {
4566 CheckForChangeOutsidePaint(
4567 Range(mh.position, mh.position + mh.length));
4568 } else {
4569 Redraw();
4572 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
4573 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4574 pdoc->IncrementStyleClock();
4576 if (paintState == notPainting) {
4577 if (mh.position < pdoc->LineStart(topLine)) {
4578 // Styling performed before this view
4579 Redraw();
4580 } else {
4581 InvalidateRange(mh.position, mh.position + mh.length);
4584 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4585 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4587 } else {
4588 // Move selection and brace highlights
4589 if (mh.modificationType & SC_MOD_INSERTTEXT) {
4590 sel.MovePositions(true, mh.position, mh.length);
4591 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
4592 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
4593 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
4594 sel.MovePositions(false, mh.position, mh.length);
4595 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
4596 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
4598 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && cs.HiddenLines()) {
4599 // Some lines are hidden so may need shown.
4600 // TODO: check if the modified area is hidden.
4601 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
4602 int lineOfPos = pdoc->LineFromPosition(mh.position);
4603 bool insertingNewLine = false;
4604 for (int i=0; i < mh.length; i++) {
4605 if ((mh.text[i] == '\n') || (mh.text[i] == '\r'))
4606 insertingNewLine = true;
4608 if (insertingNewLine && (mh.position != pdoc->LineStart(lineOfPos)))
4609 NotifyNeedShown(mh.position, pdoc->LineStart(lineOfPos+1) - mh.position);
4610 else
4611 NotifyNeedShown(mh.position, 0);
4612 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
4613 NotifyNeedShown(mh.position, mh.length);
4616 if (mh.linesAdded != 0) {
4617 // Update contraction state for inserted and removed lines
4618 // lineOfPos should be calculated in context of state before modification, shouldn't it
4619 int lineOfPos = pdoc->LineFromPosition(mh.position);
4620 if (mh.linesAdded > 0) {
4621 cs.InsertLines(lineOfPos, mh.linesAdded);
4622 } else {
4623 cs.DeleteLines(lineOfPos, -mh.linesAdded);
4626 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
4627 int lineDoc = pdoc->LineFromPosition(mh.position);
4628 if (vs.annotationVisible) {
4629 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
4630 Redraw();
4633 CheckModificationForWrap(mh);
4634 if (mh.linesAdded != 0) {
4635 // Avoid scrolling of display if change before current display
4636 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
4637 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
4638 if (newTop != topLine) {
4639 SetTopLine(newTop);
4640 SetVerticalScrollPos();
4644 //Platform::DebugPrintf("** %x Doc Changed\n", this);
4645 // TODO: could invalidate from mh.startModification to end of screen
4646 //InvalidateRange(mh.position, mh.position + mh.length);
4647 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
4648 QueueStyling(pdoc->Length());
4649 Redraw();
4651 } else {
4652 //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
4653 // mh.position, mh.position + mh.length);
4654 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
4655 QueueStyling(mh.position + mh.length);
4656 InvalidateRange(mh.position, mh.position + mh.length);
4661 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
4662 SetScrollBars();
4665 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
4666 if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) {
4667 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
4668 // Fold changes can affect the drawing of following lines so redraw whole margin
4669 RedrawSelMargin(highlightDelimiter.isEnabled ? -1 : mh.line-1, true);
4670 } else {
4671 RedrawSelMargin(mh.line);
4676 // NOW pay the piper WRT "deferred" visual updates
4677 if (IsLastStep(mh)) {
4678 SetScrollBars();
4679 Redraw();
4682 // If client wants to see this modification
4683 if (mh.modificationType & modEventMask) {
4684 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
4685 // Real modification made to text of document.
4686 NotifyChange(); // Send EN_CHANGE
4689 SCNotification scn = {0};
4690 scn.nmhdr.code = SCN_MODIFIED;
4691 scn.position = mh.position;
4692 scn.modificationType = mh.modificationType;
4693 scn.text = mh.text;
4694 scn.length = mh.length;
4695 scn.linesAdded = mh.linesAdded;
4696 scn.line = mh.line;
4697 scn.foldLevelNow = mh.foldLevelNow;
4698 scn.foldLevelPrev = mh.foldLevelPrev;
4699 scn.token = mh.token;
4700 scn.annotationLinesAdded = mh.annotationLinesAdded;
4701 NotifyParent(scn);
4705 void Editor::NotifyDeleted(Document *, void *) {
4706 /* Do nothing */
4709 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
4711 // Enumerates all macroable messages
4712 switch (iMessage) {
4713 case SCI_CUT:
4714 case SCI_COPY:
4715 case SCI_PASTE:
4716 case SCI_CLEAR:
4717 case SCI_REPLACESEL:
4718 case SCI_ADDTEXT:
4719 case SCI_INSERTTEXT:
4720 case SCI_APPENDTEXT:
4721 case SCI_CLEARALL:
4722 case SCI_SELECTALL:
4723 case SCI_GOTOLINE:
4724 case SCI_GOTOPOS:
4725 case SCI_SEARCHANCHOR:
4726 case SCI_SEARCHNEXT:
4727 case SCI_SEARCHPREV:
4728 case SCI_LINEDOWN:
4729 case SCI_LINEDOWNEXTEND:
4730 case SCI_PARADOWN:
4731 case SCI_PARADOWNEXTEND:
4732 case SCI_LINEUP:
4733 case SCI_LINEUPEXTEND:
4734 case SCI_PARAUP:
4735 case SCI_PARAUPEXTEND:
4736 case SCI_CHARLEFT:
4737 case SCI_CHARLEFTEXTEND:
4738 case SCI_CHARRIGHT:
4739 case SCI_CHARRIGHTEXTEND:
4740 case SCI_WORDLEFT:
4741 case SCI_WORDLEFTEXTEND:
4742 case SCI_WORDRIGHT:
4743 case SCI_WORDRIGHTEXTEND:
4744 case SCI_WORDPARTLEFT:
4745 case SCI_WORDPARTLEFTEXTEND:
4746 case SCI_WORDPARTRIGHT:
4747 case SCI_WORDPARTRIGHTEXTEND:
4748 case SCI_WORDLEFTEND:
4749 case SCI_WORDLEFTENDEXTEND:
4750 case SCI_WORDRIGHTEND:
4751 case SCI_WORDRIGHTENDEXTEND:
4752 case SCI_HOME:
4753 case SCI_HOMEEXTEND:
4754 case SCI_LINEEND:
4755 case SCI_LINEENDEXTEND:
4756 case SCI_HOMEWRAP:
4757 case SCI_HOMEWRAPEXTEND:
4758 case SCI_LINEENDWRAP:
4759 case SCI_LINEENDWRAPEXTEND:
4760 case SCI_DOCUMENTSTART:
4761 case SCI_DOCUMENTSTARTEXTEND:
4762 case SCI_DOCUMENTEND:
4763 case SCI_DOCUMENTENDEXTEND:
4764 case SCI_STUTTEREDPAGEUP:
4765 case SCI_STUTTEREDPAGEUPEXTEND:
4766 case SCI_STUTTEREDPAGEDOWN:
4767 case SCI_STUTTEREDPAGEDOWNEXTEND:
4768 case SCI_PAGEUP:
4769 case SCI_PAGEUPEXTEND:
4770 case SCI_PAGEDOWN:
4771 case SCI_PAGEDOWNEXTEND:
4772 case SCI_EDITTOGGLEOVERTYPE:
4773 case SCI_CANCEL:
4774 case SCI_DELETEBACK:
4775 case SCI_TAB:
4776 case SCI_BACKTAB:
4777 case SCI_FORMFEED:
4778 case SCI_VCHOME:
4779 case SCI_VCHOMEEXTEND:
4780 case SCI_VCHOMEWRAP:
4781 case SCI_VCHOMEWRAPEXTEND:
4782 case SCI_VCHOMEDISPLAY:
4783 case SCI_VCHOMEDISPLAYEXTEND:
4784 case SCI_DELWORDLEFT:
4785 case SCI_DELWORDRIGHT:
4786 case SCI_DELWORDRIGHTEND:
4787 case SCI_DELLINELEFT:
4788 case SCI_DELLINERIGHT:
4789 case SCI_LINECOPY:
4790 case SCI_LINECUT:
4791 case SCI_LINEDELETE:
4792 case SCI_LINETRANSPOSE:
4793 case SCI_LINEDUPLICATE:
4794 case SCI_LOWERCASE:
4795 case SCI_UPPERCASE:
4796 case SCI_LINESCROLLDOWN:
4797 case SCI_LINESCROLLUP:
4798 case SCI_DELETEBACKNOTLINE:
4799 case SCI_HOMEDISPLAY:
4800 case SCI_HOMEDISPLAYEXTEND:
4801 case SCI_LINEENDDISPLAY:
4802 case SCI_LINEENDDISPLAYEXTEND:
4803 case SCI_SETSELECTIONMODE:
4804 case SCI_LINEDOWNRECTEXTEND:
4805 case SCI_LINEUPRECTEXTEND:
4806 case SCI_CHARLEFTRECTEXTEND:
4807 case SCI_CHARRIGHTRECTEXTEND:
4808 case SCI_HOMERECTEXTEND:
4809 case SCI_VCHOMERECTEXTEND:
4810 case SCI_LINEENDRECTEXTEND:
4811 case SCI_PAGEUPRECTEXTEND:
4812 case SCI_PAGEDOWNRECTEXTEND:
4813 case SCI_SELECTIONDUPLICATE:
4814 case SCI_COPYALLOWLINE:
4815 case SCI_VERTICALCENTRECARET:
4816 case SCI_MOVESELECTEDLINESUP:
4817 case SCI_MOVESELECTEDLINESDOWN:
4818 case SCI_SCROLLTOSTART:
4819 case SCI_SCROLLTOEND:
4820 break;
4822 // Filter out all others like display changes. Also, newlines are redundant
4823 // with char insert messages.
4824 case SCI_NEWLINE:
4825 default:
4826 // printf("Filtered out %ld of macro recording\n", iMessage);
4827 return ;
4830 // Send notification
4831 SCNotification scn = {0};
4832 scn.nmhdr.code = SCN_MACRORECORD;
4833 scn.message = iMessage;
4834 scn.wParam = wParam;
4835 scn.lParam = lParam;
4836 NotifyParent(scn);
4839 // Something has changed that the container should know about
4840 void Editor::ContainerNeedsUpdate(int flags) {
4841 needUpdateUI |= flags;
4845 * Force scroll and keep position relative to top of window.
4847 * If stuttered = true and not already at first/last row, move to first/last row of window.
4848 * If stuttered = true and already at first/last row, scroll as normal.
4850 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
4851 int topLineNew;
4852 SelectionPosition newPos;
4854 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
4855 int topStutterLine = topLine + caretYSlop;
4856 int bottomStutterLine =
4857 pdoc->LineFromPosition(PositionFromLocation(
4858 Point(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
4859 - caretYSlop - 1;
4861 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
4862 topLineNew = topLine;
4863 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
4864 false, false, UserVirtualSpace());
4866 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
4867 topLineNew = topLine;
4868 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
4869 false, false, UserVirtualSpace());
4871 } else {
4872 Point pt = LocationFromPosition(sel.MainCaret());
4874 topLineNew = Platform::Clamp(
4875 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
4876 newPos = SPositionFromLocation(
4877 Point(lastXChosen - xOffset, pt.y + direction * (vs.lineHeight * LinesToScroll())),
4878 false, false, UserVirtualSpace());
4881 if (topLineNew != topLine) {
4882 SetTopLine(topLineNew);
4883 MovePositionTo(newPos, selt);
4884 Redraw();
4885 SetVerticalScrollPos();
4886 } else {
4887 MovePositionTo(newPos, selt);
4891 void Editor::ChangeCaseOfSelection(int caseMapping) {
4892 UndoGroup ug(pdoc);
4893 for (size_t r=0; r<sel.Count(); r++) {
4894 SelectionRange current = sel.Range(r);
4895 SelectionRange currentNoVS = current;
4896 currentNoVS.ClearVirtualSpace();
4897 char *text = CopyRange(currentNoVS.Start().Position(), currentNoVS.End().Position());
4898 size_t rangeBytes = currentNoVS.Length();
4899 if (rangeBytes > 0) {
4900 std::string sText(text, rangeBytes);
4902 std::string sMapped = CaseMapString(sText, caseMapping);
4904 if (sMapped != sText) {
4905 size_t firstDifference = 0;
4906 while (sMapped[firstDifference] == sText[firstDifference])
4907 firstDifference++;
4908 size_t lastDifference = sMapped.size() - 1;
4909 while (sMapped[lastDifference] == sText[lastDifference])
4910 lastDifference--;
4911 size_t endSame = sMapped.size() - 1 - lastDifference;
4912 pdoc->DeleteChars(
4913 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
4914 static_cast<int>(rangeBytes - firstDifference - endSame));
4915 pdoc->InsertString(
4916 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
4917 sMapped.c_str() + firstDifference,
4918 static_cast<int>(lastDifference - firstDifference + 1));
4919 // Automatic movement changes selection so reset to exactly the same as it was.
4920 sel.Range(r) = current;
4923 delete []text;
4927 void Editor::LineTranspose() {
4928 int line = pdoc->LineFromPosition(sel.MainCaret());
4929 if (line > 0) {
4930 UndoGroup ug(pdoc);
4931 int startPrev = pdoc->LineStart(line - 1);
4932 int endPrev = pdoc->LineEnd(line - 1);
4933 int start = pdoc->LineStart(line);
4934 int end = pdoc->LineEnd(line);
4935 char *line1 = CopyRange(startPrev, endPrev);
4936 int len1 = endPrev - startPrev;
4937 char *line2 = CopyRange(start, end);
4938 int len2 = end - start;
4939 pdoc->DeleteChars(start, len2);
4940 pdoc->DeleteChars(startPrev, len1);
4941 pdoc->InsertString(startPrev, line2, len2);
4942 pdoc->InsertString(start - len1 + len2, line1, len1);
4943 MovePositionTo(SelectionPosition(start - len1 + len2));
4944 delete []line1;
4945 delete []line2;
4949 void Editor::Duplicate(bool forLine) {
4950 if (sel.Empty()) {
4951 forLine = true;
4953 UndoGroup ug(pdoc);
4954 const char *eol = "";
4955 int eolLen = 0;
4956 if (forLine) {
4957 eol = StringFromEOLMode(pdoc->eolMode);
4958 eolLen = istrlen(eol);
4960 for (size_t r=0; r<sel.Count(); r++) {
4961 SelectionPosition start = sel.Range(r).Start();
4962 SelectionPosition end = sel.Range(r).End();
4963 if (forLine) {
4964 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4965 start = SelectionPosition(pdoc->LineStart(line));
4966 end = SelectionPosition(pdoc->LineEnd(line));
4968 char *text = CopyRange(start.Position(), end.Position());
4969 if (forLine)
4970 pdoc->InsertString(end.Position(), eol, eolLen);
4971 pdoc->InsertString(end.Position() + eolLen, text, SelectionRange(end, start).Length());
4972 delete []text;
4974 if (sel.Count() && sel.IsRectangular()) {
4975 SelectionPosition last = sel.Last();
4976 if (forLine) {
4977 int line = pdoc->LineFromPosition(last.Position());
4978 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
4980 if (sel.Rectangular().anchor > sel.Rectangular().caret)
4981 sel.Rectangular().anchor = last;
4982 else
4983 sel.Rectangular().caret = last;
4984 SetRectangularRange();
4988 void Editor::CancelModes() {
4989 sel.SetMoveExtends(false);
4992 void Editor::NewLine() {
4993 // Remove non-main ranges
4994 InvalidateSelection(sel.RangeMain(), true);
4995 sel.SetSelection(sel.RangeMain());
4997 // Clear main range and insert line end
4998 bool needGroupUndo = !sel.Empty();
4999 if (needGroupUndo)
5000 pdoc->BeginUndoAction();
5002 if (!sel.Empty())
5003 ClearSelection();
5004 const char *eol = "\n";
5005 if (pdoc->eolMode == SC_EOL_CRLF) {
5006 eol = "\r\n";
5007 } else if (pdoc->eolMode == SC_EOL_CR) {
5008 eol = "\r";
5009 } // else SC_EOL_LF -> "\n" already set
5010 bool inserted = pdoc->InsertCString(sel.MainCaret(), eol);
5011 // Want to end undo group before NotifyChar as applications often modify text here
5012 if (needGroupUndo)
5013 pdoc->EndUndoAction();
5014 if (inserted) {
5015 SetEmptySelection(sel.MainCaret() + istrlen(eol));
5016 while (*eol) {
5017 NotifyChar(*eol);
5018 if (recordingMacro) {
5019 char txt[2];
5020 txt[0] = *eol;
5021 txt[1] = '\0';
5022 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
5024 eol++;
5027 SetLastXChosen();
5028 SetScrollBars();
5029 EnsureCaretVisible();
5030 // Avoid blinking during rapid typing:
5031 ShowCaretAtCurrentPosition();
5034 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
5035 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
5036 if (sel.IsRectangular()) {
5037 if (selt == Selection::noSel) {
5038 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
5039 } else {
5040 caretToUse = sel.Rectangular().caret;
5044 Point pt = LocationFromPosition(caretToUse);
5045 int skipLines = 0;
5047 if (vs.annotationVisible) {
5048 int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
5049 Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
5050 int subLine = (pt.y - ptStartLine.y) / vs.lineHeight;
5052 if (direction < 0 && subLine == 0) {
5053 int lineDisplay = cs.DisplayFromDoc(lineDoc);
5054 if (lineDisplay > 0) {
5055 skipLines = pdoc->AnnotationLines(cs.DocFromDisplay(lineDisplay - 1));
5057 } else if (direction > 0 && subLine >= (cs.GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
5058 skipLines = pdoc->AnnotationLines(lineDoc);
5062 int newY = pt.y + (1 + skipLines) * direction * vs.lineHeight;
5063 SelectionPosition posNew = SPositionFromLocation(
5064 Point(lastXChosen - xOffset, newY), false, false, UserVirtualSpace());
5066 if (direction < 0) {
5067 // Line wrapping may lead to a location on the same line, so
5068 // seek back if that is the case.
5069 Point ptNew = LocationFromPosition(posNew.Position());
5070 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
5071 posNew.Add(-1);
5072 posNew.SetVirtualSpace(0);
5073 ptNew = LocationFromPosition(posNew.Position());
5075 } else if (direction > 0 && posNew.Position() != pdoc->Length()) {
5076 // There is an equivalent case when moving down which skips
5077 // over a line.
5078 Point ptNew = LocationFromPosition(posNew.Position());
5079 while ((posNew.Position() > caretToUse.Position()) && (ptNew.y > newY)) {
5080 posNew.Add(-1);
5081 posNew.SetVirtualSpace(0);
5082 ptNew = LocationFromPosition(posNew.Position());
5086 MovePositionTo(MovePositionSoVisible(posNew, direction), selt);
5089 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
5090 int lineDoc, savedPos = sel.MainCaret();
5091 do {
5092 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
5093 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
5094 if (direction > 0) {
5095 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
5096 if (selt == Selection::noSel) {
5097 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
5099 break;
5102 } while (!cs.GetVisible(lineDoc));
5105 int Editor::StartEndDisplayLine(int pos, bool start) {
5106 RefreshStyleData();
5107 int line = pdoc->LineFromPosition(pos);
5108 AutoSurface surface(this);
5109 AutoLineLayout ll(llc, RetrieveLineLayout(line));
5110 int posRet = INVALID_POSITION;
5111 if (surface && ll) {
5112 unsigned int posLineStart = pdoc->LineStart(line);
5113 LayoutLine(line, surface, vs, ll, wrapWidth);
5114 int posInLine = pos - posLineStart;
5115 if (posInLine <= ll->maxLineLength) {
5116 for (int subLine = 0; subLine < ll->lines; subLine++) {
5117 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
5118 if (start) {
5119 posRet = ll->LineStart(subLine) + posLineStart;
5120 } else {
5121 if (subLine == ll->lines - 1)
5122 posRet = ll->LineStart(subLine + 1) + posLineStart;
5123 else
5124 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
5130 if (posRet == INVALID_POSITION) {
5131 return pos;
5132 } else {
5133 return posRet;
5137 int Editor::KeyCommand(unsigned int iMessage) {
5138 switch (iMessage) {
5139 case SCI_LINEDOWN:
5140 CursorUpOrDown(1);
5141 break;
5142 case SCI_LINEDOWNEXTEND:
5143 CursorUpOrDown(1, Selection::selStream);
5144 break;
5145 case SCI_LINEDOWNRECTEXTEND:
5146 CursorUpOrDown(1, Selection::selRectangle);
5147 break;
5148 case SCI_PARADOWN:
5149 ParaUpOrDown(1);
5150 break;
5151 case SCI_PARADOWNEXTEND:
5152 ParaUpOrDown(1, Selection::selStream);
5153 break;
5154 case SCI_LINESCROLLDOWN:
5155 ScrollTo(topLine + 1);
5156 MoveCaretInsideView(false);
5157 break;
5158 case SCI_LINEUP:
5159 CursorUpOrDown(-1);
5160 break;
5161 case SCI_LINEUPEXTEND:
5162 CursorUpOrDown(-1, Selection::selStream);
5163 break;
5164 case SCI_LINEUPRECTEXTEND:
5165 CursorUpOrDown(-1, Selection::selRectangle);
5166 break;
5167 case SCI_PARAUP:
5168 ParaUpOrDown(-1);
5169 break;
5170 case SCI_PARAUPEXTEND:
5171 ParaUpOrDown(-1, Selection::selStream);
5172 break;
5173 case SCI_LINESCROLLUP:
5174 ScrollTo(topLine - 1);
5175 MoveCaretInsideView(false);
5176 break;
5177 case SCI_CHARLEFT:
5178 if (SelectionEmpty() || sel.MoveExtends()) {
5179 if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5180 SelectionPosition spCaret = sel.RangeMain().caret;
5181 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5182 MovePositionTo(spCaret);
5183 } else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
5184 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1));
5185 } else {
5186 MovePositionTo(MovePositionSoVisible(
5187 SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
5189 } else {
5190 MovePositionTo(sel.LimitsForRectangularElseMain().start);
5192 SetLastXChosen();
5193 break;
5194 case SCI_CHARLEFTEXTEND:
5195 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5196 SelectionPosition spCaret = sel.RangeMain().caret;
5197 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5198 MovePositionTo(spCaret, Selection::selStream);
5199 } else {
5200 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
5202 SetLastXChosen();
5203 break;
5204 case SCI_CHARLEFTRECTEXTEND:
5205 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5206 SelectionPosition spCaret = sel.RangeMain().caret;
5207 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5208 MovePositionTo(spCaret, Selection::selRectangle);
5209 } else {
5210 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
5212 SetLastXChosen();
5213 break;
5214 case SCI_CHARRIGHT:
5215 if (SelectionEmpty() || sel.MoveExtends()) {
5216 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5217 SelectionPosition spCaret = sel.RangeMain().caret;
5218 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5219 MovePositionTo(spCaret);
5220 } else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
5221 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1));
5222 } else {
5223 MovePositionTo(MovePositionSoVisible(
5224 SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
5226 } else {
5227 MovePositionTo(sel.LimitsForRectangularElseMain().end);
5229 SetLastXChosen();
5230 break;
5231 case SCI_CHARRIGHTEXTEND:
5232 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5233 SelectionPosition spCaret = sel.RangeMain().caret;
5234 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5235 MovePositionTo(spCaret, Selection::selStream);
5236 } else {
5237 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
5239 SetLastXChosen();
5240 break;
5241 case SCI_CHARRIGHTRECTEXTEND:
5242 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5243 SelectionPosition spCaret = sel.RangeMain().caret;
5244 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5245 MovePositionTo(spCaret, Selection::selRectangle);
5246 } else {
5247 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
5249 SetLastXChosen();
5250 break;
5251 case SCI_WORDLEFT:
5252 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
5253 SetLastXChosen();
5254 break;
5255 case SCI_WORDLEFTEXTEND:
5256 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
5257 SetLastXChosen();
5258 break;
5259 case SCI_WORDRIGHT:
5260 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
5261 SetLastXChosen();
5262 break;
5263 case SCI_WORDRIGHTEXTEND:
5264 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
5265 SetLastXChosen();
5266 break;
5268 case SCI_WORDLEFTEND:
5269 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
5270 SetLastXChosen();
5271 break;
5272 case SCI_WORDLEFTENDEXTEND:
5273 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
5274 SetLastXChosen();
5275 break;
5276 case SCI_WORDRIGHTEND:
5277 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
5278 SetLastXChosen();
5279 break;
5280 case SCI_WORDRIGHTENDEXTEND:
5281 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
5282 SetLastXChosen();
5283 break;
5285 case SCI_HOME:
5286 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5287 SetLastXChosen();
5288 break;
5289 case SCI_HOMEEXTEND:
5290 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
5291 SetLastXChosen();
5292 break;
5293 case SCI_HOMERECTEXTEND:
5294 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
5295 SetLastXChosen();
5296 break;
5297 case SCI_LINEEND:
5298 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
5299 SetLastXChosen();
5300 break;
5301 case SCI_LINEENDEXTEND:
5302 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
5303 SetLastXChosen();
5304 break;
5305 case SCI_LINEENDRECTEXTEND:
5306 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
5307 SetLastXChosen();
5308 break;
5309 case SCI_HOMEWRAP: {
5310 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5311 if (sel.RangeMain().caret <= homePos)
5312 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5313 MovePositionTo(homePos);
5314 SetLastXChosen();
5316 break;
5317 case SCI_HOMEWRAPEXTEND: {
5318 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5319 if (sel.RangeMain().caret <= homePos)
5320 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5321 MovePositionTo(homePos, Selection::selStream);
5322 SetLastXChosen();
5324 break;
5325 case SCI_LINEENDWRAP: {
5326 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5327 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5328 if (endPos > realEndPos // if moved past visible EOLs
5329 || sel.RangeMain().caret >= endPos) // if at end of display line already
5330 endPos = realEndPos;
5331 MovePositionTo(endPos);
5332 SetLastXChosen();
5334 break;
5335 case SCI_LINEENDWRAPEXTEND: {
5336 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5337 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5338 if (endPos > realEndPos // if moved past visible EOLs
5339 || sel.RangeMain().caret >= endPos) // if at end of display line already
5340 endPos = realEndPos;
5341 MovePositionTo(endPos, Selection::selStream);
5342 SetLastXChosen();
5344 break;
5345 case SCI_DOCUMENTSTART:
5346 MovePositionTo(0);
5347 SetLastXChosen();
5348 break;
5349 case SCI_DOCUMENTSTARTEXTEND:
5350 MovePositionTo(0, Selection::selStream);
5351 SetLastXChosen();
5352 break;
5353 case SCI_DOCUMENTEND:
5354 MovePositionTo(pdoc->Length());
5355 SetLastXChosen();
5356 break;
5357 case SCI_DOCUMENTENDEXTEND:
5358 MovePositionTo(pdoc->Length(), Selection::selStream);
5359 SetLastXChosen();
5360 break;
5361 case SCI_STUTTEREDPAGEUP:
5362 PageMove(-1, Selection::noSel, true);
5363 break;
5364 case SCI_STUTTEREDPAGEUPEXTEND:
5365 PageMove(-1, Selection::selStream, true);
5366 break;
5367 case SCI_STUTTEREDPAGEDOWN:
5368 PageMove(1, Selection::noSel, true);
5369 break;
5370 case SCI_STUTTEREDPAGEDOWNEXTEND:
5371 PageMove(1, Selection::selStream, true);
5372 break;
5373 case SCI_PAGEUP:
5374 PageMove(-1);
5375 break;
5376 case SCI_PAGEUPEXTEND:
5377 PageMove(-1, Selection::selStream);
5378 break;
5379 case SCI_PAGEUPRECTEXTEND:
5380 PageMove(-1, Selection::selRectangle);
5381 break;
5382 case SCI_PAGEDOWN:
5383 PageMove(1);
5384 break;
5385 case SCI_PAGEDOWNEXTEND:
5386 PageMove(1, Selection::selStream);
5387 break;
5388 case SCI_PAGEDOWNRECTEXTEND:
5389 PageMove(1, Selection::selRectangle);
5390 break;
5391 case SCI_EDITTOGGLEOVERTYPE:
5392 inOverstrike = !inOverstrike;
5393 DropCaret();
5394 ShowCaretAtCurrentPosition();
5395 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
5396 NotifyUpdateUI();
5397 break;
5398 case SCI_CANCEL: // Cancel any modes - handled in subclass
5399 // Also unselect text
5400 CancelModes();
5401 break;
5402 case SCI_DELETEBACK:
5403 DelCharBack(true);
5404 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5405 SetLastXChosen();
5407 EnsureCaretVisible();
5408 break;
5409 case SCI_DELETEBACKNOTLINE:
5410 DelCharBack(false);
5411 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5412 SetLastXChosen();
5414 EnsureCaretVisible();
5415 break;
5416 case SCI_TAB:
5417 Indent(true);
5418 if (caretSticky == SC_CARETSTICKY_OFF) {
5419 SetLastXChosen();
5421 EnsureCaretVisible();
5422 ShowCaretAtCurrentPosition(); // Avoid blinking
5423 break;
5424 case SCI_BACKTAB:
5425 Indent(false);
5426 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5427 SetLastXChosen();
5429 EnsureCaretVisible();
5430 ShowCaretAtCurrentPosition(); // Avoid blinking
5431 break;
5432 case SCI_NEWLINE:
5433 NewLine();
5434 break;
5435 case SCI_FORMFEED:
5436 AddChar('\f');
5437 break;
5438 case SCI_VCHOME:
5439 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
5440 SetLastXChosen();
5441 break;
5442 case SCI_VCHOMEEXTEND:
5443 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
5444 SetLastXChosen();
5445 break;
5446 case SCI_VCHOMERECTEXTEND:
5447 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
5448 SetLastXChosen();
5449 break;
5450 case SCI_VCHOMEWRAP: {
5451 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5452 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5453 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5454 homePos = viewLineStart;
5456 MovePositionTo(homePos);
5457 SetLastXChosen();
5459 break;
5460 case SCI_VCHOMEWRAPEXTEND: {
5461 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5462 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5463 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5464 homePos = viewLineStart;
5466 MovePositionTo(homePos, Selection::selStream);
5467 SetLastXChosen();
5469 break;
5470 case SCI_ZOOMIN:
5471 if (vs.zoomLevel < 20) {
5472 vs.zoomLevel++;
5473 InvalidateStyleRedraw();
5474 NotifyZoom();
5476 break;
5477 case SCI_ZOOMOUT:
5478 if (vs.zoomLevel > -10) {
5479 vs.zoomLevel--;
5480 InvalidateStyleRedraw();
5481 NotifyZoom();
5483 break;
5484 case SCI_DELWORDLEFT: {
5485 int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
5486 pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
5487 sel.RangeMain().ClearVirtualSpace();
5488 SetLastXChosen();
5490 break;
5491 case SCI_DELWORDRIGHT: {
5492 UndoGroup ug(pdoc);
5493 sel.RangeMain().caret = SelectionPosition(
5494 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5495 sel.RangeMain().anchor = sel.RangeMain().caret;
5496 int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
5497 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5499 break;
5500 case SCI_DELWORDRIGHTEND: {
5501 UndoGroup ug(pdoc);
5502 sel.RangeMain().caret = SelectionPosition(
5503 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5504 int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
5505 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5507 break;
5508 case SCI_DELLINELEFT: {
5509 int line = pdoc->LineFromPosition(sel.MainCaret());
5510 int start = pdoc->LineStart(line);
5511 pdoc->DeleteChars(start, sel.MainCaret() - start);
5512 sel.RangeMain().ClearVirtualSpace();
5513 SetLastXChosen();
5515 break;
5516 case SCI_DELLINERIGHT: {
5517 int line = pdoc->LineFromPosition(sel.MainCaret());
5518 int end = pdoc->LineEnd(line);
5519 pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
5521 break;
5522 case SCI_LINECOPY: {
5523 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5524 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5525 CopyRangeToClipboard(pdoc->LineStart(lineStart),
5526 pdoc->LineStart(lineEnd + 1));
5528 break;
5529 case SCI_LINECUT: {
5530 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5531 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5532 int start = pdoc->LineStart(lineStart);
5533 int end = pdoc->LineStart(lineEnd + 1);
5534 SetSelection(start, end);
5535 Cut();
5536 SetLastXChosen();
5538 break;
5539 case SCI_LINEDELETE: {
5540 int line = pdoc->LineFromPosition(sel.MainCaret());
5541 int start = pdoc->LineStart(line);
5542 int end = pdoc->LineStart(line + 1);
5543 pdoc->DeleteChars(start, end - start);
5545 break;
5546 case SCI_LINETRANSPOSE:
5547 LineTranspose();
5548 break;
5549 case SCI_LINEDUPLICATE:
5550 Duplicate(true);
5551 break;
5552 case SCI_SELECTIONDUPLICATE:
5553 Duplicate(false);
5554 break;
5555 case SCI_LOWERCASE:
5556 ChangeCaseOfSelection(cmLower);
5557 break;
5558 case SCI_UPPERCASE:
5559 ChangeCaseOfSelection(cmUpper);
5560 break;
5561 case SCI_WORDPARTLEFT:
5562 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
5563 SetLastXChosen();
5564 break;
5565 case SCI_WORDPARTLEFTEXTEND:
5566 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
5567 SetLastXChosen();
5568 break;
5569 case SCI_WORDPARTRIGHT:
5570 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
5571 SetLastXChosen();
5572 break;
5573 case SCI_WORDPARTRIGHTEXTEND:
5574 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
5575 SetLastXChosen();
5576 break;
5577 case SCI_HOMEDISPLAY:
5578 MovePositionTo(MovePositionSoVisible(
5579 StartEndDisplayLine(sel.MainCaret(), true), -1));
5580 SetLastXChosen();
5581 break;
5582 case SCI_VCHOMEDISPLAY: {
5583 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5584 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5585 if (viewLineStart > homePos)
5586 homePos = viewLineStart;
5588 MovePositionTo(homePos);
5589 SetLastXChosen();
5591 break;
5592 case SCI_HOMEDISPLAYEXTEND:
5593 MovePositionTo(MovePositionSoVisible(
5594 StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
5595 SetLastXChosen();
5596 break;
5597 case SCI_VCHOMEDISPLAYEXTEND: {
5598 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5599 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5600 if (viewLineStart > homePos)
5601 homePos = viewLineStart;
5603 MovePositionTo(homePos, Selection::selStream);
5604 SetLastXChosen();
5606 break;
5607 case SCI_LINEENDDISPLAY:
5608 MovePositionTo(MovePositionSoVisible(
5609 StartEndDisplayLine(sel.MainCaret(), false), 1));
5610 SetLastXChosen();
5611 break;
5612 case SCI_LINEENDDISPLAYEXTEND:
5613 MovePositionTo(MovePositionSoVisible(
5614 StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
5615 SetLastXChosen();
5616 break;
5617 case SCI_SCROLLTOSTART:
5618 ScrollTo(0);
5619 break;
5620 case SCI_SCROLLTOEND:
5621 ScrollTo(MaxScrollPos());
5622 break;
5624 return 0;
5627 int Editor::KeyDefault(int, int) {
5628 return 0;
5631 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
5632 DwellEnd(false);
5633 int msg = kmap.Find(key, modifiers);
5634 if (msg) {
5635 if (consumed)
5636 *consumed = true;
5637 return WndProc(msg, 0, 0);
5638 } else {
5639 if (consumed)
5640 *consumed = false;
5641 return KeyDefault(key, modifiers);
5645 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
5646 int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
5647 (alt ? SCI_ALT : 0);
5648 return KeyDownWithModifiers(key, modifiers, consumed);
5651 void Editor::Indent(bool forwards) {
5652 for (size_t r=0; r<sel.Count(); r++) {
5653 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
5654 int caretPosition = sel.Range(r).caret.Position();
5655 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
5656 if (lineOfAnchor == lineCurrentPos) {
5657 if (forwards) {
5658 UndoGroup ug(pdoc);
5659 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
5660 caretPosition = sel.Range(r).caret.Position();
5661 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
5662 pdoc->tabIndents) {
5663 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5664 int indentationStep = pdoc->IndentSize();
5665 pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
5666 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5667 } else {
5668 if (pdoc->useTabs) {
5669 pdoc->InsertChar(caretPosition, '\t');
5670 sel.Range(r) = SelectionRange(caretPosition+1);
5671 } else {
5672 int numSpaces = (pdoc->tabInChars) -
5673 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
5674 if (numSpaces < 1)
5675 numSpaces = pdoc->tabInChars;
5676 for (int i = 0; i < numSpaces; i++) {
5677 pdoc->InsertChar(caretPosition + i, ' ');
5679 sel.Range(r) = SelectionRange(caretPosition+numSpaces);
5682 } else {
5683 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
5684 pdoc->tabIndents) {
5685 UndoGroup ug(pdoc);
5686 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5687 int indentationStep = pdoc->IndentSize();
5688 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
5689 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5690 } else {
5691 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
5692 pdoc->tabInChars;
5693 if (newColumn < 0)
5694 newColumn = 0;
5695 int newPos = caretPosition;
5696 while (pdoc->GetColumn(newPos) > newColumn)
5697 newPos--;
5698 sel.Range(r) = SelectionRange(newPos);
5701 } else { // Multiline
5702 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
5703 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
5704 // Multiple lines selected so indent / dedent
5705 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
5706 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
5707 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
5708 lineBottomSel--; // If not selecting any characters on a line, do not indent
5710 UndoGroup ug(pdoc);
5711 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
5713 if (lineOfAnchor < lineCurrentPos) {
5714 if (currentPosPosOnLine == 0)
5715 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5716 else
5717 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
5718 } else {
5719 if (anchorPosOnLine == 0)
5720 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5721 else
5722 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
5728 class CaseFolderASCII : public CaseFolderTable {
5729 public:
5730 CaseFolderASCII() {
5731 StandardASCII();
5733 ~CaseFolderASCII() {
5738 CaseFolder *Editor::CaseFolderForEncoding() {
5739 // Simple default that only maps ASCII upper case to lower case.
5740 return new CaseFolderASCII();
5744 * Search of a text in the document, in the given range.
5745 * @return The position of the found text, -1 if not found.
5747 long Editor::FindText(
5748 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5749 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5750 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
5752 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
5753 int lengthFound = istrlen(ft->lpstrText);
5754 if (!pdoc->HasCaseFolder())
5755 pdoc->SetCaseFolder(CaseFolderForEncoding());
5756 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
5757 (wParam & SCFIND_MATCHCASE) != 0,
5758 (wParam & SCFIND_WHOLEWORD) != 0,
5759 (wParam & SCFIND_WORDSTART) != 0,
5760 (wParam & SCFIND_REGEXP) != 0,
5761 wParam,
5762 &lengthFound);
5763 if (pos != -1) {
5764 ft->chrgText.cpMin = pos;
5765 ft->chrgText.cpMax = pos + lengthFound;
5767 return pos;
5771 * Relocatable search support : Searches relative to current selection
5772 * point and sets the selection to the found text range with
5773 * each search.
5776 * Anchor following searches at current selection start: This allows
5777 * multiple incremental interactive searches to be macro recorded
5778 * while still setting the selection to found text so the find/select
5779 * operation is self-contained.
5781 void Editor::SearchAnchor() {
5782 searchAnchor = SelectionStart().Position();
5786 * Find text from current search anchor: Must call @c SearchAnchor first.
5787 * Used for next text and previous text requests.
5788 * @return The position of the found text, -1 if not found.
5790 long Editor::SearchText(
5791 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
5792 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5793 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5794 sptr_t lParam) { ///< The text to search for.
5796 const char *txt = reinterpret_cast<char *>(lParam);
5797 int pos;
5798 int lengthFound = istrlen(txt);
5799 if (!pdoc->HasCaseFolder())
5800 pdoc->SetCaseFolder(CaseFolderForEncoding());
5801 if (iMessage == SCI_SEARCHNEXT) {
5802 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
5803 (wParam & SCFIND_MATCHCASE) != 0,
5804 (wParam & SCFIND_WHOLEWORD) != 0,
5805 (wParam & SCFIND_WORDSTART) != 0,
5806 (wParam & SCFIND_REGEXP) != 0,
5807 wParam,
5808 &lengthFound);
5809 } else {
5810 pos = pdoc->FindText(searchAnchor, 0, txt,
5811 (wParam & SCFIND_MATCHCASE) != 0,
5812 (wParam & SCFIND_WHOLEWORD) != 0,
5813 (wParam & SCFIND_WORDSTART) != 0,
5814 (wParam & SCFIND_REGEXP) != 0,
5815 wParam,
5816 &lengthFound);
5818 if (pos != -1) {
5819 SetSelection(pos, pos + lengthFound);
5822 return pos;
5825 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
5826 std::string ret(s);
5827 for (size_t i=0; i<ret.size(); i++) {
5828 switch (caseMapping) {
5829 case cmUpper:
5830 if (ret[i] >= 'a' && ret[i] <= 'z')
5831 ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
5832 break;
5833 case cmLower:
5834 if (ret[i] >= 'A' && ret[i] <= 'Z')
5835 ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
5836 break;
5839 return ret;
5843 * Search for text in the target range of the document.
5844 * @return The position of the found text, -1 if not found.
5846 long Editor::SearchInTarget(const char *text, int length) {
5847 int lengthFound = length;
5849 if (!pdoc->HasCaseFolder())
5850 pdoc->SetCaseFolder(CaseFolderForEncoding());
5851 int pos = pdoc->FindText(targetStart, targetEnd, text,
5852 (searchFlags & SCFIND_MATCHCASE) != 0,
5853 (searchFlags & SCFIND_WHOLEWORD) != 0,
5854 (searchFlags & SCFIND_WORDSTART) != 0,
5855 (searchFlags & SCFIND_REGEXP) != 0,
5856 searchFlags,
5857 &lengthFound);
5858 if (pos != -1) {
5859 targetStart = pos;
5860 targetEnd = pos + lengthFound;
5862 return pos;
5865 void Editor::GoToLine(int lineNo) {
5866 if (lineNo > pdoc->LinesTotal())
5867 lineNo = pdoc->LinesTotal();
5868 if (lineNo < 0)
5869 lineNo = 0;
5870 SetEmptySelection(pdoc->LineStart(lineNo));
5871 ShowCaretAtCurrentPosition();
5872 EnsureCaretVisible();
5875 static bool Close(Point pt1, Point pt2) {
5876 if (abs(pt1.x - pt2.x) > 3)
5877 return false;
5878 if (abs(pt1.y - pt2.y) > 3)
5879 return false;
5880 return true;
5883 char *Editor::CopyRange(int start, int end) {
5884 char *text = 0;
5885 if (start < end) {
5886 int len = end - start;
5887 text = new char[len + 1];
5888 for (int i = 0; i < len; i++) {
5889 text[i] = pdoc->CharAt(start + i);
5891 text[len] = '\0';
5893 return text;
5896 std::string Editor::RangeText(int start, int end) const {
5897 if (start < end) {
5898 int len = end - start;
5899 std::string ret(len, '\0');
5900 for (int i = 0; i < len; i++) {
5901 ret[i] = pdoc->CharAt(start + i);
5903 return ret;
5905 return std::string();
5908 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
5909 if (sel.Empty()) {
5910 if (allowLineCopy) {
5911 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
5912 int start = pdoc->LineStart(currentLine);
5913 int end = pdoc->LineEnd(currentLine);
5915 char *text = CopyRange(start, end);
5916 size_t textLen = text ? strlen(text) : 0;
5917 // include room for \r\n\0
5918 textLen += 3;
5919 char *textWithEndl = new char[textLen];
5920 textWithEndl[0] = '\0';
5921 if (text)
5922 strcat(textWithEndl, text);
5923 if (pdoc->eolMode != SC_EOL_LF)
5924 strcat(textWithEndl, "\r");
5925 if (pdoc->eolMode != SC_EOL_CR)
5926 strcat(textWithEndl, "\n");
5927 ss->Set(textWithEndl, static_cast<int>(strlen(textWithEndl) + 1),
5928 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, true);
5929 delete []text;
5931 } else {
5932 int delimiterLength = 0;
5933 if (sel.selType == Selection::selRectangle) {
5934 if (pdoc->eolMode == SC_EOL_CRLF) {
5935 delimiterLength = 2;
5936 } else {
5937 delimiterLength = 1;
5940 size_t size = sel.Length() + delimiterLength * sel.Count();
5941 char *text = new char[size + 1];
5942 int j = 0;
5943 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
5944 if (sel.selType == Selection::selRectangle)
5945 std::sort(rangesInOrder.begin(), rangesInOrder.end());
5946 for (size_t r=0; r<rangesInOrder.size(); r++) {
5947 SelectionRange current = rangesInOrder[r];
5948 for (int i = current.Start().Position();
5949 i < current.End().Position();
5950 i++) {
5951 text[j++] = pdoc->CharAt(i);
5953 if (sel.selType == Selection::selRectangle) {
5954 if (pdoc->eolMode != SC_EOL_LF) {
5955 text[j++] = '\r';
5957 if (pdoc->eolMode != SC_EOL_CR) {
5958 text[j++] = '\n';
5962 text[size] = '\0';
5963 ss->Set(text, static_cast<int>(size + 1), pdoc->dbcsCodePage,
5964 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
5968 void Editor::CopyRangeToClipboard(int start, int end) {
5969 start = pdoc->ClampPositionIntoDocument(start);
5970 end = pdoc->ClampPositionIntoDocument(end);
5971 SelectionText selectedText;
5972 selectedText.Set(CopyRange(start, end), end - start + 1,
5973 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5974 CopyToClipboard(selectedText);
5977 void Editor::CopyText(int length, const char *text) {
5978 SelectionText selectedText;
5979 selectedText.Copy(text, length + 1,
5980 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5981 CopyToClipboard(selectedText);
5984 void Editor::SetDragPosition(SelectionPosition newPos) {
5985 if (newPos.Position() >= 0) {
5986 newPos = MovePositionOutsideChar(newPos, 1);
5987 posDrop = newPos;
5989 if (!(posDrag == newPos)) {
5990 caret.on = true;
5991 SetTicking(true);
5992 InvalidateCaret();
5993 posDrag = newPos;
5994 InvalidateCaret();
5998 void Editor::DisplayCursor(Window::Cursor c) {
5999 if (cursorMode == SC_CURSORNORMAL)
6000 wMain.SetCursor(c);
6001 else
6002 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
6005 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
6006 int xMove = ptStart.x - ptNow.x;
6007 int yMove = ptStart.y - ptNow.y;
6008 int distanceSquared = xMove * xMove + yMove * yMove;
6009 return distanceSquared > 16;
6012 void Editor::StartDrag() {
6013 // Always handled by subclasses
6014 //SetMouseCapture(true);
6015 //DisplayCursor(Window::cursorArrow);
6018 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
6019 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
6020 if (inDragDrop == ddDragging)
6021 dropWentOutside = false;
6023 bool positionWasInSelection = PositionInSelection(position.Position());
6025 bool positionOnEdgeOfSelection =
6026 (position == SelectionStart()) || (position == SelectionEnd());
6028 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
6029 (positionOnEdgeOfSelection && !moving)) {
6031 SelectionPosition selStart = SelectionStart();
6032 SelectionPosition selEnd = SelectionEnd();
6034 UndoGroup ug(pdoc);
6036 SelectionPosition positionAfterDeletion = position;
6037 if ((inDragDrop == ddDragging) && moving) {
6038 // Remove dragged out text
6039 if (rectangular || sel.selType == Selection::selLines) {
6040 for (size_t r=0; r<sel.Count(); r++) {
6041 if (position >= sel.Range(r).Start()) {
6042 if (position > sel.Range(r).End()) {
6043 positionAfterDeletion.Add(-sel.Range(r).Length());
6044 } else {
6045 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
6049 } else {
6050 if (position > selStart) {
6051 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
6054 ClearSelection();
6056 position = positionAfterDeletion;
6058 if (rectangular) {
6059 PasteRectangular(position, value, istrlen(value));
6060 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
6061 SetEmptySelection(position);
6062 } else {
6063 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
6064 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
6065 if (pdoc->InsertCString(position.Position(), value)) {
6066 SelectionPosition posAfterInsertion = position;
6067 posAfterInsertion.Add(istrlen(value));
6068 SetSelection(posAfterInsertion, position);
6071 } else if (inDragDrop == ddDragging) {
6072 SetEmptySelection(position);
6077 * @return true if given position is inside the selection,
6079 bool Editor::PositionInSelection(int pos) {
6080 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
6081 for (size_t r=0; r<sel.Count(); r++) {
6082 if (sel.Range(r).Contains(pos))
6083 return true;
6085 return false;
6088 bool Editor::PointInSelection(Point pt) {
6089 SelectionPosition pos = SPositionFromLocation(pt, false, true);
6090 Point ptPos = LocationFromPosition(pos);
6091 for (size_t r=0; r<sel.Count(); r++) {
6092 SelectionRange range = sel.Range(r);
6093 if (range.Contains(pos)) {
6094 bool hit = true;
6095 if (pos == range.Start()) {
6096 // see if just before selection
6097 if (pt.x < ptPos.x) {
6098 hit = false;
6101 if (pos == range.End()) {
6102 // see if just after selection
6103 if (pt.x > ptPos.x) {
6104 hit = false;
6107 if (hit)
6108 return true;
6111 return false;
6114 bool Editor::PointInSelMargin(Point pt) {
6115 // Really means: "Point in a margin"
6116 if (vs.fixedColumnWidth > 0) { // There is a margin
6117 PRectangle rcSelMargin = GetClientRectangle();
6118 rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth;
6119 return rcSelMargin.Contains(pt);
6120 } else {
6121 return false;
6125 Window::Cursor Editor::GetMarginCursor(Point pt) {
6126 int x = 0;
6127 for (int margin = 0; margin < ViewStyle::margins; margin++) {
6128 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
6129 return static_cast<Window::Cursor>(vs.ms[margin].cursor);
6130 x += vs.ms[margin].width;
6132 return Window::cursorReverseArrow;
6135 void Editor::TrimAndSetSelection(int currentPos_, int anchor_) {
6136 sel.TrimSelection(SelectionRange(currentPos_, anchor_));
6137 SetSelection(currentPos_, anchor_);
6140 void Editor::LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine) {
6141 int selCurrentPos, selAnchorPos;
6142 if (wholeLine) {
6143 int lineCurrent_ = pdoc->LineFromPosition(lineCurrentPos_);
6144 int lineAnchor_ = pdoc->LineFromPosition(lineAnchorPos_);
6145 if (lineAnchorPos_ < lineCurrentPos_) {
6146 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
6147 selAnchorPos = pdoc->LineStart(lineAnchor_);
6148 } else if (lineAnchorPos_ > lineCurrentPos_) {
6149 selCurrentPos = pdoc->LineStart(lineCurrent_);
6150 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
6151 } else { // Same line, select it
6152 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
6153 selAnchorPos = pdoc->LineStart(lineAnchor_);
6155 } else {
6156 if (lineAnchorPos_ < lineCurrentPos_) {
6157 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
6158 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6159 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6160 } else if (lineAnchorPos_ > lineCurrentPos_) {
6161 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
6162 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6163 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
6164 } else { // Same line, select it
6165 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6166 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6167 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6170 TrimAndSetSelection(selCurrentPos, selAnchorPos);
6173 void Editor::WordSelection(int pos) {
6174 if (pos < wordSelectAnchorStartPos) {
6175 // Extend backward to the word containing pos.
6176 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
6177 // This ensures that a series of empty lines isn't counted as a single "word".
6178 if (!pdoc->IsLineEndPosition(pos))
6179 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
6180 TrimAndSetSelection(pos, wordSelectAnchorEndPos);
6181 } else if (pos > wordSelectAnchorEndPos) {
6182 // Extend forward to the word containing the character to the left of pos.
6183 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
6184 // This ensures that a series of empty lines isn't counted as a single "word".
6185 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
6186 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
6187 TrimAndSetSelection(pos, wordSelectAnchorStartPos);
6188 } else {
6189 // Select only the anchored word
6190 if (pos >= originalAnchorPos)
6191 TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
6192 else
6193 TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
6197 void Editor::DwellEnd(bool mouseMoved) {
6198 if (mouseMoved)
6199 ticksToDwell = dwellDelay;
6200 else
6201 ticksToDwell = SC_TIME_FOREVER;
6202 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
6203 dwelling = false;
6204 NotifyDwelling(ptMouseLast, dwelling);
6208 void Editor::MouseLeave() {
6209 SetHotSpotRange(NULL);
6210 if (!HaveMouseCapture()) {
6211 ptMouseLast = Point(-1,-1);
6212 DwellEnd(true);
6216 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
6217 return (!rectangular && ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0))
6218 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
6221 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
6222 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
6223 ptMouseLast = pt;
6224 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
6225 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6226 inDragDrop = ddNone;
6227 sel.SetMoveExtends(false);
6229 if (NotifyMarginClick(pt, shift, ctrl, alt))
6230 return;
6232 NotifyIndicatorClick(true, newPos.Position(), shift, ctrl, alt);
6234 bool inSelMargin = PointInSelMargin(pt);
6235 // In margin ctrl+(double)click should always select everything
6236 if (ctrl && inSelMargin) {
6237 SelectAll();
6238 lastClickTime = curTime;
6239 lastClick = pt;
6240 return;
6242 if (shift && !inSelMargin) {
6243 SetSelection(newPos);
6245 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
6246 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
6247 SetMouseCapture(true);
6248 if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))
6249 SetEmptySelection(newPos.Position());
6250 bool doubleClick = false;
6251 // Stop mouse button bounce changing selection type
6252 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
6253 if (inSelMargin) {
6254 // Inside margin selection type should be either selSubLine or selWholeLine.
6255 if (selectionType == selSubLine) {
6256 // If it is selSubLine, we're inside a *double* click and word wrap is enabled,
6257 // so we switch to selWholeLine in order to select whole line.
6258 selectionType = selWholeLine;
6259 } else if (selectionType != selSubLine && selectionType != selWholeLine) {
6260 // If it is neither, reset selection type to line selection.
6261 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6263 } else {
6264 if (selectionType == selChar) {
6265 selectionType = selWord;
6266 doubleClick = true;
6267 } else if (selectionType == selWord) {
6268 // Since we ended up here, we're inside a *triple* click, which should always select
6269 // whole line irregardless of word wrap being enabled or not.
6270 selectionType = selWholeLine;
6271 } else {
6272 selectionType = selChar;
6273 originalAnchorPos = sel.MainCaret();
6278 if (selectionType == selWord) {
6279 int charPos = originalAnchorPos;
6280 if (sel.MainCaret() == originalAnchorPos) {
6281 charPos = PositionFromLocation(pt, false, true);
6282 charPos = MovePositionOutsideChar(charPos, -1);
6285 int startWord, endWord;
6286 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
6287 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
6288 endWord = pdoc->ExtendWordSelect(charPos, 1);
6289 } else {
6290 // Selecting backwards, or anchor beyond last character on line. In these cases,
6291 // we select the word containing the character to the *left* of the anchor.
6292 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
6293 startWord = pdoc->ExtendWordSelect(charPos, -1);
6294 endWord = pdoc->ExtendWordSelect(startWord, 1);
6295 } else {
6296 // Anchor at start of line; select nothing to begin with.
6297 startWord = charPos;
6298 endWord = charPos;
6302 wordSelectAnchorStartPos = startWord;
6303 wordSelectAnchorEndPos = endWord;
6304 wordSelectInitialCaretPos = sel.MainCaret();
6305 WordSelection(wordSelectInitialCaretPos);
6306 } else if (selectionType == selSubLine || selectionType == selWholeLine) {
6307 lineAnchorPos = newPos.Position();
6308 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6309 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
6310 } else {
6311 SetEmptySelection(sel.MainCaret());
6313 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
6314 if (doubleClick) {
6315 NotifyDoubleClick(pt, shift, ctrl, alt);
6316 if (PositionIsHotspot(newPos.Position()))
6317 NotifyHotSpotDoubleClicked(newPos.Position(), shift, ctrl, alt);
6319 } else { // Single click
6320 if (inSelMargin) {
6321 sel.selType = Selection::selStream;
6322 if (!shift) {
6323 // Single click in margin: select whole line or only subline if word wrap is enabled
6324 lineAnchorPos = newPos.Position();
6325 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6326 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6327 } else {
6328 // Single shift+click in margin: select from line anchor to clicked line
6329 if (sel.MainAnchor() > sel.MainCaret())
6330 lineAnchorPos = sel.MainAnchor() - 1;
6331 else
6332 lineAnchorPos = sel.MainAnchor();
6333 // Reset selection type if there is an empty selection.
6334 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
6335 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
6336 // This ensures that we continue selecting in the same selection mode.
6337 if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
6338 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6339 LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
6342 SetDragPosition(SelectionPosition(invalidPosition));
6343 SetMouseCapture(true);
6344 } else {
6345 if (PointIsHotspot(pt)) {
6346 NotifyHotSpotClicked(newPos.Position(), shift, ctrl, alt);
6347 hotSpotClickPos = PositionFromLocation(pt,true,false);
6349 if (!shift) {
6350 if (PointInSelection(pt) && !SelectionEmpty())
6351 inDragDrop = ddInitial;
6352 else
6353 inDragDrop = ddNone;
6355 SetMouseCapture(true);
6356 if (inDragDrop != ddInitial) {
6357 SetDragPosition(SelectionPosition(invalidPosition));
6358 if (!shift) {
6359 if (ctrl && multipleSelection) {
6360 SelectionRange range(newPos);
6361 sel.TentativeSelection(range);
6362 InvalidateSelection(range, true);
6363 } else {
6364 InvalidateSelection(SelectionRange(newPos), true);
6365 if (sel.Count() > 1)
6366 Redraw();
6367 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
6368 sel.Clear();
6369 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6370 SetSelection(newPos, newPos);
6373 SelectionPosition anchorCurrent = newPos;
6374 if (shift)
6375 anchorCurrent = sel.IsRectangular() ?
6376 sel.Rectangular().anchor : sel.RangeMain().anchor;
6377 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6378 selectionType = selChar;
6379 originalAnchorPos = sel.MainCaret();
6380 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
6381 SetRectangularRange();
6385 lastClickTime = curTime;
6386 lastClick = pt;
6387 lastXChosen = pt.x + xOffset;
6388 ShowCaretAtCurrentPosition();
6391 bool Editor::PositionIsHotspot(int position) {
6392 return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
6395 bool Editor::PointIsHotspot(Point pt) {
6396 int pos = PositionFromLocation(pt, true);
6397 if (pos == INVALID_POSITION)
6398 return false;
6399 return PositionIsHotspot(pos);
6402 void Editor::SetHotSpotRange(Point *pt) {
6403 if (pt) {
6404 int pos = PositionFromLocation(*pt);
6406 // If we don't limit this to word characters then the
6407 // range can encompass more than the run range and then
6408 // the underline will not be drawn properly.
6409 int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
6410 int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
6412 // Only invalidate the range if the hotspot range has changed...
6413 if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
6414 if (hsStart != -1) {
6415 InvalidateRange(hsStart, hsEnd);
6417 hsStart = hsStart_;
6418 hsEnd = hsEnd_;
6419 InvalidateRange(hsStart, hsEnd);
6421 } else {
6422 if (hsStart != -1) {
6423 int hsStart_ = hsStart;
6424 int hsEnd_ = hsEnd;
6425 hsStart = -1;
6426 hsEnd = -1;
6427 InvalidateRange(hsStart_, hsEnd_);
6428 } else {
6429 hsStart = -1;
6430 hsEnd = -1;
6435 void Editor::GetHotSpotRange(int &hsStart_, int &hsEnd_) {
6436 hsStart_ = hsStart;
6437 hsEnd_ = hsEnd;
6440 void Editor::ButtonMove(Point pt) {
6441 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
6442 DwellEnd(true);
6445 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
6446 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6447 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
6449 if (inDragDrop == ddInitial) {
6450 if (DragThreshold(ptMouseLast, pt)) {
6451 SetMouseCapture(false);
6452 SetDragPosition(movePos);
6453 CopySelectionRange(&drag);
6454 StartDrag();
6456 return;
6459 ptMouseLast = pt;
6460 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
6461 if (HaveMouseCapture()) {
6463 // Slow down autoscrolling/selection
6464 autoScrollTimer.ticksToWait -= timer.tickSize;
6465 if (autoScrollTimer.ticksToWait > 0)
6466 return;
6467 autoScrollTimer.ticksToWait = autoScrollDelay;
6469 // Adjust selection
6470 if (posDrag.IsValid()) {
6471 SetDragPosition(movePos);
6472 } else {
6473 if (selectionType == selChar) {
6474 if (sel.IsRectangular()) {
6475 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
6476 SetSelection(movePos, sel.RangeMain().anchor);
6477 } else if (sel.Count() > 1) {
6478 SelectionRange range(movePos, sel.RangeMain().anchor);
6479 sel.TentativeSelection(range);
6480 InvalidateSelection(range, true);
6481 } else {
6482 SetSelection(movePos, sel.RangeMain().anchor);
6484 } else if (selectionType == selWord) {
6485 // Continue selecting by word
6486 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
6487 // No need to do anything. Previously this case was lumped
6488 // in with "Moved forward", but that can be harmful in this
6489 // case: a handler for the NotifyDoubleClick re-adjusts
6490 // the selection for a fancier definition of "word" (for
6491 // example, in Perl it is useful to include the leading
6492 // '$', '%' or '@' on variables for word selection). In this
6493 // the ButtonMove() called via Tick() for auto-scrolling
6494 // could result in the fancier word selection adjustment
6495 // being unmade.
6496 } else {
6497 wordSelectInitialCaretPos = -1;
6498 WordSelection(movePos.Position());
6500 } else {
6501 // Continue selecting by line
6502 LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
6506 // Autoscroll
6507 PRectangle rcClient = GetClientRectangle();
6508 int lineMove = DisplayFromPosition(movePos.Position());
6509 if (pt.y > rcClient.bottom) {
6510 ScrollTo(lineMove - LinesOnScreen() + 1);
6511 Redraw();
6512 } else if (pt.y < rcClient.top) {
6513 ScrollTo(lineMove);
6514 Redraw();
6516 EnsureCaretVisible(false, false, true);
6518 if (hsStart != -1 && !PositionIsHotspot(movePos.Position()))
6519 SetHotSpotRange(NULL);
6521 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,false) != hotSpotClickPos) {
6522 if (inDragDrop == ddNone) {
6523 DisplayCursor(Window::cursorText);
6525 hotSpotClickPos = INVALID_POSITION;
6528 } else {
6529 if (vs.fixedColumnWidth > 0) { // There is a margin
6530 if (PointInSelMargin(pt)) {
6531 DisplayCursor(GetMarginCursor(pt));
6532 SetHotSpotRange(NULL);
6533 return; // No need to test for selection
6536 // Display regular (drag) cursor over selection
6537 if (PointInSelection(pt) && !SelectionEmpty()) {
6538 DisplayCursor(Window::cursorArrow);
6539 } else if (PointIsHotspot(pt)) {
6540 DisplayCursor(Window::cursorHand);
6541 SetHotSpotRange(&pt);
6542 } else {
6543 DisplayCursor(Window::cursorText);
6544 SetHotSpotRange(NULL);
6549 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
6550 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
6551 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
6552 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6553 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6554 if (inDragDrop == ddInitial) {
6555 inDragDrop = ddNone;
6556 SetEmptySelection(newPos);
6557 selectionType = selChar;
6558 originalAnchorPos = sel.MainCaret();
6560 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
6561 hotSpotClickPos = INVALID_POSITION;
6562 NotifyHotSpotReleaseClick(newPos.Position(), false, ctrl, false);
6564 if (HaveMouseCapture()) {
6565 if (PointInSelMargin(pt)) {
6566 DisplayCursor(GetMarginCursor(pt));
6567 } else {
6568 DisplayCursor(Window::cursorText);
6569 SetHotSpotRange(NULL);
6571 ptMouseLast = pt;
6572 SetMouseCapture(false);
6573 NotifyIndicatorClick(false, newPos.Position(), false, false, false);
6574 if (inDragDrop == ddDragging) {
6575 SelectionPosition selStart = SelectionStart();
6576 SelectionPosition selEnd = SelectionEnd();
6577 if (selStart < selEnd) {
6578 if (drag.len) {
6579 if (ctrl) {
6580 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6581 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6583 } else if (newPos < selStart) {
6584 pdoc->DeleteChars(selStart.Position(), drag.len);
6585 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6586 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6588 } else if (newPos > selEnd) {
6589 pdoc->DeleteChars(selStart.Position(), drag.len);
6590 newPos.Add(-drag.len);
6591 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6592 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6594 } else {
6595 SetEmptySelection(newPos.Position());
6597 drag.Free();
6599 selectionType = selChar;
6601 } else {
6602 if (selectionType == selChar) {
6603 if (sel.Count() > 1) {
6604 sel.RangeMain() =
6605 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
6606 InvalidateSelection(sel.RangeMain(), true);
6607 } else {
6608 SetSelection(newPos, sel.RangeMain().anchor);
6611 sel.CommitTentative();
6613 SetRectangularRange();
6614 lastClickTime = curTime;
6615 lastClick = pt;
6616 lastXChosen = pt.x + xOffset;
6617 if (sel.selType == Selection::selStream) {
6618 SetLastXChosen();
6620 inDragDrop = ddNone;
6621 EnsureCaretVisible(false);
6625 // Called frequently to perform background UI including
6626 // caret blinking and automatic scrolling.
6627 void Editor::Tick() {
6628 if (HaveMouseCapture()) {
6629 // Auto scroll
6630 ButtonMove(ptMouseLast);
6632 if (caret.period > 0) {
6633 timer.ticksToWait -= timer.tickSize;
6634 if (timer.ticksToWait <= 0) {
6635 caret.on = !caret.on;
6636 timer.ticksToWait = caret.period;
6637 if (caret.active) {
6638 InvalidateCaret();
6642 if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) {
6643 scrollWidth = lineWidthMaxSeen;
6644 SetScrollBars();
6646 if ((dwellDelay < SC_TIME_FOREVER) &&
6647 (ticksToDwell > 0) &&
6648 (!HaveMouseCapture()) &&
6649 (ptMouseLast.y >= 0)) {
6650 ticksToDwell -= timer.tickSize;
6651 if (ticksToDwell <= 0) {
6652 dwelling = true;
6653 NotifyDwelling(ptMouseLast, dwelling);
6658 bool Editor::Idle() {
6660 bool idleDone;
6662 bool wrappingDone = wrapState == eWrapNone;
6664 if (!wrappingDone) {
6665 // Wrap lines during idle.
6666 WrapLines(false, -1);
6667 // No more wrapping
6668 if (wrapStart == wrapEnd)
6669 wrappingDone = true;
6672 // Add more idle things to do here, but make sure idleDone is
6673 // set correctly before the function returns. returning
6674 // false will stop calling this idle funtion until SetIdle() is
6675 // called again.
6677 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
6679 return !idleDone;
6682 void Editor::SetFocusState(bool focusState) {
6683 hasFocus = focusState;
6684 NotifyFocus(hasFocus);
6685 if (hasFocus) {
6686 ShowCaretAtCurrentPosition();
6687 } else {
6688 CancelModes();
6689 DropCaret();
6693 int Editor::PositionAfterArea(PRectangle rcArea) {
6694 // The start of the document line after the display line after the area
6695 // This often means that the line after a modification is restyled which helps
6696 // detect multiline comment additions and heals single line comments
6697 int lineAfter = topLine + (rcArea.bottom - 1) / vs.lineHeight + 1;
6698 if (lineAfter < cs.LinesDisplayed())
6699 return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
6700 else
6701 return pdoc->Length();
6704 // Style to a position within the view. If this causes a change at end of last line then
6705 // affects later lines so style all the viewed text.
6706 void Editor::StyleToPositionInView(Position pos) {
6707 int endWindow = PositionAfterArea(GetClientRectangle());
6708 if (pos > endWindow)
6709 pos = endWindow;
6710 int styleAtEnd = pdoc->StyleAt(pos-1);
6711 pdoc->EnsureStyledTo(pos);
6712 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleAt(pos-1))) {
6713 // Style at end of line changed so is multi-line change like starting a comment
6714 // so require rest of window to be styled.
6715 pdoc->EnsureStyledTo(endWindow);
6719 void Editor::IdleStyling() {
6720 // Style the line after the modification as this allows modifications that change just the
6721 // line of the modification to heal instead of propagating to the rest of the window.
6722 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(styleNeeded.upTo) + 2));
6724 if (needUpdateUI) {
6725 NotifyUpdateUI();
6726 needUpdateUI = 0;
6728 styleNeeded.Reset();
6731 void Editor::QueueStyling(int upTo) {
6732 styleNeeded.NeedUpTo(upTo);
6735 bool Editor::PaintContains(PRectangle rc) {
6736 if (rc.Empty()) {
6737 return true;
6738 } else {
6739 return rcPaint.Contains(rc);
6743 bool Editor::PaintContainsMargin() {
6744 PRectangle rcSelMargin = GetClientRectangle();
6745 rcSelMargin.right = vs.fixedColumnWidth;
6746 return PaintContains(rcSelMargin);
6749 void Editor::CheckForChangeOutsidePaint(Range r) {
6750 if (paintState == painting && !paintingAllText) {
6751 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
6752 if (!r.Valid())
6753 return;
6755 PRectangle rcRange = RectangleFromRange(r.start, r.end);
6756 PRectangle rcText = GetTextRectangle();
6757 if (rcRange.top < rcText.top) {
6758 rcRange.top = rcText.top;
6760 if (rcRange.bottom > rcText.bottom) {
6761 rcRange.bottom = rcText.bottom;
6764 if (!PaintContains(rcRange)) {
6765 AbandonPaint();
6770 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
6771 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
6772 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
6773 CheckForChangeOutsidePaint(Range(braces[0]));
6774 CheckForChangeOutsidePaint(Range(pos0));
6775 braces[0] = pos0;
6777 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
6778 CheckForChangeOutsidePaint(Range(braces[1]));
6779 CheckForChangeOutsidePaint(Range(pos1));
6780 braces[1] = pos1;
6782 bracesMatchStyle = matchStyle;
6783 if (paintState == notPainting) {
6784 Redraw();
6789 void Editor::SetAnnotationHeights(int start, int end) {
6790 if (vs.annotationVisible) {
6791 bool changedHeight = false;
6792 for (int line=start; line<end && line<pdoc->LinesTotal(); line++) {
6793 int linesWrapped = 1;
6794 if (wrapState != eWrapNone) {
6795 AutoSurface surface(this);
6796 AutoLineLayout ll(llc, RetrieveLineLayout(line));
6797 if (surface && ll) {
6798 LayoutLine(line, surface, vs, ll, wrapWidth);
6799 linesWrapped = ll->lines;
6802 if (cs.SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
6803 changedHeight = true;
6805 if (changedHeight) {
6806 Redraw();
6811 void Editor::SetDocPointer(Document *document) {
6812 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
6813 pdoc->RemoveWatcher(this, 0);
6814 pdoc->Release();
6815 if (document == NULL) {
6816 pdoc = new Document();
6817 } else {
6818 pdoc = document;
6820 pdoc->AddRef();
6822 // Ensure all positions within document
6823 sel.Clear();
6824 targetStart = 0;
6825 targetEnd = 0;
6827 braces[0] = invalidPosition;
6828 braces[1] = invalidPosition;
6830 // Reset the contraction state to fully shown.
6831 cs.Clear();
6832 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6833 SetAnnotationHeights(0, pdoc->LinesTotal());
6834 llc.Deallocate();
6835 NeedWrapping();
6837 pdoc->AddWatcher(this, 0);
6838 SetScrollBars();
6839 Redraw();
6842 void Editor::SetAnnotationVisible(int visible) {
6843 if (vs.annotationVisible != visible) {
6844 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
6845 vs.annotationVisible = visible;
6846 if (changedFromOrToHidden) {
6847 int dir = vs.annotationVisible ? 1 : -1;
6848 for (int line=0; line<pdoc->LinesTotal(); line++) {
6849 int annotationLines = pdoc->AnnotationLines(line);
6850 if (annotationLines > 0) {
6851 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
6855 Redraw();
6860 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
6862 void Editor::Expand(int &line, bool doExpand) {
6863 int lineMaxSubord = pdoc->GetLastChild(line);
6864 line++;
6865 while (line <= lineMaxSubord) {
6866 if (doExpand)
6867 cs.SetVisible(line, line, true);
6868 int level = pdoc->GetLevel(line);
6869 if (level & SC_FOLDLEVELHEADERFLAG) {
6870 if (doExpand && cs.GetExpanded(line)) {
6871 Expand(line, true);
6872 } else {
6873 Expand(line, false);
6875 } else {
6876 line++;
6881 void Editor::ToggleContraction(int line) {
6882 if (line >= 0) {
6883 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
6884 line = pdoc->GetFoldParent(line);
6885 if (line < 0)
6886 return;
6889 if (cs.GetExpanded(line)) {
6890 int lineMaxSubord = pdoc->GetLastChild(line);
6891 if (lineMaxSubord > line) {
6892 cs.SetExpanded(line, 0);
6893 cs.SetVisible(line + 1, lineMaxSubord, false);
6895 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
6896 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
6897 // This does not re-expand the fold
6898 EnsureCaretVisible();
6901 SetScrollBars();
6902 Redraw();
6905 } else {
6906 if (!(cs.GetVisible(line))) {
6907 EnsureLineVisible(line, false);
6908 GoToLine(line);
6910 cs.SetExpanded(line, 1);
6911 Expand(line, true);
6912 SetScrollBars();
6913 Redraw();
6918 int Editor::ContractedFoldNext(int lineStart) {
6919 for (int line = lineStart; line<pdoc->LinesTotal();) {
6920 if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
6921 return line;
6922 line = cs.ContractedNext(line+1);
6923 if (line < 0)
6924 return -1;
6927 return -1;
6931 * Recurse up from this line to find any folds that prevent this line from being visible
6932 * and unfold them all.
6934 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
6936 // In case in need of wrapping to ensure DisplayFromDoc works.
6937 if (lineDoc >= wrapStart)
6938 WrapLines(true, -1);
6940 if (!cs.GetVisible(lineDoc)) {
6941 int lookLine = lineDoc;
6942 int lookLineLevel = pdoc->GetLevel(lookLine);
6943 while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
6944 lookLineLevel = pdoc->GetLevel(--lookLine);
6946 int lineParent = pdoc->GetFoldParent(lookLine);
6947 if (lineParent >= 0) {
6948 if (lineDoc != lineParent)
6949 EnsureLineVisible(lineParent, enforcePolicy);
6950 if (!cs.GetExpanded(lineParent)) {
6951 cs.SetExpanded(lineParent, 1);
6952 Expand(lineParent, true);
6955 SetScrollBars();
6956 Redraw();
6958 if (enforcePolicy) {
6959 int lineDisplay = cs.DisplayFromDoc(lineDoc);
6960 if (visiblePolicy & VISIBLE_SLOP) {
6961 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
6962 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
6963 SetVerticalScrollPos();
6964 Redraw();
6965 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
6966 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
6967 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
6968 SetVerticalScrollPos();
6969 Redraw();
6971 } else {
6972 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
6973 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
6974 SetVerticalScrollPos();
6975 Redraw();
6981 int Editor::GetTag(char *tagValue, int tagNumber) {
6982 const char *text = 0;
6983 int length = 0;
6984 if ((tagNumber >= 1) && (tagNumber <= 9)) {
6985 char name[3] = "\\?";
6986 name[1] = static_cast<char>(tagNumber + '0');
6987 length = 2;
6988 text = pdoc->SubstituteByPosition(name, &length);
6990 if (tagValue) {
6991 if (text)
6992 memcpy(tagValue, text, length + 1);
6993 else
6994 *tagValue = '\0';
6996 return length;
6999 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
7000 UndoGroup ug(pdoc);
7001 if (length == -1)
7002 length = istrlen(text);
7003 if (replacePatterns) {
7004 text = pdoc->SubstituteByPosition(text, &length);
7005 if (!text) {
7006 return 0;
7009 if (targetStart != targetEnd)
7010 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
7011 targetEnd = targetStart;
7012 pdoc->InsertString(targetStart, text, length);
7013 targetEnd = targetStart + length;
7014 return length;
7017 bool Editor::IsUnicodeMode() const {
7018 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
7021 int Editor::CodePage() const {
7022 if (pdoc)
7023 return pdoc->dbcsCodePage;
7024 else
7025 return 0;
7028 int Editor::WrapCount(int line) {
7029 AutoSurface surface(this);
7030 AutoLineLayout ll(llc, RetrieveLineLayout(line));
7032 if (surface && ll) {
7033 LayoutLine(line, surface, vs, ll, wrapWidth);
7034 return ll->lines;
7035 } else {
7036 return 1;
7040 void Editor::AddStyledText(char *buffer, int appendLength) {
7041 // The buffer consists of alternating character bytes and style bytes
7042 int textLength = appendLength / 2;
7043 char *text = new char[textLength];
7044 int i;
7045 for (i = 0; i < textLength; i++) {
7046 text[i] = buffer[i*2];
7048 pdoc->InsertString(CurrentPosition(), text, textLength);
7049 for (i = 0; i < textLength; i++) {
7050 text[i] = buffer[i*2+1];
7052 pdoc->StartStyling(CurrentPosition(), static_cast<char>(0xff));
7053 pdoc->SetStyles(textLength, text);
7054 delete []text;
7055 SetEmptySelection(sel.MainCaret() + textLength);
7058 static bool ValidMargin(unsigned long wParam) {
7059 return wParam < ViewStyle::margins;
7062 static char *CharPtrFromSPtr(sptr_t lParam) {
7063 return reinterpret_cast<char *>(lParam);
7066 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7067 vs.EnsureStyle(wParam);
7068 switch (iMessage) {
7069 case SCI_STYLESETFORE:
7070 vs.styles[wParam].fore = ColourDesired(lParam);
7071 break;
7072 case SCI_STYLESETBACK:
7073 vs.styles[wParam].back = ColourDesired(lParam);
7074 break;
7075 case SCI_STYLESETBOLD:
7076 vs.styles[wParam].weight = lParam != 0 ? SC_WEIGHT_BOLD : SC_WEIGHT_NORMAL;
7077 break;
7078 case SCI_STYLESETWEIGHT:
7079 vs.styles[wParam].weight = lParam;
7080 break;
7081 case SCI_STYLESETITALIC:
7082 vs.styles[wParam].italic = lParam != 0;
7083 break;
7084 case SCI_STYLESETEOLFILLED:
7085 vs.styles[wParam].eolFilled = lParam != 0;
7086 break;
7087 case SCI_STYLESETSIZE:
7088 vs.styles[wParam].size = lParam * SC_FONT_SIZE_MULTIPLIER;
7089 break;
7090 case SCI_STYLESETSIZEFRACTIONAL:
7091 vs.styles[wParam].size = lParam;
7092 break;
7093 case SCI_STYLESETFONT:
7094 if (lParam != 0) {
7095 vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam));
7097 break;
7098 case SCI_STYLESETUNDERLINE:
7099 vs.styles[wParam].underline = lParam != 0;
7100 break;
7101 case SCI_STYLESETCASE:
7102 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
7103 break;
7104 case SCI_STYLESETCHARACTERSET:
7105 vs.styles[wParam].characterSet = lParam;
7106 pdoc->SetCaseFolder(NULL);
7107 break;
7108 case SCI_STYLESETVISIBLE:
7109 vs.styles[wParam].visible = lParam != 0;
7110 break;
7111 case SCI_STYLESETCHANGEABLE:
7112 vs.styles[wParam].changeable = lParam != 0;
7113 break;
7114 case SCI_STYLESETHOTSPOT:
7115 vs.styles[wParam].hotspot = lParam != 0;
7116 break;
7118 InvalidateStyleRedraw();
7121 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7122 vs.EnsureStyle(wParam);
7123 switch (iMessage) {
7124 case SCI_STYLEGETFORE:
7125 return vs.styles[wParam].fore.AsLong();
7126 case SCI_STYLEGETBACK:
7127 return vs.styles[wParam].back.AsLong();
7128 case SCI_STYLEGETBOLD:
7129 return vs.styles[wParam].weight > SC_WEIGHT_NORMAL;
7130 case SCI_STYLEGETWEIGHT:
7131 return vs.styles[wParam].weight;
7132 case SCI_STYLEGETITALIC:
7133 return vs.styles[wParam].italic ? 1 : 0;
7134 case SCI_STYLEGETEOLFILLED:
7135 return vs.styles[wParam].eolFilled ? 1 : 0;
7136 case SCI_STYLEGETSIZE:
7137 return vs.styles[wParam].size / SC_FONT_SIZE_MULTIPLIER;
7138 case SCI_STYLEGETSIZEFRACTIONAL:
7139 return vs.styles[wParam].size;
7140 case SCI_STYLEGETFONT:
7141 if (!vs.styles[wParam].fontName)
7142 return 0;
7143 if (lParam != 0)
7144 strcpy(CharPtrFromSPtr(lParam), vs.styles[wParam].fontName);
7145 return strlen(vs.styles[wParam].fontName);
7146 case SCI_STYLEGETUNDERLINE:
7147 return vs.styles[wParam].underline ? 1 : 0;
7148 case SCI_STYLEGETCASE:
7149 return static_cast<int>(vs.styles[wParam].caseForce);
7150 case SCI_STYLEGETCHARACTERSET:
7151 return vs.styles[wParam].characterSet;
7152 case SCI_STYLEGETVISIBLE:
7153 return vs.styles[wParam].visible ? 1 : 0;
7154 case SCI_STYLEGETCHANGEABLE:
7155 return vs.styles[wParam].changeable ? 1 : 0;
7156 case SCI_STYLEGETHOTSPOT:
7157 return vs.styles[wParam].hotspot ? 1 : 0;
7159 return 0;
7162 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
7163 const size_t n = strlen(val);
7164 if (lParam != 0) {
7165 char *ptr = reinterpret_cast<char *>(lParam);
7166 strcpy(ptr, val);
7168 return n; // Not including NUL
7171 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7172 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
7174 // Optional macro recording hook
7175 if (recordingMacro)
7176 NotifyMacroRecord(iMessage, wParam, lParam);
7178 switch (iMessage) {
7180 case SCI_GETTEXT: {
7181 if (lParam == 0)
7182 return pdoc->Length() + 1;
7183 if (wParam == 0)
7184 return 0;
7185 char *ptr = CharPtrFromSPtr(lParam);
7186 unsigned int iChar = 0;
7187 for (; iChar < wParam - 1; iChar++)
7188 ptr[iChar] = pdoc->CharAt(iChar);
7189 ptr[iChar] = '\0';
7190 return iChar;
7193 case SCI_SETTEXT: {
7194 if (lParam == 0)
7195 return 0;
7196 UndoGroup ug(pdoc);
7197 pdoc->DeleteChars(0, pdoc->Length());
7198 SetEmptySelection(0);
7199 pdoc->InsertCString(0, CharPtrFromSPtr(lParam));
7200 return 1;
7203 case SCI_GETTEXTLENGTH:
7204 return pdoc->Length();
7206 case SCI_CUT:
7207 Cut();
7208 SetLastXChosen();
7209 break;
7211 case SCI_COPY:
7212 Copy();
7213 break;
7215 case SCI_COPYALLOWLINE:
7216 CopyAllowLine();
7217 break;
7219 case SCI_VERTICALCENTRECARET:
7220 VerticalCentreCaret();
7221 break;
7223 case SCI_MOVESELECTEDLINESUP:
7224 MoveSelectedLinesUp();
7225 break;
7227 case SCI_MOVESELECTEDLINESDOWN:
7228 MoveSelectedLinesDown();
7229 break;
7231 case SCI_COPYRANGE:
7232 CopyRangeToClipboard(wParam, lParam);
7233 break;
7235 case SCI_COPYTEXT:
7236 CopyText(wParam, CharPtrFromSPtr(lParam));
7237 break;
7239 case SCI_PASTE:
7240 Paste();
7241 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
7242 SetLastXChosen();
7244 EnsureCaretVisible();
7245 break;
7247 case SCI_CLEAR:
7248 Clear();
7249 SetLastXChosen();
7250 EnsureCaretVisible();
7251 break;
7253 case SCI_UNDO:
7254 Undo();
7255 SetLastXChosen();
7256 break;
7258 case SCI_CANUNDO:
7259 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
7261 case SCI_EMPTYUNDOBUFFER:
7262 pdoc->DeleteUndoHistory();
7263 return 0;
7265 case SCI_GETFIRSTVISIBLELINE:
7266 return topLine;
7268 case SCI_SETFIRSTVISIBLELINE:
7269 ScrollTo(wParam);
7270 break;
7272 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
7273 int lineStart = pdoc->LineStart(wParam);
7274 int lineEnd = pdoc->LineStart(wParam + 1);
7275 if (lParam == 0) {
7276 return lineEnd - lineStart;
7278 char *ptr = CharPtrFromSPtr(lParam);
7279 int iPlace = 0;
7280 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
7281 ptr[iPlace++] = pdoc->CharAt(iChar);
7283 return iPlace;
7286 case SCI_GETLINECOUNT:
7287 if (pdoc->LinesTotal() == 0)
7288 return 1;
7289 else
7290 return pdoc->LinesTotal();
7292 case SCI_GETMODIFY:
7293 return !pdoc->IsSavePoint();
7295 case SCI_SETSEL: {
7296 int nStart = static_cast<int>(wParam);
7297 int nEnd = static_cast<int>(lParam);
7298 if (nEnd < 0)
7299 nEnd = pdoc->Length();
7300 if (nStart < 0)
7301 nStart = nEnd; // Remove selection
7302 InvalidateSelection(SelectionRange(nStart, nEnd));
7303 sel.Clear();
7304 sel.selType = Selection::selStream;
7305 SetSelection(nEnd, nStart);
7306 EnsureCaretVisible();
7308 break;
7310 case SCI_GETSELTEXT: {
7311 SelectionText selectedText;
7312 CopySelectionRange(&selectedText);
7313 if (lParam == 0) {
7314 return selectedText.len ? selectedText.len : 1;
7315 } else {
7316 char *ptr = CharPtrFromSPtr(lParam);
7317 int iChar = 0;
7318 if (selectedText.len) {
7319 for (; iChar < selectedText.len; iChar++)
7320 ptr[iChar] = selectedText.s[iChar];
7321 } else {
7322 ptr[0] = '\0';
7324 return iChar;
7328 case SCI_LINEFROMPOSITION:
7329 if (static_cast<int>(wParam) < 0)
7330 return 0;
7331 return pdoc->LineFromPosition(wParam);
7333 case SCI_POSITIONFROMLINE:
7334 if (static_cast<int>(wParam) < 0)
7335 wParam = pdoc->LineFromPosition(SelectionStart().Position());
7336 if (wParam == 0)
7337 return 0; // Even if there is no text, there is a first line that starts at 0
7338 if (static_cast<int>(wParam) > pdoc->LinesTotal())
7339 return -1;
7340 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
7341 // return -1;
7342 return pdoc->LineStart(wParam);
7344 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
7345 case SCI_LINELENGTH:
7346 if ((static_cast<int>(wParam) < 0) ||
7347 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
7348 return 0;
7349 return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
7351 case SCI_REPLACESEL: {
7352 if (lParam == 0)
7353 return 0;
7354 UndoGroup ug(pdoc);
7355 ClearSelection();
7356 char *replacement = CharPtrFromSPtr(lParam);
7357 pdoc->InsertCString(sel.MainCaret(), replacement);
7358 SetEmptySelection(sel.MainCaret() + istrlen(replacement));
7359 EnsureCaretVisible();
7361 break;
7363 case SCI_SETTARGETSTART:
7364 targetStart = wParam;
7365 break;
7367 case SCI_GETTARGETSTART:
7368 return targetStart;
7370 case SCI_SETTARGETEND:
7371 targetEnd = wParam;
7372 break;
7374 case SCI_GETTARGETEND:
7375 return targetEnd;
7377 case SCI_TARGETFROMSELECTION:
7378 if (sel.MainCaret() < sel.MainAnchor()) {
7379 targetStart = sel.MainCaret();
7380 targetEnd = sel.MainAnchor();
7381 } else {
7382 targetStart = sel.MainAnchor();
7383 targetEnd = sel.MainCaret();
7385 break;
7387 case SCI_REPLACETARGET:
7388 PLATFORM_ASSERT(lParam);
7389 return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam);
7391 case SCI_REPLACETARGETRE:
7392 PLATFORM_ASSERT(lParam);
7393 return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam);
7395 case SCI_SEARCHINTARGET:
7396 PLATFORM_ASSERT(lParam);
7397 return SearchInTarget(CharPtrFromSPtr(lParam), wParam);
7399 case SCI_SETSEARCHFLAGS:
7400 searchFlags = wParam;
7401 break;
7403 case SCI_GETSEARCHFLAGS:
7404 return searchFlags;
7406 case SCI_GETTAG:
7407 return GetTag(CharPtrFromSPtr(lParam), wParam);
7409 case SCI_POSITIONBEFORE:
7410 return pdoc->MovePositionOutsideChar(wParam - 1, -1, true);
7412 case SCI_POSITIONAFTER:
7413 return pdoc->MovePositionOutsideChar(wParam + 1, 1, true);
7415 case SCI_LINESCROLL:
7416 ScrollTo(topLine + lParam);
7417 HorizontalScrollTo(xOffset + wParam * vs.spaceWidth);
7418 return 1;
7420 case SCI_SETXOFFSET:
7421 xOffset = wParam;
7422 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7423 SetHorizontalScrollPos();
7424 Redraw();
7425 break;
7427 case SCI_GETXOFFSET:
7428 return xOffset;
7430 case SCI_CHOOSECARETX:
7431 SetLastXChosen();
7432 break;
7434 case SCI_SCROLLCARET:
7435 EnsureCaretVisible();
7436 break;
7438 case SCI_SETREADONLY:
7439 pdoc->SetReadOnly(wParam != 0);
7440 return 1;
7442 case SCI_GETREADONLY:
7443 return pdoc->IsReadOnly();
7445 case SCI_CANPASTE:
7446 return CanPaste();
7448 case SCI_POINTXFROMPOSITION:
7449 if (lParam < 0) {
7450 return 0;
7451 } else {
7452 Point pt = LocationFromPosition(lParam);
7453 return pt.x;
7456 case SCI_POINTYFROMPOSITION:
7457 if (lParam < 0) {
7458 return 0;
7459 } else {
7460 Point pt = LocationFromPosition(lParam);
7461 return pt.y;
7464 case SCI_FINDTEXT:
7465 return FindText(wParam, lParam);
7467 case SCI_GETTEXTRANGE: {
7468 if (lParam == 0)
7469 return 0;
7470 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7471 int cpMax = tr->chrg.cpMax;
7472 if (cpMax == -1)
7473 cpMax = pdoc->Length();
7474 PLATFORM_ASSERT(cpMax <= pdoc->Length());
7475 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
7476 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
7477 // Spec says copied text is terminated with a NUL
7478 tr->lpstrText[len] = '\0';
7479 return len; // Not including NUL
7482 case SCI_HIDESELECTION:
7483 hideSelection = wParam != 0;
7484 Redraw();
7485 break;
7487 case SCI_FORMATRANGE:
7488 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
7490 case SCI_GETMARGINLEFT:
7491 return vs.leftMarginWidth;
7493 case SCI_GETMARGINRIGHT:
7494 return vs.rightMarginWidth;
7496 case SCI_SETMARGINLEFT:
7497 vs.leftMarginWidth = lParam;
7498 InvalidateStyleRedraw();
7499 break;
7501 case SCI_SETMARGINRIGHT:
7502 vs.rightMarginWidth = lParam;
7503 InvalidateStyleRedraw();
7504 break;
7506 // Control specific mesages
7508 case SCI_ADDTEXT: {
7509 if (lParam == 0)
7510 return 0;
7511 pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam);
7512 SetEmptySelection(sel.MainCaret() + wParam);
7513 return 0;
7516 case SCI_ADDSTYLEDTEXT:
7517 if (lParam)
7518 AddStyledText(CharPtrFromSPtr(lParam), wParam);
7519 return 0;
7521 case SCI_INSERTTEXT: {
7522 if (lParam == 0)
7523 return 0;
7524 int insertPos = wParam;
7525 if (static_cast<int>(wParam) == -1)
7526 insertPos = CurrentPosition();
7527 int newCurrent = CurrentPosition();
7528 char *sz = CharPtrFromSPtr(lParam);
7529 pdoc->InsertCString(insertPos, sz);
7530 if (newCurrent > insertPos)
7531 newCurrent += istrlen(sz);
7532 SetEmptySelection(newCurrent);
7533 return 0;
7536 case SCI_APPENDTEXT:
7537 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam);
7538 return 0;
7540 case SCI_CLEARALL:
7541 ClearAll();
7542 return 0;
7544 case SCI_DELETERANGE:
7545 pdoc->DeleteChars(wParam, lParam);
7546 return 0;
7548 case SCI_CLEARDOCUMENTSTYLE:
7549 ClearDocumentStyle();
7550 return 0;
7552 case SCI_SETUNDOCOLLECTION:
7553 pdoc->SetUndoCollection(wParam != 0);
7554 return 0;
7556 case SCI_GETUNDOCOLLECTION:
7557 return pdoc->IsCollectingUndo();
7559 case SCI_BEGINUNDOACTION:
7560 pdoc->BeginUndoAction();
7561 return 0;
7563 case SCI_ENDUNDOACTION:
7564 pdoc->EndUndoAction();
7565 return 0;
7567 case SCI_GETCARETPERIOD:
7568 return caret.period;
7570 case SCI_SETCARETPERIOD:
7571 caret.period = wParam;
7572 break;
7574 case SCI_GETWORDCHARS:
7575 return pdoc->GetCharsOfClass(CharClassify::ccWord, reinterpret_cast<unsigned char *>(lParam));
7577 case SCI_SETWORDCHARS: {
7578 pdoc->SetDefaultCharClasses(false);
7579 if (lParam == 0)
7580 return 0;
7581 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
7583 break;
7585 case SCI_GETWHITESPACECHARS:
7586 return pdoc->GetCharsOfClass(CharClassify::ccSpace, reinterpret_cast<unsigned char *>(lParam));
7588 case SCI_SETWHITESPACECHARS: {
7589 if (lParam == 0)
7590 return 0;
7591 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
7593 break;
7595 case SCI_GETPUNCTUATIONCHARS:
7596 return pdoc->GetCharsOfClass(CharClassify::ccPunctuation, reinterpret_cast<unsigned char *>(lParam));
7598 case SCI_SETPUNCTUATIONCHARS: {
7599 if (lParam == 0)
7600 return 0;
7601 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccPunctuation);
7603 break;
7605 case SCI_SETCHARSDEFAULT:
7606 pdoc->SetDefaultCharClasses(true);
7607 break;
7609 case SCI_GETLENGTH:
7610 return pdoc->Length();
7612 case SCI_ALLOCATE:
7613 pdoc->Allocate(wParam);
7614 break;
7616 case SCI_GETCHARAT:
7617 return pdoc->CharAt(wParam);
7619 case SCI_SETCURRENTPOS:
7620 if (sel.IsRectangular()) {
7621 sel.Rectangular().caret.SetPosition(wParam);
7622 SetRectangularRange();
7623 Redraw();
7624 } else {
7625 SetSelection(wParam, sel.MainAnchor());
7627 break;
7629 case SCI_GETCURRENTPOS:
7630 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
7632 case SCI_SETANCHOR:
7633 if (sel.IsRectangular()) {
7634 sel.Rectangular().anchor.SetPosition(wParam);
7635 SetRectangularRange();
7636 Redraw();
7637 } else {
7638 SetSelection(sel.MainCaret(), wParam);
7640 break;
7642 case SCI_GETANCHOR:
7643 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
7645 case SCI_SETSELECTIONSTART:
7646 SetSelection(Platform::Maximum(sel.MainCaret(), wParam), wParam);
7647 break;
7649 case SCI_GETSELECTIONSTART:
7650 return sel.LimitsForRectangularElseMain().start.Position();
7652 case SCI_SETSELECTIONEND:
7653 SetSelection(wParam, Platform::Minimum(sel.MainAnchor(), wParam));
7654 break;
7656 case SCI_GETSELECTIONEND:
7657 return sel.LimitsForRectangularElseMain().end.Position();
7659 case SCI_SETEMPTYSELECTION:
7660 SetEmptySelection(wParam);
7661 break;
7663 case SCI_SETPRINTMAGNIFICATION:
7664 printMagnification = wParam;
7665 break;
7667 case SCI_GETPRINTMAGNIFICATION:
7668 return printMagnification;
7670 case SCI_SETPRINTCOLOURMODE:
7671 printColourMode = wParam;
7672 break;
7674 case SCI_GETPRINTCOLOURMODE:
7675 return printColourMode;
7677 case SCI_SETPRINTWRAPMODE:
7678 printWrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
7679 break;
7681 case SCI_GETPRINTWRAPMODE:
7682 return printWrapState;
7684 case SCI_GETSTYLEAT:
7685 if (static_cast<int>(wParam) >= pdoc->Length())
7686 return 0;
7687 else
7688 return pdoc->StyleAt(wParam);
7690 case SCI_REDO:
7691 Redo();
7692 break;
7694 case SCI_SELECTALL:
7695 SelectAll();
7696 break;
7698 case SCI_SETSAVEPOINT:
7699 pdoc->SetSavePoint();
7700 break;
7702 case SCI_GETSTYLEDTEXT: {
7703 if (lParam == 0)
7704 return 0;
7705 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7706 int iPlace = 0;
7707 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
7708 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
7709 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
7711 tr->lpstrText[iPlace] = '\0';
7712 tr->lpstrText[iPlace + 1] = '\0';
7713 return iPlace;
7716 case SCI_CANREDO:
7717 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
7719 case SCI_MARKERLINEFROMHANDLE:
7720 return pdoc->LineFromHandle(wParam);
7722 case SCI_MARKERDELETEHANDLE:
7723 pdoc->DeleteMarkFromHandle(wParam);
7724 break;
7726 case SCI_GETVIEWWS:
7727 return vs.viewWhitespace;
7729 case SCI_SETVIEWWS:
7730 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
7731 Redraw();
7732 break;
7734 case SCI_GETWHITESPACESIZE:
7735 return vs.whitespaceSize;
7737 case SCI_SETWHITESPACESIZE:
7738 vs.whitespaceSize = static_cast<int>(wParam);
7739 Redraw();
7740 break;
7742 case SCI_POSITIONFROMPOINT:
7743 return PositionFromLocation(Point(wParam, lParam), false, false);
7745 case SCI_POSITIONFROMPOINTCLOSE:
7746 return PositionFromLocation(Point(wParam, lParam), true, false);
7748 case SCI_CHARPOSITIONFROMPOINT:
7749 return PositionFromLocation(Point(wParam, lParam), false, true);
7751 case SCI_CHARPOSITIONFROMPOINTCLOSE:
7752 return PositionFromLocation(Point(wParam, lParam), true, true);
7754 case SCI_GOTOLINE:
7755 GoToLine(wParam);
7756 break;
7758 case SCI_GOTOPOS:
7759 SetEmptySelection(wParam);
7760 EnsureCaretVisible();
7761 break;
7763 case SCI_GETCURLINE: {
7764 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
7765 int lineStart = pdoc->LineStart(lineCurrentPos);
7766 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
7767 if (lParam == 0) {
7768 return 1 + lineEnd - lineStart;
7770 PLATFORM_ASSERT(wParam > 0);
7771 char *ptr = CharPtrFromSPtr(lParam);
7772 unsigned int iPlace = 0;
7773 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
7774 ptr[iPlace++] = pdoc->CharAt(iChar);
7776 ptr[iPlace] = '\0';
7777 return sel.MainCaret() - lineStart;
7780 case SCI_GETENDSTYLED:
7781 return pdoc->GetEndStyled();
7783 case SCI_GETEOLMODE:
7784 return pdoc->eolMode;
7786 case SCI_SETEOLMODE:
7787 pdoc->eolMode = wParam;
7788 break;
7790 case SCI_STARTSTYLING:
7791 pdoc->StartStyling(wParam, static_cast<char>(lParam));
7792 break;
7794 case SCI_SETSTYLING:
7795 pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
7796 break;
7798 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
7799 if (lParam == 0)
7800 return 0;
7801 pdoc->SetStyles(wParam, CharPtrFromSPtr(lParam));
7802 break;
7804 case SCI_SETBUFFEREDDRAW:
7805 bufferedDraw = wParam != 0;
7806 break;
7808 case SCI_GETBUFFEREDDRAW:
7809 return bufferedDraw;
7811 case SCI_GETTWOPHASEDRAW:
7812 return twoPhaseDraw;
7814 case SCI_SETTWOPHASEDRAW:
7815 twoPhaseDraw = wParam != 0;
7816 InvalidateStyleRedraw();
7817 break;
7819 case SCI_SETFONTQUALITY:
7820 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
7821 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
7822 InvalidateStyleRedraw();
7823 break;
7825 case SCI_GETFONTQUALITY:
7826 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
7828 case SCI_SETTABWIDTH:
7829 if (wParam > 0) {
7830 pdoc->tabInChars = wParam;
7831 if (pdoc->indentInChars == 0)
7832 pdoc->actualIndentInChars = pdoc->tabInChars;
7834 InvalidateStyleRedraw();
7835 break;
7837 case SCI_GETTABWIDTH:
7838 return pdoc->tabInChars;
7840 case SCI_SETINDENT:
7841 pdoc->indentInChars = wParam;
7842 if (pdoc->indentInChars != 0)
7843 pdoc->actualIndentInChars = pdoc->indentInChars;
7844 else
7845 pdoc->actualIndentInChars = pdoc->tabInChars;
7846 InvalidateStyleRedraw();
7847 break;
7849 case SCI_GETINDENT:
7850 return pdoc->indentInChars;
7852 case SCI_SETUSETABS:
7853 pdoc->useTabs = wParam != 0;
7854 InvalidateStyleRedraw();
7855 break;
7857 case SCI_GETUSETABS:
7858 return pdoc->useTabs;
7860 case SCI_SETLINEINDENTATION:
7861 pdoc->SetLineIndentation(wParam, lParam);
7862 break;
7864 case SCI_GETLINEINDENTATION:
7865 return pdoc->GetLineIndentation(wParam);
7867 case SCI_GETLINEINDENTPOSITION:
7868 return pdoc->GetLineIndentPosition(wParam);
7870 case SCI_SETTABINDENTS:
7871 pdoc->tabIndents = wParam != 0;
7872 break;
7874 case SCI_GETTABINDENTS:
7875 return pdoc->tabIndents;
7877 case SCI_SETBACKSPACEUNINDENTS:
7878 pdoc->backspaceUnindents = wParam != 0;
7879 break;
7881 case SCI_GETBACKSPACEUNINDENTS:
7882 return pdoc->backspaceUnindents;
7884 case SCI_SETMOUSEDWELLTIME:
7885 dwellDelay = wParam;
7886 ticksToDwell = dwellDelay;
7887 break;
7889 case SCI_GETMOUSEDWELLTIME:
7890 return dwellDelay;
7892 case SCI_WORDSTARTPOSITION:
7893 return pdoc->ExtendWordSelect(wParam, -1, lParam != 0);
7895 case SCI_WORDENDPOSITION:
7896 return pdoc->ExtendWordSelect(wParam, 1, lParam != 0);
7898 case SCI_SETWRAPMODE:
7899 switch (wParam) {
7900 case SC_WRAP_WORD:
7901 wrapState = eWrapWord;
7902 break;
7903 case SC_WRAP_CHAR:
7904 wrapState = eWrapChar;
7905 break;
7906 default:
7907 wrapState = eWrapNone;
7908 break;
7910 xOffset = 0;
7911 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7912 InvalidateStyleRedraw();
7913 ReconfigureScrollBars();
7914 break;
7916 case SCI_GETWRAPMODE:
7917 return wrapState;
7919 case SCI_SETWRAPVISUALFLAGS:
7920 if (wrapVisualFlags != static_cast<int>(wParam)) {
7921 wrapVisualFlags = wParam;
7922 InvalidateStyleRedraw();
7923 ReconfigureScrollBars();
7925 break;
7927 case SCI_GETWRAPVISUALFLAGS:
7928 return wrapVisualFlags;
7930 case SCI_SETWRAPVISUALFLAGSLOCATION:
7931 wrapVisualFlagsLocation = wParam;
7932 InvalidateStyleRedraw();
7933 break;
7935 case SCI_GETWRAPVISUALFLAGSLOCATION:
7936 return wrapVisualFlagsLocation;
7938 case SCI_SETWRAPSTARTINDENT:
7939 if (wrapVisualStartIndent != static_cast<int>(wParam)) {
7940 wrapVisualStartIndent = wParam;
7941 InvalidateStyleRedraw();
7942 ReconfigureScrollBars();
7944 break;
7946 case SCI_GETWRAPSTARTINDENT:
7947 return wrapVisualStartIndent;
7949 case SCI_SETWRAPINDENTMODE:
7950 if (wrapIndentMode != static_cast<int>(wParam)) {
7951 wrapIndentMode = wParam;
7952 InvalidateStyleRedraw();
7953 ReconfigureScrollBars();
7955 break;
7957 case SCI_GETWRAPINDENTMODE:
7958 return wrapIndentMode;
7960 case SCI_SETLAYOUTCACHE:
7961 llc.SetLevel(wParam);
7962 break;
7964 case SCI_GETLAYOUTCACHE:
7965 return llc.GetLevel();
7967 case SCI_SETPOSITIONCACHE:
7968 posCache.SetSize(wParam);
7969 break;
7971 case SCI_GETPOSITIONCACHE:
7972 return posCache.GetSize();
7974 case SCI_SETSCROLLWIDTH:
7975 PLATFORM_ASSERT(wParam > 0);
7976 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
7977 lineWidthMaxSeen = 0;
7978 scrollWidth = wParam;
7979 SetScrollBars();
7981 break;
7983 case SCI_GETSCROLLWIDTH:
7984 return scrollWidth;
7986 case SCI_SETSCROLLWIDTHTRACKING:
7987 trackLineWidth = wParam != 0;
7988 break;
7990 case SCI_GETSCROLLWIDTHTRACKING:
7991 return trackLineWidth;
7993 case SCI_LINESJOIN:
7994 LinesJoin();
7995 break;
7997 case SCI_LINESSPLIT:
7998 LinesSplit(wParam);
7999 break;
8001 case SCI_TEXTWIDTH:
8002 PLATFORM_ASSERT(wParam < vs.stylesSize);
8003 PLATFORM_ASSERT(lParam);
8004 return TextWidth(wParam, CharPtrFromSPtr(lParam));
8006 case SCI_TEXTHEIGHT:
8007 return vs.lineHeight;
8009 case SCI_SETENDATLASTLINE:
8010 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
8011 if (endAtLastLine != (wParam != 0)) {
8012 endAtLastLine = wParam != 0;
8013 SetScrollBars();
8015 break;
8017 case SCI_GETENDATLASTLINE:
8018 return endAtLastLine;
8020 case SCI_SETCARETSTICKY:
8021 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
8022 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
8023 caretSticky = wParam;
8025 break;
8027 case SCI_GETCARETSTICKY:
8028 return caretSticky;
8030 case SCI_TOGGLECARETSTICKY:
8031 caretSticky = !caretSticky;
8032 break;
8034 case SCI_GETCOLUMN:
8035 return pdoc->GetColumn(wParam);
8037 case SCI_FINDCOLUMN:
8038 return pdoc->FindColumn(wParam, lParam);
8040 case SCI_SETHSCROLLBAR :
8041 if (horizontalScrollBarVisible != (wParam != 0)) {
8042 horizontalScrollBarVisible = wParam != 0;
8043 SetScrollBars();
8044 ReconfigureScrollBars();
8046 break;
8048 case SCI_GETHSCROLLBAR:
8049 return horizontalScrollBarVisible;
8051 case SCI_SETVSCROLLBAR:
8052 if (verticalScrollBarVisible != (wParam != 0)) {
8053 verticalScrollBarVisible = wParam != 0;
8054 SetScrollBars();
8055 ReconfigureScrollBars();
8057 break;
8059 case SCI_GETVSCROLLBAR:
8060 return verticalScrollBarVisible;
8062 case SCI_SETINDENTATIONGUIDES:
8063 vs.viewIndentationGuides = IndentView(wParam);
8064 Redraw();
8065 break;
8067 case SCI_GETINDENTATIONGUIDES:
8068 return vs.viewIndentationGuides;
8070 case SCI_SETHIGHLIGHTGUIDE:
8071 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
8072 highlightGuideColumn = wParam;
8073 Redraw();
8075 break;
8077 case SCI_GETHIGHLIGHTGUIDE:
8078 return highlightGuideColumn;
8080 case SCI_GETLINEENDPOSITION:
8081 return pdoc->LineEnd(wParam);
8083 case SCI_SETCODEPAGE:
8084 if (ValidCodePage(wParam)) {
8085 if (pdoc->SetDBCSCodePage(wParam)) {
8086 InvalidateStyleRedraw();
8089 break;
8091 case SCI_GETCODEPAGE:
8092 return pdoc->dbcsCodePage;
8094 #ifdef INCLUDE_DEPRECATED_FEATURES
8095 case SCI_SETUSEPALETTE:
8096 InvalidateStyleRedraw();
8097 break;
8099 case SCI_GETUSEPALETTE:
8100 return 0;
8101 #endif
8103 // Marker definition and setting
8104 case SCI_MARKERDEFINE:
8105 if (wParam <= MARKER_MAX) {
8106 vs.markers[wParam].markType = lParam;
8107 vs.CalcLargestMarkerHeight();
8109 InvalidateStyleData();
8110 RedrawSelMargin();
8111 break;
8113 case SCI_MARKERSYMBOLDEFINED:
8114 if (wParam <= MARKER_MAX)
8115 return vs.markers[wParam].markType;
8116 else
8117 return 0;
8119 case SCI_MARKERSETFORE:
8120 if (wParam <= MARKER_MAX)
8121 vs.markers[wParam].fore = ColourDesired(lParam);
8122 InvalidateStyleData();
8123 RedrawSelMargin();
8124 break;
8125 case SCI_MARKERSETBACKSELECTED:
8126 if (wParam <= MARKER_MAX)
8127 vs.markers[wParam].backSelected = ColourDesired(lParam);
8128 InvalidateStyleData();
8129 RedrawSelMargin();
8130 break;
8131 case SCI_MARKERENABLEHIGHLIGHT:
8132 highlightDelimiter.isEnabled = wParam == 1;
8133 RedrawSelMargin();
8134 break;
8135 case SCI_MARKERSETBACK:
8136 if (wParam <= MARKER_MAX)
8137 vs.markers[wParam].back = ColourDesired(lParam);
8138 InvalidateStyleData();
8139 RedrawSelMargin();
8140 break;
8141 case SCI_MARKERSETALPHA:
8142 if (wParam <= MARKER_MAX)
8143 vs.markers[wParam].alpha = lParam;
8144 InvalidateStyleRedraw();
8145 break;
8146 case SCI_MARKERADD: {
8147 int markerID = pdoc->AddMark(wParam, lParam);
8148 return markerID;
8150 case SCI_MARKERADDSET:
8151 if (lParam != 0)
8152 pdoc->AddMarkSet(wParam, lParam);
8153 break;
8155 case SCI_MARKERDELETE:
8156 pdoc->DeleteMark(wParam, lParam);
8157 break;
8159 case SCI_MARKERDELETEALL:
8160 pdoc->DeleteAllMarks(static_cast<int>(wParam));
8161 break;
8163 case SCI_MARKERGET:
8164 return pdoc->GetMark(wParam);
8166 case SCI_MARKERNEXT:
8167 return pdoc->MarkerNext(wParam, lParam);
8169 case SCI_MARKERPREVIOUS: {
8170 for (int iLine = wParam; iLine >= 0; iLine--) {
8171 if ((pdoc->GetMark(iLine) & lParam) != 0)
8172 return iLine;
8175 return -1;
8177 case SCI_MARKERDEFINEPIXMAP:
8178 if (wParam <= MARKER_MAX) {
8179 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
8180 vs.CalcLargestMarkerHeight();
8182 InvalidateStyleData();
8183 RedrawSelMargin();
8184 break;
8186 case SCI_RGBAIMAGESETWIDTH:
8187 sizeRGBAImage.x = wParam;
8188 break;
8190 case SCI_RGBAIMAGESETHEIGHT:
8191 sizeRGBAImage.y = wParam;
8192 break;
8194 case SCI_RGBAIMAGESETSCALE:
8195 scaleRGBAImage = wParam;
8196 break;
8198 case SCI_MARKERDEFINERGBAIMAGE:
8199 if (wParam <= MARKER_MAX) {
8200 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0, reinterpret_cast<unsigned char *>(lParam));
8201 vs.CalcLargestMarkerHeight();
8203 InvalidateStyleData();
8204 RedrawSelMargin();
8205 break;
8207 case SCI_SETMARGINTYPEN:
8208 if (ValidMargin(wParam)) {
8209 vs.ms[wParam].style = lParam;
8210 InvalidateStyleRedraw();
8212 break;
8214 case SCI_GETMARGINTYPEN:
8215 if (ValidMargin(wParam))
8216 return vs.ms[wParam].style;
8217 else
8218 return 0;
8220 case SCI_SETMARGINWIDTHN:
8221 if (ValidMargin(wParam)) {
8222 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
8223 if (vs.ms[wParam].width != lParam) {
8224 vs.ms[wParam].width = lParam;
8225 InvalidateStyleRedraw();
8228 break;
8230 case SCI_GETMARGINWIDTHN:
8231 if (ValidMargin(wParam))
8232 return vs.ms[wParam].width;
8233 else
8234 return 0;
8236 case SCI_SETMARGINMASKN:
8237 if (ValidMargin(wParam)) {
8238 vs.ms[wParam].mask = lParam;
8239 InvalidateStyleRedraw();
8241 break;
8243 case SCI_GETMARGINMASKN:
8244 if (ValidMargin(wParam))
8245 return vs.ms[wParam].mask;
8246 else
8247 return 0;
8249 case SCI_SETMARGINSENSITIVEN:
8250 if (ValidMargin(wParam)) {
8251 vs.ms[wParam].sensitive = lParam != 0;
8252 InvalidateStyleRedraw();
8254 break;
8256 case SCI_GETMARGINSENSITIVEN:
8257 if (ValidMargin(wParam))
8258 return vs.ms[wParam].sensitive ? 1 : 0;
8259 else
8260 return 0;
8262 case SCI_SETMARGINCURSORN:
8263 if (ValidMargin(wParam))
8264 vs.ms[wParam].cursor = lParam;
8265 break;
8267 case SCI_GETMARGINCURSORN:
8268 if (ValidMargin(wParam))
8269 return vs.ms[wParam].cursor;
8270 else
8271 return 0;
8273 case SCI_STYLECLEARALL:
8274 vs.ClearStyles();
8275 InvalidateStyleRedraw();
8276 break;
8278 case SCI_STYLESETFORE:
8279 case SCI_STYLESETBACK:
8280 case SCI_STYLESETBOLD:
8281 case SCI_STYLESETWEIGHT:
8282 case SCI_STYLESETITALIC:
8283 case SCI_STYLESETEOLFILLED:
8284 case SCI_STYLESETSIZE:
8285 case SCI_STYLESETSIZEFRACTIONAL:
8286 case SCI_STYLESETFONT:
8287 case SCI_STYLESETUNDERLINE:
8288 case SCI_STYLESETCASE:
8289 case SCI_STYLESETCHARACTERSET:
8290 case SCI_STYLESETVISIBLE:
8291 case SCI_STYLESETCHANGEABLE:
8292 case SCI_STYLESETHOTSPOT:
8293 StyleSetMessage(iMessage, wParam, lParam);
8294 break;
8296 case SCI_STYLEGETFORE:
8297 case SCI_STYLEGETBACK:
8298 case SCI_STYLEGETBOLD:
8299 case SCI_STYLEGETWEIGHT:
8300 case SCI_STYLEGETITALIC:
8301 case SCI_STYLEGETEOLFILLED:
8302 case SCI_STYLEGETSIZE:
8303 case SCI_STYLEGETSIZEFRACTIONAL:
8304 case SCI_STYLEGETFONT:
8305 case SCI_STYLEGETUNDERLINE:
8306 case SCI_STYLEGETCASE:
8307 case SCI_STYLEGETCHARACTERSET:
8308 case SCI_STYLEGETVISIBLE:
8309 case SCI_STYLEGETCHANGEABLE:
8310 case SCI_STYLEGETHOTSPOT:
8311 return StyleGetMessage(iMessage, wParam, lParam);
8313 case SCI_STYLERESETDEFAULT:
8314 vs.ResetDefaultStyle();
8315 InvalidateStyleRedraw();
8316 break;
8317 case SCI_SETSTYLEBITS:
8318 vs.EnsureStyle((1 << wParam) - 1);
8319 pdoc->SetStylingBits(wParam);
8320 break;
8322 case SCI_GETSTYLEBITS:
8323 return pdoc->stylingBits;
8325 case SCI_SETLINESTATE:
8326 return pdoc->SetLineState(wParam, lParam);
8328 case SCI_GETLINESTATE:
8329 return pdoc->GetLineState(wParam);
8331 case SCI_GETMAXLINESTATE:
8332 return pdoc->GetMaxLineState();
8334 case SCI_GETCARETLINEVISIBLE:
8335 return vs.showCaretLineBackground;
8336 case SCI_SETCARETLINEVISIBLE:
8337 vs.showCaretLineBackground = wParam != 0;
8338 InvalidateStyleRedraw();
8339 break;
8340 case SCI_GETCARETLINEVISIBLEALWAYS:
8341 return vs.alwaysShowCaretLineBackground;
8342 case SCI_SETCARETLINEVISIBLEALWAYS:
8343 vs.alwaysShowCaretLineBackground = wParam != 0;
8344 InvalidateStyleRedraw();
8345 break;
8347 case SCI_GETCARETLINEBACK:
8348 return vs.caretLineBackground.AsLong();
8349 case SCI_SETCARETLINEBACK:
8350 vs.caretLineBackground = wParam;
8351 InvalidateStyleRedraw();
8352 break;
8353 case SCI_GETCARETLINEBACKALPHA:
8354 return vs.caretLineAlpha;
8355 case SCI_SETCARETLINEBACKALPHA:
8356 vs.caretLineAlpha = wParam;
8357 InvalidateStyleRedraw();
8358 break;
8360 // Folding messages
8362 case SCI_VISIBLEFROMDOCLINE:
8363 return cs.DisplayFromDoc(wParam);
8365 case SCI_DOCLINEFROMVISIBLE:
8366 return cs.DocFromDisplay(wParam);
8368 case SCI_WRAPCOUNT:
8369 return WrapCount(wParam);
8371 case SCI_SETFOLDLEVEL: {
8372 int prev = pdoc->SetLevel(wParam, lParam);
8373 if (prev != lParam)
8374 RedrawSelMargin();
8375 return prev;
8378 case SCI_GETFOLDLEVEL:
8379 return pdoc->GetLevel(wParam);
8381 case SCI_GETLASTCHILD:
8382 return pdoc->GetLastChild(wParam, lParam);
8384 case SCI_GETFOLDPARENT:
8385 return pdoc->GetFoldParent(wParam);
8387 case SCI_SHOWLINES:
8388 cs.SetVisible(wParam, lParam, true);
8389 SetScrollBars();
8390 Redraw();
8391 break;
8393 case SCI_HIDELINES:
8394 if (wParam > 0)
8395 cs.SetVisible(wParam, lParam, false);
8396 SetScrollBars();
8397 Redraw();
8398 break;
8400 case SCI_GETLINEVISIBLE:
8401 return cs.GetVisible(wParam);
8403 case SCI_GETALLLINESVISIBLE:
8404 return cs.HiddenLines() ? 0 : 1;
8406 case SCI_SETFOLDEXPANDED:
8407 if (cs.SetExpanded(wParam, lParam != 0)) {
8408 RedrawSelMargin();
8410 break;
8412 case SCI_GETFOLDEXPANDED:
8413 return cs.GetExpanded(wParam);
8415 case SCI_SETFOLDFLAGS:
8416 foldFlags = wParam;
8417 Redraw();
8418 break;
8420 case SCI_TOGGLEFOLD:
8421 ToggleContraction(wParam);
8422 break;
8424 case SCI_CONTRACTEDFOLDNEXT:
8425 return ContractedFoldNext(wParam);
8427 case SCI_ENSUREVISIBLE:
8428 EnsureLineVisible(wParam, false);
8429 break;
8431 case SCI_ENSUREVISIBLEENFORCEPOLICY:
8432 EnsureLineVisible(wParam, true);
8433 break;
8435 case SCI_SEARCHANCHOR:
8436 SearchAnchor();
8437 break;
8439 case SCI_SEARCHNEXT:
8440 case SCI_SEARCHPREV:
8441 return SearchText(iMessage, wParam, lParam);
8443 case SCI_SETXCARETPOLICY:
8444 caretXPolicy = wParam;
8445 caretXSlop = lParam;
8446 break;
8448 case SCI_SETYCARETPOLICY:
8449 caretYPolicy = wParam;
8450 caretYSlop = lParam;
8451 break;
8453 case SCI_SETVISIBLEPOLICY:
8454 visiblePolicy = wParam;
8455 visibleSlop = lParam;
8456 break;
8458 case SCI_LINESONSCREEN:
8459 return LinesOnScreen();
8461 case SCI_SETSELFORE:
8462 vs.selforeset = wParam != 0;
8463 vs.selforeground = ColourDesired(lParam);
8464 vs.selAdditionalForeground = ColourDesired(lParam);
8465 InvalidateStyleRedraw();
8466 break;
8468 case SCI_SETSELBACK:
8469 vs.selbackset = wParam != 0;
8470 vs.selbackground = ColourDesired(lParam);
8471 vs.selAdditionalBackground = ColourDesired(lParam);
8472 InvalidateStyleRedraw();
8473 break;
8475 case SCI_SETSELALPHA:
8476 vs.selAlpha = wParam;
8477 vs.selAdditionalAlpha = wParam;
8478 InvalidateStyleRedraw();
8479 break;
8481 case SCI_GETSELALPHA:
8482 return vs.selAlpha;
8484 case SCI_GETSELEOLFILLED:
8485 return vs.selEOLFilled;
8487 case SCI_SETSELEOLFILLED:
8488 vs.selEOLFilled = wParam != 0;
8489 InvalidateStyleRedraw();
8490 break;
8492 case SCI_SETWHITESPACEFORE:
8493 vs.whitespaceForegroundSet = wParam != 0;
8494 vs.whitespaceForeground = ColourDesired(lParam);
8495 InvalidateStyleRedraw();
8496 break;
8498 case SCI_SETWHITESPACEBACK:
8499 vs.whitespaceBackgroundSet = wParam != 0;
8500 vs.whitespaceBackground = ColourDesired(lParam);
8501 InvalidateStyleRedraw();
8502 break;
8504 case SCI_SETCARETFORE:
8505 vs.caretcolour = ColourDesired(wParam);
8506 InvalidateStyleRedraw();
8507 break;
8509 case SCI_GETCARETFORE:
8510 return vs.caretcolour.AsLong();
8512 case SCI_SETCARETSTYLE:
8513 if (wParam <= CARETSTYLE_BLOCK)
8514 vs.caretStyle = wParam;
8515 else
8516 /* Default to the line caret */
8517 vs.caretStyle = CARETSTYLE_LINE;
8518 InvalidateStyleRedraw();
8519 break;
8521 case SCI_GETCARETSTYLE:
8522 return vs.caretStyle;
8524 case SCI_SETCARETWIDTH:
8525 if (static_cast<int>(wParam) <= 0)
8526 vs.caretWidth = 0;
8527 else if (wParam >= 3)
8528 vs.caretWidth = 3;
8529 else
8530 vs.caretWidth = wParam;
8531 InvalidateStyleRedraw();
8532 break;
8534 case SCI_GETCARETWIDTH:
8535 return vs.caretWidth;
8537 case SCI_ASSIGNCMDKEY:
8538 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8539 Platform::HighShortFromLong(wParam), lParam);
8540 break;
8542 case SCI_CLEARCMDKEY:
8543 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8544 Platform::HighShortFromLong(wParam), SCI_NULL);
8545 break;
8547 case SCI_CLEARALLCMDKEYS:
8548 kmap.Clear();
8549 break;
8551 case SCI_INDICSETSTYLE:
8552 if (wParam <= INDIC_MAX) {
8553 vs.indicators[wParam].style = lParam;
8554 InvalidateStyleRedraw();
8556 break;
8558 case SCI_INDICGETSTYLE:
8559 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
8561 case SCI_INDICSETFORE:
8562 if (wParam <= INDIC_MAX) {
8563 vs.indicators[wParam].fore = ColourDesired(lParam);
8564 InvalidateStyleRedraw();
8566 break;
8568 case SCI_INDICGETFORE:
8569 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.AsLong() : 0;
8571 case SCI_INDICSETUNDER:
8572 if (wParam <= INDIC_MAX) {
8573 vs.indicators[wParam].under = lParam != 0;
8574 InvalidateStyleRedraw();
8576 break;
8578 case SCI_INDICGETUNDER:
8579 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
8581 case SCI_INDICSETALPHA:
8582 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8583 vs.indicators[wParam].fillAlpha = lParam;
8584 InvalidateStyleRedraw();
8586 break;
8588 case SCI_INDICGETALPHA:
8589 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
8591 case SCI_INDICSETOUTLINEALPHA:
8592 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8593 vs.indicators[wParam].outlineAlpha = lParam;
8594 InvalidateStyleRedraw();
8596 break;
8598 case SCI_INDICGETOUTLINEALPHA:
8599 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
8601 case SCI_SETINDICATORCURRENT:
8602 pdoc->decorations.SetCurrentIndicator(wParam);
8603 break;
8604 case SCI_GETINDICATORCURRENT:
8605 return pdoc->decorations.GetCurrentIndicator();
8606 case SCI_SETINDICATORVALUE:
8607 pdoc->decorations.SetCurrentValue(wParam);
8608 break;
8609 case SCI_GETINDICATORVALUE:
8610 return pdoc->decorations.GetCurrentValue();
8612 case SCI_INDICATORFILLRANGE:
8613 pdoc->DecorationFillRange(wParam, pdoc->decorations.GetCurrentValue(), lParam);
8614 break;
8616 case SCI_INDICATORCLEARRANGE:
8617 pdoc->DecorationFillRange(wParam, 0, lParam);
8618 break;
8620 case SCI_INDICATORALLONFOR:
8621 return pdoc->decorations.AllOnFor(wParam);
8623 case SCI_INDICATORVALUEAT:
8624 return pdoc->decorations.ValueAt(wParam, lParam);
8626 case SCI_INDICATORSTART:
8627 return pdoc->decorations.Start(wParam, lParam);
8629 case SCI_INDICATOREND:
8630 return pdoc->decorations.End(wParam, lParam);
8632 case SCI_LINEDOWN:
8633 case SCI_LINEDOWNEXTEND:
8634 case SCI_PARADOWN:
8635 case SCI_PARADOWNEXTEND:
8636 case SCI_LINEUP:
8637 case SCI_LINEUPEXTEND:
8638 case SCI_PARAUP:
8639 case SCI_PARAUPEXTEND:
8640 case SCI_CHARLEFT:
8641 case SCI_CHARLEFTEXTEND:
8642 case SCI_CHARRIGHT:
8643 case SCI_CHARRIGHTEXTEND:
8644 case SCI_WORDLEFT:
8645 case SCI_WORDLEFTEXTEND:
8646 case SCI_WORDRIGHT:
8647 case SCI_WORDRIGHTEXTEND:
8648 case SCI_WORDLEFTEND:
8649 case SCI_WORDLEFTENDEXTEND:
8650 case SCI_WORDRIGHTEND:
8651 case SCI_WORDRIGHTENDEXTEND:
8652 case SCI_HOME:
8653 case SCI_HOMEEXTEND:
8654 case SCI_LINEEND:
8655 case SCI_LINEENDEXTEND:
8656 case SCI_HOMEWRAP:
8657 case SCI_HOMEWRAPEXTEND:
8658 case SCI_LINEENDWRAP:
8659 case SCI_LINEENDWRAPEXTEND:
8660 case SCI_DOCUMENTSTART:
8661 case SCI_DOCUMENTSTARTEXTEND:
8662 case SCI_DOCUMENTEND:
8663 case SCI_DOCUMENTENDEXTEND:
8664 case SCI_SCROLLTOSTART:
8665 case SCI_SCROLLTOEND:
8667 case SCI_STUTTEREDPAGEUP:
8668 case SCI_STUTTEREDPAGEUPEXTEND:
8669 case SCI_STUTTEREDPAGEDOWN:
8670 case SCI_STUTTEREDPAGEDOWNEXTEND:
8672 case SCI_PAGEUP:
8673 case SCI_PAGEUPEXTEND:
8674 case SCI_PAGEDOWN:
8675 case SCI_PAGEDOWNEXTEND:
8676 case SCI_EDITTOGGLEOVERTYPE:
8677 case SCI_CANCEL:
8678 case SCI_DELETEBACK:
8679 case SCI_TAB:
8680 case SCI_BACKTAB:
8681 case SCI_NEWLINE:
8682 case SCI_FORMFEED:
8683 case SCI_VCHOME:
8684 case SCI_VCHOMEEXTEND:
8685 case SCI_VCHOMEWRAP:
8686 case SCI_VCHOMEWRAPEXTEND:
8687 case SCI_VCHOMEDISPLAY:
8688 case SCI_VCHOMEDISPLAYEXTEND:
8689 case SCI_ZOOMIN:
8690 case SCI_ZOOMOUT:
8691 case SCI_DELWORDLEFT:
8692 case SCI_DELWORDRIGHT:
8693 case SCI_DELWORDRIGHTEND:
8694 case SCI_DELLINELEFT:
8695 case SCI_DELLINERIGHT:
8696 case SCI_LINECOPY:
8697 case SCI_LINECUT:
8698 case SCI_LINEDELETE:
8699 case SCI_LINETRANSPOSE:
8700 case SCI_LINEDUPLICATE:
8701 case SCI_LOWERCASE:
8702 case SCI_UPPERCASE:
8703 case SCI_LINESCROLLDOWN:
8704 case SCI_LINESCROLLUP:
8705 case SCI_WORDPARTLEFT:
8706 case SCI_WORDPARTLEFTEXTEND:
8707 case SCI_WORDPARTRIGHT:
8708 case SCI_WORDPARTRIGHTEXTEND:
8709 case SCI_DELETEBACKNOTLINE:
8710 case SCI_HOMEDISPLAY:
8711 case SCI_HOMEDISPLAYEXTEND:
8712 case SCI_LINEENDDISPLAY:
8713 case SCI_LINEENDDISPLAYEXTEND:
8714 case SCI_LINEDOWNRECTEXTEND:
8715 case SCI_LINEUPRECTEXTEND:
8716 case SCI_CHARLEFTRECTEXTEND:
8717 case SCI_CHARRIGHTRECTEXTEND:
8718 case SCI_HOMERECTEXTEND:
8719 case SCI_VCHOMERECTEXTEND:
8720 case SCI_LINEENDRECTEXTEND:
8721 case SCI_PAGEUPRECTEXTEND:
8722 case SCI_PAGEDOWNRECTEXTEND:
8723 case SCI_SELECTIONDUPLICATE:
8724 return KeyCommand(iMessage);
8726 case SCI_BRACEHIGHLIGHT:
8727 SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
8728 break;
8730 case SCI_BRACEHIGHLIGHTINDICATOR:
8731 if (lParam >= 0 && lParam <= INDIC_MAX) {
8732 vs.braceHighlightIndicatorSet = wParam != 0;
8733 vs.braceHighlightIndicator = lParam;
8735 break;
8737 case SCI_BRACEBADLIGHT:
8738 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
8739 break;
8741 case SCI_BRACEBADLIGHTINDICATOR:
8742 if (lParam >= 0 && lParam <= INDIC_MAX) {
8743 vs.braceBadLightIndicatorSet = wParam != 0;
8744 vs.braceBadLightIndicator = lParam;
8746 break;
8748 case SCI_BRACEMATCH:
8749 // wParam is position of char to find brace for,
8750 // lParam is maximum amount of text to restyle to find it
8751 return pdoc->BraceMatch(wParam, lParam);
8753 case SCI_GETVIEWEOL:
8754 return vs.viewEOL;
8756 case SCI_SETVIEWEOL:
8757 vs.viewEOL = wParam != 0;
8758 InvalidateStyleRedraw();
8759 break;
8761 case SCI_SETZOOM:
8762 vs.zoomLevel = wParam;
8763 InvalidateStyleRedraw();
8764 NotifyZoom();
8765 break;
8767 case SCI_GETZOOM:
8768 return vs.zoomLevel;
8770 case SCI_GETEDGECOLUMN:
8771 return theEdge;
8773 case SCI_SETEDGECOLUMN:
8774 theEdge = wParam;
8775 InvalidateStyleRedraw();
8776 break;
8778 case SCI_GETEDGEMODE:
8779 return vs.edgeState;
8781 case SCI_SETEDGEMODE:
8782 vs.edgeState = wParam;
8783 InvalidateStyleRedraw();
8784 break;
8786 case SCI_GETEDGECOLOUR:
8787 return vs.edgecolour.AsLong();
8789 case SCI_SETEDGECOLOUR:
8790 vs.edgecolour = ColourDesired(wParam);
8791 InvalidateStyleRedraw();
8792 break;
8794 case SCI_GETDOCPOINTER:
8795 return reinterpret_cast<sptr_t>(pdoc);
8797 case SCI_SETDOCPOINTER:
8798 CancelModes();
8799 SetDocPointer(reinterpret_cast<Document *>(lParam));
8800 return 0;
8802 case SCI_CREATEDOCUMENT: {
8803 Document *doc = new Document();
8804 if (doc) {
8805 doc->AddRef();
8807 return reinterpret_cast<sptr_t>(doc);
8810 case SCI_ADDREFDOCUMENT:
8811 (reinterpret_cast<Document *>(lParam))->AddRef();
8812 break;
8814 case SCI_RELEASEDOCUMENT:
8815 (reinterpret_cast<Document *>(lParam))->Release();
8816 break;
8818 case SCI_CREATELOADER: {
8819 Document *doc = new Document();
8820 if (doc) {
8821 doc->AddRef();
8822 doc->Allocate(wParam);
8823 doc->SetUndoCollection(false);
8825 return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
8828 case SCI_SETMODEVENTMASK:
8829 modEventMask = wParam;
8830 return 0;
8832 case SCI_GETMODEVENTMASK:
8833 return modEventMask;
8835 case SCI_CONVERTEOLS:
8836 pdoc->ConvertLineEnds(wParam);
8837 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
8838 return 0;
8840 case SCI_SETLENGTHFORENCODE:
8841 lengthForEncode = wParam;
8842 return 0;
8844 case SCI_SELECTIONISRECTANGLE:
8845 return sel.selType == Selection::selRectangle ? 1 : 0;
8847 case SCI_SETSELECTIONMODE: {
8848 switch (wParam) {
8849 case SC_SEL_STREAM:
8850 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
8851 sel.selType = Selection::selStream;
8852 break;
8853 case SC_SEL_RECTANGLE:
8854 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
8855 sel.selType = Selection::selRectangle;
8856 break;
8857 case SC_SEL_LINES:
8858 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
8859 sel.selType = Selection::selLines;
8860 break;
8861 case SC_SEL_THIN:
8862 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
8863 sel.selType = Selection::selThin;
8864 break;
8865 default:
8866 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
8867 sel.selType = Selection::selStream;
8869 InvalidateSelection(sel.RangeMain(), true);
8871 case SCI_GETSELECTIONMODE:
8872 switch (sel.selType) {
8873 case Selection::selStream:
8874 return SC_SEL_STREAM;
8875 case Selection::selRectangle:
8876 return SC_SEL_RECTANGLE;
8877 case Selection::selLines:
8878 return SC_SEL_LINES;
8879 case Selection::selThin:
8880 return SC_SEL_THIN;
8881 default: // ?!
8882 return SC_SEL_STREAM;
8884 case SCI_GETLINESELSTARTPOSITION:
8885 case SCI_GETLINESELENDPOSITION: {
8886 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(wParam)),
8887 SelectionPosition(pdoc->LineEnd(wParam)));
8888 for (size_t r=0; r<sel.Count(); r++) {
8889 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
8890 if (portion.start.IsValid()) {
8891 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
8894 return INVALID_POSITION;
8897 case SCI_SETOVERTYPE:
8898 inOverstrike = wParam != 0;
8899 break;
8901 case SCI_GETOVERTYPE:
8902 return inOverstrike ? 1 : 0;
8904 case SCI_SETFOCUS:
8905 SetFocusState(wParam != 0);
8906 break;
8908 case SCI_GETFOCUS:
8909 return hasFocus;
8911 case SCI_SETSTATUS:
8912 errorStatus = wParam;
8913 break;
8915 case SCI_GETSTATUS:
8916 return errorStatus;
8918 case SCI_SETMOUSEDOWNCAPTURES:
8919 mouseDownCaptures = wParam != 0;
8920 break;
8922 case SCI_GETMOUSEDOWNCAPTURES:
8923 return mouseDownCaptures;
8925 case SCI_SETCURSOR:
8926 cursorMode = wParam;
8927 DisplayCursor(Window::cursorText);
8928 break;
8930 case SCI_GETCURSOR:
8931 return cursorMode;
8933 case SCI_SETCONTROLCHARSYMBOL:
8934 controlCharSymbol = wParam;
8935 break;
8937 case SCI_GETCONTROLCHARSYMBOL:
8938 return controlCharSymbol;
8940 case SCI_STARTRECORD:
8941 recordingMacro = true;
8942 return 0;
8944 case SCI_STOPRECORD:
8945 recordingMacro = false;
8946 return 0;
8948 case SCI_MOVECARETINSIDEVIEW:
8949 MoveCaretInsideView();
8950 break;
8952 case SCI_SETFOLDMARGINCOLOUR:
8953 vs.foldmarginColourSet = wParam != 0;
8954 vs.foldmarginColour = ColourDesired(lParam);
8955 InvalidateStyleRedraw();
8956 break;
8958 case SCI_SETFOLDMARGINHICOLOUR:
8959 vs.foldmarginHighlightColourSet = wParam != 0;
8960 vs.foldmarginHighlightColour = ColourDesired(lParam);
8961 InvalidateStyleRedraw();
8962 break;
8964 case SCI_SETHOTSPOTACTIVEFORE:
8965 vs.hotspotForegroundSet = wParam != 0;
8966 vs.hotspotForeground = ColourDesired(lParam);
8967 InvalidateStyleRedraw();
8968 break;
8970 case SCI_GETHOTSPOTACTIVEFORE:
8971 return vs.hotspotForeground.AsLong();
8973 case SCI_SETHOTSPOTACTIVEBACK:
8974 vs.hotspotBackgroundSet = wParam != 0;
8975 vs.hotspotBackground = ColourDesired(lParam);
8976 InvalidateStyleRedraw();
8977 break;
8979 case SCI_GETHOTSPOTACTIVEBACK:
8980 return vs.hotspotBackground.AsLong();
8982 case SCI_SETHOTSPOTACTIVEUNDERLINE:
8983 vs.hotspotUnderline = wParam != 0;
8984 InvalidateStyleRedraw();
8985 break;
8987 case SCI_GETHOTSPOTACTIVEUNDERLINE:
8988 return vs.hotspotUnderline ? 1 : 0;
8990 case SCI_SETHOTSPOTSINGLELINE:
8991 vs.hotspotSingleLine = wParam != 0;
8992 InvalidateStyleRedraw();
8993 break;
8995 case SCI_GETHOTSPOTSINGLELINE:
8996 return vs.hotspotSingleLine ? 1 : 0;
8998 case SCI_SETPASTECONVERTENDINGS:
8999 convertPastes = wParam != 0;
9000 break;
9002 case SCI_GETPASTECONVERTENDINGS:
9003 return convertPastes ? 1 : 0;
9005 case SCI_GETCHARACTERPOINTER:
9006 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
9008 case SCI_GETRANGEPOINTER:
9009 return reinterpret_cast<sptr_t>(pdoc->RangePointer(wParam, lParam));
9011 case SCI_GETGAPPOSITION:
9012 return pdoc->GapPosition();
9014 case SCI_SETEXTRAASCENT:
9015 vs.extraAscent = wParam;
9016 InvalidateStyleRedraw();
9017 break;
9019 case SCI_GETEXTRAASCENT:
9020 return vs.extraAscent;
9022 case SCI_SETEXTRADESCENT:
9023 vs.extraDescent = wParam;
9024 InvalidateStyleRedraw();
9025 break;
9027 case SCI_GETEXTRADESCENT:
9028 return vs.extraDescent;
9030 case SCI_MARGINSETSTYLEOFFSET:
9031 vs.marginStyleOffset = wParam;
9032 InvalidateStyleRedraw();
9033 break;
9035 case SCI_MARGINGETSTYLEOFFSET:
9036 return vs.marginStyleOffset;
9038 case SCI_SETMARGINOPTIONS:
9039 marginOptions = wParam;
9040 break;
9042 case SCI_GETMARGINOPTIONS:
9043 return marginOptions;
9045 case SCI_MARGINSETTEXT:
9046 pdoc->MarginSetText(wParam, CharPtrFromSPtr(lParam));
9047 break;
9049 case SCI_MARGINGETTEXT: {
9050 const StyledText st = pdoc->MarginStyledText(wParam);
9051 if (lParam) {
9052 if (st.text)
9053 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
9054 else
9055 strcpy(CharPtrFromSPtr(lParam), "");
9057 return st.length;
9060 case SCI_MARGINSETSTYLE:
9061 pdoc->MarginSetStyle(wParam, lParam);
9062 break;
9064 case SCI_MARGINGETSTYLE: {
9065 const StyledText st = pdoc->MarginStyledText(wParam);
9066 return st.style;
9069 case SCI_MARGINSETSTYLES:
9070 pdoc->MarginSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
9071 break;
9073 case SCI_MARGINGETSTYLES: {
9074 const StyledText st = pdoc->MarginStyledText(wParam);
9075 if (lParam) {
9076 if (st.styles)
9077 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
9078 else
9079 strcpy(CharPtrFromSPtr(lParam), "");
9081 return st.styles ? st.length : 0;
9084 case SCI_MARGINTEXTCLEARALL:
9085 pdoc->MarginClearAll();
9086 break;
9088 case SCI_ANNOTATIONSETTEXT:
9089 pdoc->AnnotationSetText(wParam, CharPtrFromSPtr(lParam));
9090 break;
9092 case SCI_ANNOTATIONGETTEXT: {
9093 const StyledText st = pdoc->AnnotationStyledText(wParam);
9094 if (lParam) {
9095 if (st.text)
9096 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
9097 else
9098 strcpy(CharPtrFromSPtr(lParam), "");
9100 return st.length;
9103 case SCI_ANNOTATIONGETSTYLE: {
9104 const StyledText st = pdoc->AnnotationStyledText(wParam);
9105 return st.style;
9108 case SCI_ANNOTATIONSETSTYLE:
9109 pdoc->AnnotationSetStyle(wParam, lParam);
9110 break;
9112 case SCI_ANNOTATIONSETSTYLES:
9113 pdoc->AnnotationSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
9114 break;
9116 case SCI_ANNOTATIONGETSTYLES: {
9117 const StyledText st = pdoc->AnnotationStyledText(wParam);
9118 if (lParam) {
9119 if (st.styles)
9120 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
9121 else
9122 strcpy(CharPtrFromSPtr(lParam), "");
9124 return st.styles ? st.length : 0;
9127 case SCI_ANNOTATIONGETLINES:
9128 return pdoc->AnnotationLines(wParam);
9130 case SCI_ANNOTATIONCLEARALL:
9131 pdoc->AnnotationClearAll();
9132 break;
9134 case SCI_ANNOTATIONSETVISIBLE:
9135 SetAnnotationVisible(wParam);
9136 break;
9138 case SCI_ANNOTATIONGETVISIBLE:
9139 return vs.annotationVisible;
9141 case SCI_ANNOTATIONSETSTYLEOFFSET:
9142 vs.annotationStyleOffset = wParam;
9143 InvalidateStyleRedraw();
9144 break;
9146 case SCI_ANNOTATIONGETSTYLEOFFSET:
9147 return vs.annotationStyleOffset;
9149 case SCI_ADDUNDOACTION:
9150 pdoc->AddUndoAction(wParam, lParam & UNDO_MAY_COALESCE);
9151 break;
9153 case SCI_SETMULTIPLESELECTION:
9154 multipleSelection = wParam != 0;
9155 InvalidateCaret();
9156 break;
9158 case SCI_GETMULTIPLESELECTION:
9159 return multipleSelection;
9161 case SCI_SETADDITIONALSELECTIONTYPING:
9162 additionalSelectionTyping = wParam != 0;
9163 InvalidateCaret();
9164 break;
9166 case SCI_GETADDITIONALSELECTIONTYPING:
9167 return additionalSelectionTyping;
9169 case SCI_SETMULTIPASTE:
9170 multiPasteMode = wParam;
9171 break;
9173 case SCI_GETMULTIPASTE:
9174 return multiPasteMode;
9176 case SCI_SETADDITIONALCARETSBLINK:
9177 additionalCaretsBlink = wParam != 0;
9178 InvalidateCaret();
9179 break;
9181 case SCI_GETADDITIONALCARETSBLINK:
9182 return additionalCaretsBlink;
9184 case SCI_SETADDITIONALCARETSVISIBLE:
9185 additionalCaretsVisible = wParam != 0;
9186 InvalidateCaret();
9187 break;
9189 case SCI_GETADDITIONALCARETSVISIBLE:
9190 return additionalCaretsVisible;
9192 case SCI_GETSELECTIONS:
9193 return sel.Count();
9195 case SCI_GETSELECTIONEMPTY:
9196 return sel.Empty();
9198 case SCI_CLEARSELECTIONS:
9199 sel.Clear();
9200 Redraw();
9201 break;
9203 case SCI_SETSELECTION:
9204 sel.SetSelection(SelectionRange(wParam, lParam));
9205 Redraw();
9206 break;
9208 case SCI_ADDSELECTION:
9209 sel.AddSelection(SelectionRange(wParam, lParam));
9210 Redraw();
9211 break;
9213 case SCI_SETMAINSELECTION:
9214 sel.SetMain(wParam);
9215 Redraw();
9216 break;
9218 case SCI_GETMAINSELECTION:
9219 return sel.Main();
9221 case SCI_SETSELECTIONNCARET:
9222 sel.Range(wParam).caret.SetPosition(lParam);
9223 Redraw();
9224 break;
9226 case SCI_GETSELECTIONNCARET:
9227 return sel.Range(wParam).caret.Position();
9229 case SCI_SETSELECTIONNANCHOR:
9230 sel.Range(wParam).anchor.SetPosition(lParam);
9231 Redraw();
9232 break;
9233 case SCI_GETSELECTIONNANCHOR:
9234 return sel.Range(wParam).anchor.Position();
9236 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
9237 sel.Range(wParam).caret.SetVirtualSpace(lParam);
9238 Redraw();
9239 break;
9241 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
9242 return sel.Range(wParam).caret.VirtualSpace();
9244 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
9245 sel.Range(wParam).anchor.SetVirtualSpace(lParam);
9246 Redraw();
9247 break;
9249 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
9250 return sel.Range(wParam).anchor.VirtualSpace();
9252 case SCI_SETSELECTIONNSTART:
9253 sel.Range(wParam).anchor.SetPosition(lParam);
9254 Redraw();
9255 break;
9257 case SCI_GETSELECTIONNSTART:
9258 return sel.Range(wParam).Start().Position();
9260 case SCI_SETSELECTIONNEND:
9261 sel.Range(wParam).caret.SetPosition(lParam);
9262 Redraw();
9263 break;
9265 case SCI_GETSELECTIONNEND:
9266 return sel.Range(wParam).End().Position();
9268 case SCI_SETRECTANGULARSELECTIONCARET:
9269 if (!sel.IsRectangular())
9270 sel.Clear();
9271 sel.selType = Selection::selRectangle;
9272 sel.Rectangular().caret.SetPosition(wParam);
9273 SetRectangularRange();
9274 Redraw();
9275 break;
9277 case SCI_GETRECTANGULARSELECTIONCARET:
9278 return sel.Rectangular().caret.Position();
9280 case SCI_SETRECTANGULARSELECTIONANCHOR:
9281 if (!sel.IsRectangular())
9282 sel.Clear();
9283 sel.selType = Selection::selRectangle;
9284 sel.Rectangular().anchor.SetPosition(wParam);
9285 SetRectangularRange();
9286 Redraw();
9287 break;
9289 case SCI_GETRECTANGULARSELECTIONANCHOR:
9290 return sel.Rectangular().anchor.Position();
9292 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9293 if (!sel.IsRectangular())
9294 sel.Clear();
9295 sel.selType = Selection::selRectangle;
9296 sel.Rectangular().caret.SetVirtualSpace(wParam);
9297 SetRectangularRange();
9298 Redraw();
9299 break;
9301 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9302 return sel.Rectangular().caret.VirtualSpace();
9304 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9305 if (!sel.IsRectangular())
9306 sel.Clear();
9307 sel.selType = Selection::selRectangle;
9308 sel.Rectangular().anchor.SetVirtualSpace(wParam);
9309 SetRectangularRange();
9310 Redraw();
9311 break;
9313 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9314 return sel.Rectangular().anchor.VirtualSpace();
9316 case SCI_SETVIRTUALSPACEOPTIONS:
9317 virtualSpaceOptions = wParam;
9318 break;
9320 case SCI_GETVIRTUALSPACEOPTIONS:
9321 return virtualSpaceOptions;
9323 case SCI_SETADDITIONALSELFORE:
9324 vs.selAdditionalForeground = ColourDesired(wParam);
9325 InvalidateStyleRedraw();
9326 break;
9328 case SCI_SETADDITIONALSELBACK:
9329 vs.selAdditionalBackground = ColourDesired(wParam);
9330 InvalidateStyleRedraw();
9331 break;
9333 case SCI_SETADDITIONALSELALPHA:
9334 vs.selAdditionalAlpha = wParam;
9335 InvalidateStyleRedraw();
9336 break;
9338 case SCI_GETADDITIONALSELALPHA:
9339 return vs.selAdditionalAlpha;
9341 case SCI_SETADDITIONALCARETFORE:
9342 vs.additionalCaretColour = ColourDesired(wParam);
9343 InvalidateStyleRedraw();
9344 break;
9346 case SCI_GETADDITIONALCARETFORE:
9347 return vs.additionalCaretColour.AsLong();
9349 case SCI_ROTATESELECTION:
9350 sel.RotateMain();
9351 InvalidateSelection(sel.RangeMain(), true);
9352 break;
9354 case SCI_SWAPMAINANCHORCARET:
9355 InvalidateSelection(sel.RangeMain());
9356 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
9357 break;
9359 case SCI_CHANGELEXERSTATE:
9360 pdoc->ChangeLexerState(wParam, lParam);
9361 break;
9363 case SCI_SETIDENTIFIER:
9364 SetCtrlID(wParam);
9365 break;
9367 case SCI_GETIDENTIFIER:
9368 return GetCtrlID();
9370 case SCI_SETTECHNOLOGY:
9371 // No action by default
9372 break;
9374 case SCI_GETTECHNOLOGY:
9375 return technology;
9377 case SCI_COUNTCHARACTERS:
9378 return pdoc->CountCharacters(wParam, lParam);
9380 default:
9381 return DefWndProc(iMessage, wParam, lParam);
9383 //Platform::DebugPrintf("end wnd proc\n");
9384 return 0l;