applied backgroundcolors.patch
[TortoiseGit.git] / ext / scintilla / src / Editor.cxx
blobdc5f2694acf70996975776e6b4dec51697d4399b
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.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.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 // Don't highlight matching braces using indicators
3753 vsPrint.braceHighlightIndicatorSet = false;
3754 vsPrint.braceBadLightIndicatorSet = false;
3756 // Set colours for printing according to users settings
3757 for (size_t sty = 0; sty < vsPrint.stylesSize; sty++) {
3758 if (printColourMode == SC_PRINT_INVERTLIGHT) {
3759 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
3760 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
3761 } else if (printColourMode == SC_PRINT_BLACKONWHITE) {
3762 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
3763 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3764 } else if (printColourMode == SC_PRINT_COLOURONWHITE) {
3765 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3766 } else if (printColourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
3767 if (sty <= STYLE_DEFAULT) {
3768 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3772 // White background for the line numbers
3773 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
3775 // Printing uses different margins, so reset screen margins
3776 vsPrint.leftMarginWidth = 0;
3777 vsPrint.rightMarginWidth = 0;
3779 vsPrint.Refresh(*surfaceMeasure);
3780 // Determining width must hapen after fonts have been realised in Refresh
3781 int lineNumberWidth = 0;
3782 if (lineNumberIndex >= 0) {
3783 lineNumberWidth = surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
3784 "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace));
3785 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
3786 vsPrint.Refresh(*surfaceMeasure); // Recalculate fixedColumnWidth
3789 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
3790 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
3791 if (linePrintLast < linePrintStart)
3792 linePrintLast = linePrintStart;
3793 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
3794 if (linePrintLast > linePrintMax)
3795 linePrintLast = linePrintMax;
3796 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
3797 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
3798 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
3799 int endPosPrint = pdoc->Length();
3800 if (linePrintLast < pdoc->LinesTotal())
3801 endPosPrint = pdoc->LineStart(linePrintLast + 1);
3803 // Ensure we are styled to where we are formatting.
3804 pdoc->EnsureStyledTo(endPosPrint);
3806 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
3807 int ypos = pfr->rc.top;
3809 int lineDoc = linePrintStart;
3811 int nPrintPos = pfr->chrg.cpMin;
3812 int visibleLine = 0;
3813 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
3814 if (printWrapState == eWrapNone)
3815 widthPrint = LineLayout::wrapWidthInfinite;
3817 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
3819 // When printing, the hdc and hdcTarget may be the same, so
3820 // changing the state of surfaceMeasure may change the underlying
3821 // state of surface. Therefore, any cached state is discarded before
3822 // using each surface.
3823 surfaceMeasure->FlushCachedState();
3825 // Copy this line and its styles from the document into local arrays
3826 // and determine the x position at which each character starts.
3827 LineLayout ll(8000);
3828 LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
3830 ll.containsCaret = false;
3832 PRectangle rcLine;
3833 rcLine.left = pfr->rc.left;
3834 rcLine.top = ypos;
3835 rcLine.right = pfr->rc.right - 1;
3836 rcLine.bottom = ypos + vsPrint.lineHeight;
3838 // When document line is wrapped over multiple display lines, find where
3839 // to start printing from to ensure a particular position is on the first
3840 // line of the page.
3841 if (visibleLine == 0) {
3842 int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
3843 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
3844 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
3845 visibleLine = -iwl;
3849 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
3850 visibleLine = -(ll.lines - 1);
3854 if (draw && lineNumberWidth &&
3855 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
3856 (visibleLine >= 0)) {
3857 char number[100];
3858 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
3859 PRectangle rcNumber = rcLine;
3860 rcNumber.right = rcNumber.left + lineNumberWidth;
3861 // Right justify
3862 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
3863 vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
3864 surface->FlushCachedState();
3865 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
3866 ypos + vsPrint.maxAscent, number, istrlen(number),
3867 vsPrint.styles[STYLE_LINENUMBER].fore,
3868 vsPrint.styles[STYLE_LINENUMBER].back);
3871 // Draw the line
3872 surface->FlushCachedState();
3874 for (int iwl = 0; iwl < ll.lines; iwl++) {
3875 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
3876 if (visibleLine >= 0) {
3877 if (draw) {
3878 rcLine.top = ypos;
3879 rcLine.bottom = ypos + vsPrint.lineHeight;
3880 DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
3882 ypos += vsPrint.lineHeight;
3884 visibleLine++;
3885 if (iwl == ll.lines - 1)
3886 nPrintPos = pdoc->LineStart(lineDoc + 1);
3887 else
3888 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
3892 ++lineDoc;
3895 // Clear cache so measurements are not used for screen
3896 posCache.Clear();
3898 return nPrintPos;
3901 int Editor::TextWidth(int style, const char *text) {
3902 RefreshStyleData();
3903 AutoSurface surface(this);
3904 if (surface) {
3905 return surface->WidthText(vs.styles[style].font, text, istrlen(text));
3906 } else {
3907 return 1;
3911 // Empty method is overridden on GTK+ to show / hide scrollbars
3912 void Editor::ReconfigureScrollBars() {}
3914 void Editor::SetScrollBars() {
3915 RefreshStyleData();
3917 int nMax = MaxScrollPos();
3918 int nPage = LinesOnScreen();
3919 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
3920 if (modified) {
3921 DwellEnd(true);
3924 // TODO: ensure always showing as many lines as possible
3925 // May not be, if, for example, window made larger
3926 if (topLine > MaxScrollPos()) {
3927 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
3928 SetVerticalScrollPos();
3929 Redraw();
3931 if (modified) {
3932 if (!AbandonPaint())
3933 Redraw();
3935 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
3938 void Editor::ChangeSize() {
3939 DropGraphics(false);
3940 SetScrollBars();
3941 if (wrapState != eWrapNone) {
3942 PRectangle rcTextArea = GetClientRectangle();
3943 rcTextArea.left = vs.fixedColumnWidth;
3944 rcTextArea.right -= vs.rightMarginWidth;
3945 if (wrapWidth != rcTextArea.Width()) {
3946 NeedWrapping();
3947 Redraw();
3952 int Editor::InsertSpace(int position, unsigned int spaces) {
3953 if (spaces > 0) {
3954 std::string spaceText(spaces, ' ');
3955 pdoc->InsertString(position, spaceText.c_str(), spaces);
3956 position += spaces;
3958 return position;
3961 void Editor::AddChar(char ch) {
3962 char s[2];
3963 s[0] = ch;
3964 s[1] = '\0';
3965 AddCharUTF(s, 1);
3968 void Editor::FilterSelections() {
3969 if (!additionalSelectionTyping && (sel.Count() > 1)) {
3970 SelectionRange rangeOnly = sel.RangeMain();
3971 InvalidateSelection(rangeOnly, true);
3972 sel.SetSelection(rangeOnly);
3976 static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
3977 return *a < *b;
3980 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
3981 void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
3982 FilterSelections();
3984 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
3986 std::vector<SelectionRange *> selPtrs;
3987 for (size_t r = 0; r < sel.Count(); r++) {
3988 selPtrs.push_back(&sel.Range(r));
3990 std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
3992 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
3993 rit != selPtrs.rend(); ++rit) {
3994 SelectionRange *currentSel = *rit;
3995 if (!RangeContainsProtected(currentSel->Start().Position(),
3996 currentSel->End().Position())) {
3997 int positionInsert = currentSel->Start().Position();
3998 if (!currentSel->Empty()) {
3999 if (currentSel->Length()) {
4000 pdoc->DeleteChars(positionInsert, currentSel->Length());
4001 currentSel->ClearVirtualSpace();
4002 } else {
4003 // Range is all virtual so collapse to start of virtual space
4004 currentSel->MinimizeVirtualSpace();
4006 } else if (inOverstrike) {
4007 if (positionInsert < pdoc->Length()) {
4008 if (!IsEOLChar(pdoc->CharAt(positionInsert))) {
4009 pdoc->DelChar(positionInsert);
4010 currentSel->ClearVirtualSpace();
4014 positionInsert = InsertSpace(positionInsert, currentSel->caret.VirtualSpace());
4015 if (pdoc->InsertString(positionInsert, s, len)) {
4016 currentSel->caret.SetPosition(positionInsert + len);
4017 currentSel->anchor.SetPosition(positionInsert + len);
4019 currentSel->ClearVirtualSpace();
4020 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4021 if (wrapState != eWrapNone) {
4022 AutoSurface surface(this);
4023 if (surface) {
4024 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
4025 SetScrollBars();
4026 SetVerticalScrollPos();
4027 Redraw();
4034 if (wrapState != eWrapNone) {
4035 SetScrollBars();
4037 ThinRectangularRange();
4038 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4039 EnsureCaretVisible();
4040 // Avoid blinking during rapid typing:
4041 ShowCaretAtCurrentPosition();
4042 if ((caretSticky == SC_CARETSTICKY_OFF) ||
4043 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
4044 SetLastXChosen();
4047 if (treatAsDBCS) {
4048 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
4049 static_cast<unsigned char>(s[1]));
4050 } else {
4051 int byte = static_cast<unsigned char>(s[0]);
4052 if ((byte < 0xC0) || (1 == len)) {
4053 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
4054 // characters when not in UTF-8 mode.
4055 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
4056 // characters representing themselves.
4057 } else {
4058 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
4059 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
4060 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
4061 if (byte < 0xE0) {
4062 int byte2 = static_cast<unsigned char>(s[1]);
4063 if ((byte2 & 0xC0) == 0x80) {
4064 // Two-byte-character lead-byte followed by a trail-byte.
4065 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
4067 // A two-byte-character lead-byte not followed by trail-byte
4068 // represents itself.
4069 } else if (byte < 0xF0) {
4070 int byte2 = static_cast<unsigned char>(s[1]);
4071 int byte3 = static_cast<unsigned char>(s[2]);
4072 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
4073 // Three-byte-character lead byte followed by two trail bytes.
4074 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
4075 (byte3 & 0x3F));
4077 // A three-byte-character lead-byte not followed by two trail-bytes
4078 // represents itself.
4081 NotifyChar(byte);
4084 if (recordingMacro) {
4085 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
4089 void Editor::InsertPaste(SelectionPosition selStart, const char *text, int len) {
4090 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
4091 selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
4092 if (pdoc->InsertString(selStart.Position(), text, len)) {
4093 SetEmptySelection(selStart.Position() + len);
4095 } else {
4096 // SC_MULTIPASTE_EACH
4097 for (size_t r=0; r<sel.Count(); r++) {
4098 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4099 sel.Range(r).End().Position())) {
4100 int positionInsert = sel.Range(r).Start().Position();
4101 if (!sel.Range(r).Empty()) {
4102 if (sel.Range(r).Length()) {
4103 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
4104 sel.Range(r).ClearVirtualSpace();
4105 } else {
4106 // Range is all virtual so collapse to start of virtual space
4107 sel.Range(r).MinimizeVirtualSpace();
4110 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
4111 if (pdoc->InsertString(positionInsert, text, len)) {
4112 sel.Range(r).caret.SetPosition(positionInsert + len);
4113 sel.Range(r).anchor.SetPosition(positionInsert + len);
4115 sel.Range(r).ClearVirtualSpace();
4121 void Editor::ClearSelection(bool retainMultipleSelections) {
4122 if (!sel.IsRectangular() && !retainMultipleSelections)
4123 FilterSelections();
4124 UndoGroup ug(pdoc);
4125 for (size_t r=0; r<sel.Count(); r++) {
4126 if (!sel.Range(r).Empty()) {
4127 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4128 sel.Range(r).End().Position())) {
4129 pdoc->DeleteChars(sel.Range(r).Start().Position(),
4130 sel.Range(r).Length());
4131 sel.Range(r) = sel.Range(r).Start();
4135 ThinRectangularRange();
4136 sel.RemoveDuplicates();
4137 ClaimSelection();
4140 void Editor::ClearAll() {
4142 UndoGroup ug(pdoc);
4143 if (0 != pdoc->Length()) {
4144 pdoc->DeleteChars(0, pdoc->Length());
4146 if (!pdoc->IsReadOnly()) {
4147 cs.Clear();
4148 pdoc->AnnotationClearAll();
4149 pdoc->MarginClearAll();
4152 sel.Clear();
4153 SetTopLine(0);
4154 SetVerticalScrollPos();
4155 InvalidateStyleRedraw();
4158 void Editor::ClearDocumentStyle() {
4159 Decoration *deco = pdoc->decorations.root;
4160 while (deco) {
4161 // Save next in case deco deleted
4162 Decoration *decoNext = deco->next;
4163 if (deco->indicator < INDIC_CONTAINER) {
4164 pdoc->decorations.SetCurrentIndicator(deco->indicator);
4165 pdoc->DecorationFillRange(0, 0, pdoc->Length());
4167 deco = decoNext;
4169 pdoc->StartStyling(0, '\377');
4170 pdoc->SetStyleFor(pdoc->Length(), 0);
4171 cs.ShowAll();
4172 pdoc->ClearLevels();
4175 void Editor::CopyAllowLine() {
4176 SelectionText selectedText;
4177 CopySelectionRange(&selectedText, true);
4178 CopyToClipboard(selectedText);
4181 void Editor::Cut() {
4182 pdoc->CheckReadOnly();
4183 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
4184 Copy();
4185 ClearSelection();
4189 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
4190 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
4191 return;
4193 sel.Clear();
4194 sel.RangeMain() = SelectionRange(pos);
4195 int line = pdoc->LineFromPosition(sel.MainCaret());
4196 UndoGroup ug(pdoc);
4197 sel.RangeMain().caret = SelectionPosition(
4198 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
4199 int xInsert = XFromPosition(sel.RangeMain().caret);
4200 bool prevCr = false;
4201 while ((len > 0) && IsEOLChar(ptr[len-1]))
4202 len--;
4203 for (int i = 0; i < len; i++) {
4204 if (IsEOLChar(ptr[i])) {
4205 if ((ptr[i] == '\r') || (!prevCr))
4206 line++;
4207 if (line >= pdoc->LinesTotal()) {
4208 if (pdoc->eolMode != SC_EOL_LF)
4209 pdoc->InsertChar(pdoc->Length(), '\r');
4210 if (pdoc->eolMode != SC_EOL_CR)
4211 pdoc->InsertChar(pdoc->Length(), '\n');
4213 // Pad the end of lines with spaces if required
4214 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
4215 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
4216 while (XFromPosition(sel.MainCaret()) < xInsert) {
4217 pdoc->InsertChar(sel.MainCaret(), ' ');
4218 sel.RangeMain().caret.Add(1);
4221 prevCr = ptr[i] == '\r';
4222 } else {
4223 pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
4224 sel.RangeMain().caret.Add(1);
4225 prevCr = false;
4228 SetEmptySelection(pos);
4231 bool Editor::CanPaste() {
4232 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
4235 void Editor::Clear() {
4236 // If multiple selections, don't delete EOLS
4237 if (sel.Empty()) {
4238 bool singleVirtual = false;
4239 if ((sel.Count() == 1) &&
4240 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
4241 sel.RangeMain().Start().VirtualSpace()) {
4242 singleVirtual = true;
4244 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
4245 for (size_t r=0; r<sel.Count(); r++) {
4246 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
4247 if (sel.Range(r).Start().VirtualSpace()) {
4248 if (sel.Range(r).anchor < sel.Range(r).caret)
4249 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
4250 else
4251 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
4253 if ((sel.Count() == 1) || !IsEOLChar(pdoc->CharAt(sel.Range(r).caret.Position()))) {
4254 pdoc->DelChar(sel.Range(r).caret.Position());
4255 sel.Range(r).ClearVirtualSpace();
4256 } // else multiple selection so don't eat line ends
4257 } else {
4258 sel.Range(r).ClearVirtualSpace();
4261 } else {
4262 ClearSelection();
4264 sel.RemoveDuplicates();
4267 void Editor::SelectAll() {
4268 sel.Clear();
4269 SetSelection(0, pdoc->Length());
4270 Redraw();
4273 void Editor::Undo() {
4274 if (pdoc->CanUndo()) {
4275 InvalidateCaret();
4276 int newPos = pdoc->Undo();
4277 if (newPos >= 0)
4278 SetEmptySelection(newPos);
4279 EnsureCaretVisible();
4283 void Editor::Redo() {
4284 if (pdoc->CanRedo()) {
4285 int newPos = pdoc->Redo();
4286 if (newPos >= 0)
4287 SetEmptySelection(newPos);
4288 EnsureCaretVisible();
4292 void Editor::DelChar() {
4293 if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) {
4294 pdoc->DelChar(sel.MainCaret());
4296 // Avoid blinking during rapid typing:
4297 ShowCaretAtCurrentPosition();
4300 void Editor::DelCharBack(bool allowLineStartDeletion) {
4301 if (!sel.IsRectangular())
4302 FilterSelections();
4303 if (sel.IsRectangular())
4304 allowLineStartDeletion = false;
4305 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
4306 if (sel.Empty()) {
4307 for (size_t r=0; r<sel.Count(); r++) {
4308 if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
4309 if (sel.Range(r).caret.VirtualSpace()) {
4310 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
4311 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
4312 } else {
4313 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4314 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
4315 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
4316 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
4317 UndoGroup ugInner(pdoc, !ug.Needed());
4318 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4319 int indentationStep = pdoc->IndentSize();
4320 if (indentation % indentationStep == 0) {
4321 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
4322 } else {
4323 pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
4325 // SetEmptySelection
4326 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos),
4327 pdoc->GetLineIndentPosition(lineCurrentPos));
4328 } else {
4329 pdoc->DelCharBack(sel.Range(r).caret.Position());
4333 } else {
4334 sel.Range(r).ClearVirtualSpace();
4337 ThinRectangularRange();
4338 } else {
4339 ClearSelection();
4341 sel.RemoveDuplicates();
4342 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
4343 // Avoid blinking during rapid typing:
4344 ShowCaretAtCurrentPosition();
4347 void Editor::NotifyFocus(bool) {}
4349 void Editor::SetCtrlID(int identifier) {
4350 ctrlID = identifier;
4353 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
4354 SCNotification scn = {0};
4355 scn.nmhdr.code = SCN_STYLENEEDED;
4356 scn.position = endStyleNeeded;
4357 NotifyParent(scn);
4360 void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
4361 NotifyStyleToNeeded(endStyleNeeded);
4364 void Editor::NotifyLexerChanged(Document *, void *) {
4367 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
4368 errorStatus = status;
4371 void Editor::NotifyChar(int ch) {
4372 SCNotification scn = {0};
4373 scn.nmhdr.code = SCN_CHARADDED;
4374 scn.ch = ch;
4375 NotifyParent(scn);
4378 void Editor::NotifySavePoint(bool isSavePoint) {
4379 SCNotification scn = {0};
4380 if (isSavePoint) {
4381 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
4382 } else {
4383 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
4385 NotifyParent(scn);
4388 void Editor::NotifyModifyAttempt() {
4389 SCNotification scn = {0};
4390 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
4391 NotifyParent(scn);
4394 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
4395 SCNotification scn = {0};
4396 scn.nmhdr.code = SCN_DOUBLECLICK;
4397 scn.line = LineFromLocation(pt);
4398 scn.position = PositionFromLocation(pt, true);
4399 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4400 (alt ? SCI_ALT : 0);
4401 NotifyParent(scn);
4404 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
4405 SCNotification scn = {0};
4406 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
4407 scn.position = position;
4408 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4409 (alt ? SCI_ALT : 0);
4410 NotifyParent(scn);
4413 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
4414 SCNotification scn = {0};
4415 scn.nmhdr.code = SCN_HOTSPOTCLICK;
4416 scn.position = position;
4417 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4418 (alt ? SCI_ALT : 0);
4419 NotifyParent(scn);
4422 void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
4423 SCNotification scn = {0};
4424 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
4425 scn.position = position;
4426 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4427 (alt ? SCI_ALT : 0);
4428 NotifyParent(scn);
4431 void Editor::NotifyUpdateUI() {
4432 SCNotification scn = {0};
4433 scn.nmhdr.code = SCN_UPDATEUI;
4434 scn.updated = needUpdateUI;
4435 NotifyParent(scn);
4438 void Editor::NotifyPainted() {
4439 SCNotification scn = {0};
4440 scn.nmhdr.code = SCN_PAINTED;
4441 NotifyParent(scn);
4444 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
4445 int mask = pdoc->decorations.AllOnFor(position);
4446 if ((click && mask) || pdoc->decorations.clickNotified) {
4447 SCNotification scn = {0};
4448 pdoc->decorations.clickNotified = click;
4449 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
4450 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | (alt ? SCI_ALT : 0);
4451 scn.position = position;
4452 NotifyParent(scn);
4456 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
4457 int marginClicked = -1;
4458 int x = 0;
4459 for (int margin = 0; margin < ViewStyle::margins; margin++) {
4460 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
4461 marginClicked = margin;
4462 x += vs.ms[margin].width;
4464 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
4465 SCNotification scn = {0};
4466 scn.nmhdr.code = SCN_MARGINCLICK;
4467 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4468 (alt ? SCI_ALT : 0);
4469 scn.position = pdoc->LineStart(LineFromLocation(pt));
4470 scn.margin = marginClicked;
4471 NotifyParent(scn);
4472 return true;
4473 } else {
4474 return false;
4478 void Editor::NotifyNeedShown(int pos, int len) {
4479 SCNotification scn = {0};
4480 scn.nmhdr.code = SCN_NEEDSHOWN;
4481 scn.position = pos;
4482 scn.length = len;
4483 NotifyParent(scn);
4486 void Editor::NotifyDwelling(Point pt, bool state) {
4487 SCNotification scn = {0};
4488 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
4489 scn.position = PositionFromLocation(pt, true);
4490 scn.x = pt.x;
4491 scn.y = pt.y;
4492 NotifyParent(scn);
4495 void Editor::NotifyZoom() {
4496 SCNotification scn = {0};
4497 scn.nmhdr.code = SCN_ZOOM;
4498 NotifyParent(scn);
4501 // Notifications from document
4502 void Editor::NotifyModifyAttempt(Document *, void *) {
4503 //Platform::DebugPrintf("** Modify Attempt\n");
4504 NotifyModifyAttempt();
4507 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
4508 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
4509 NotifySavePoint(atSavePoint);
4512 void Editor::CheckModificationForWrap(DocModification mh) {
4513 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
4514 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4515 int lineDoc = pdoc->LineFromPosition(mh.position);
4516 int lines = Platform::Maximum(0, mh.linesAdded);
4517 if (wrapState != eWrapNone) {
4518 NeedWrapping(lineDoc, lineDoc + lines + 1);
4520 RefreshStyleData();
4521 // Fix up annotation heights
4522 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
4526 // Move a position so it is still after the same character as before the insertion.
4527 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
4528 if (position > startInsertion) {
4529 return position + length;
4531 return position;
4534 // Move a position so it is still after the same character as before the deletion if that
4535 // character is still present else after the previous surviving character.
4536 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
4537 if (position > startDeletion) {
4538 int endDeletion = startDeletion + length;
4539 if (position > endDeletion) {
4540 return position - length;
4541 } else {
4542 return startDeletion;
4544 } else {
4545 return position;
4549 void Editor::NotifyModified(Document *, DocModification mh, void *) {
4550 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
4551 if (paintState == painting) {
4552 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
4554 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
4555 if (paintState == painting) {
4556 CheckForChangeOutsidePaint(
4557 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
4558 } else {
4559 // Could check that change is before last visible line.
4560 Redraw();
4563 if (mh.modificationType & SC_MOD_LEXERSTATE) {
4564 if (paintState == painting) {
4565 CheckForChangeOutsidePaint(
4566 Range(mh.position, mh.position + mh.length));
4567 } else {
4568 Redraw();
4571 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
4572 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4573 pdoc->IncrementStyleClock();
4575 if (paintState == notPainting) {
4576 if (mh.position < pdoc->LineStart(topLine)) {
4577 // Styling performed before this view
4578 Redraw();
4579 } else {
4580 InvalidateRange(mh.position, mh.position + mh.length);
4583 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4584 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4586 } else {
4587 // Move selection and brace highlights
4588 if (mh.modificationType & SC_MOD_INSERTTEXT) {
4589 sel.MovePositions(true, mh.position, mh.length);
4590 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
4591 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
4592 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
4593 sel.MovePositions(false, mh.position, mh.length);
4594 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
4595 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
4597 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && cs.HiddenLines()) {
4598 // Some lines are hidden so may need shown.
4599 // TODO: check if the modified area is hidden.
4600 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
4601 int lineOfPos = pdoc->LineFromPosition(mh.position);
4602 bool insertingNewLine = false;
4603 for (int i=0; i < mh.length; i++) {
4604 if ((mh.text[i] == '\n') || (mh.text[i] == '\r'))
4605 insertingNewLine = true;
4607 if (insertingNewLine && (mh.position != pdoc->LineStart(lineOfPos)))
4608 NotifyNeedShown(mh.position, pdoc->LineStart(lineOfPos+1) - mh.position);
4609 else
4610 NotifyNeedShown(mh.position, 0);
4611 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
4612 NotifyNeedShown(mh.position, mh.length);
4615 if (mh.linesAdded != 0) {
4616 // Update contraction state for inserted and removed lines
4617 // lineOfPos should be calculated in context of state before modification, shouldn't it
4618 int lineOfPos = pdoc->LineFromPosition(mh.position);
4619 if (mh.linesAdded > 0) {
4620 cs.InsertLines(lineOfPos, mh.linesAdded);
4621 } else {
4622 cs.DeleteLines(lineOfPos, -mh.linesAdded);
4625 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
4626 int lineDoc = pdoc->LineFromPosition(mh.position);
4627 if (vs.annotationVisible) {
4628 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
4629 Redraw();
4632 CheckModificationForWrap(mh);
4633 if (mh.linesAdded != 0) {
4634 // Avoid scrolling of display if change before current display
4635 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
4636 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
4637 if (newTop != topLine) {
4638 SetTopLine(newTop);
4639 SetVerticalScrollPos();
4643 //Platform::DebugPrintf("** %x Doc Changed\n", this);
4644 // TODO: could invalidate from mh.startModification to end of screen
4645 //InvalidateRange(mh.position, mh.position + mh.length);
4646 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
4647 QueueStyling(pdoc->Length());
4648 Redraw();
4650 } else {
4651 //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
4652 // mh.position, mh.position + mh.length);
4653 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
4654 QueueStyling(mh.position + mh.length);
4655 InvalidateRange(mh.position, mh.position + mh.length);
4660 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
4661 SetScrollBars();
4664 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
4665 if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) {
4666 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
4667 // Fold changes can affect the drawing of following lines so redraw whole margin
4668 RedrawSelMargin(highlightDelimiter.isEnabled ? -1 : mh.line-1, true);
4669 } else {
4670 RedrawSelMargin(mh.line);
4675 // NOW pay the piper WRT "deferred" visual updates
4676 if (IsLastStep(mh)) {
4677 SetScrollBars();
4678 Redraw();
4681 // If client wants to see this modification
4682 if (mh.modificationType & modEventMask) {
4683 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
4684 // Real modification made to text of document.
4685 NotifyChange(); // Send EN_CHANGE
4688 SCNotification scn = {0};
4689 scn.nmhdr.code = SCN_MODIFIED;
4690 scn.position = mh.position;
4691 scn.modificationType = mh.modificationType;
4692 scn.text = mh.text;
4693 scn.length = mh.length;
4694 scn.linesAdded = mh.linesAdded;
4695 scn.line = mh.line;
4696 scn.foldLevelNow = mh.foldLevelNow;
4697 scn.foldLevelPrev = mh.foldLevelPrev;
4698 scn.token = mh.token;
4699 scn.annotationLinesAdded = mh.annotationLinesAdded;
4700 NotifyParent(scn);
4704 void Editor::NotifyDeleted(Document *, void *) {
4705 /* Do nothing */
4708 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
4710 // Enumerates all macroable messages
4711 switch (iMessage) {
4712 case SCI_CUT:
4713 case SCI_COPY:
4714 case SCI_PASTE:
4715 case SCI_CLEAR:
4716 case SCI_REPLACESEL:
4717 case SCI_ADDTEXT:
4718 case SCI_INSERTTEXT:
4719 case SCI_APPENDTEXT:
4720 case SCI_CLEARALL:
4721 case SCI_SELECTALL:
4722 case SCI_GOTOLINE:
4723 case SCI_GOTOPOS:
4724 case SCI_SEARCHANCHOR:
4725 case SCI_SEARCHNEXT:
4726 case SCI_SEARCHPREV:
4727 case SCI_LINEDOWN:
4728 case SCI_LINEDOWNEXTEND:
4729 case SCI_PARADOWN:
4730 case SCI_PARADOWNEXTEND:
4731 case SCI_LINEUP:
4732 case SCI_LINEUPEXTEND:
4733 case SCI_PARAUP:
4734 case SCI_PARAUPEXTEND:
4735 case SCI_CHARLEFT:
4736 case SCI_CHARLEFTEXTEND:
4737 case SCI_CHARRIGHT:
4738 case SCI_CHARRIGHTEXTEND:
4739 case SCI_WORDLEFT:
4740 case SCI_WORDLEFTEXTEND:
4741 case SCI_WORDRIGHT:
4742 case SCI_WORDRIGHTEXTEND:
4743 case SCI_WORDPARTLEFT:
4744 case SCI_WORDPARTLEFTEXTEND:
4745 case SCI_WORDPARTRIGHT:
4746 case SCI_WORDPARTRIGHTEXTEND:
4747 case SCI_WORDLEFTEND:
4748 case SCI_WORDLEFTENDEXTEND:
4749 case SCI_WORDRIGHTEND:
4750 case SCI_WORDRIGHTENDEXTEND:
4751 case SCI_HOME:
4752 case SCI_HOMEEXTEND:
4753 case SCI_LINEEND:
4754 case SCI_LINEENDEXTEND:
4755 case SCI_HOMEWRAP:
4756 case SCI_HOMEWRAPEXTEND:
4757 case SCI_LINEENDWRAP:
4758 case SCI_LINEENDWRAPEXTEND:
4759 case SCI_DOCUMENTSTART:
4760 case SCI_DOCUMENTSTARTEXTEND:
4761 case SCI_DOCUMENTEND:
4762 case SCI_DOCUMENTENDEXTEND:
4763 case SCI_STUTTEREDPAGEUP:
4764 case SCI_STUTTEREDPAGEUPEXTEND:
4765 case SCI_STUTTEREDPAGEDOWN:
4766 case SCI_STUTTEREDPAGEDOWNEXTEND:
4767 case SCI_PAGEUP:
4768 case SCI_PAGEUPEXTEND:
4769 case SCI_PAGEDOWN:
4770 case SCI_PAGEDOWNEXTEND:
4771 case SCI_EDITTOGGLEOVERTYPE:
4772 case SCI_CANCEL:
4773 case SCI_DELETEBACK:
4774 case SCI_TAB:
4775 case SCI_BACKTAB:
4776 case SCI_FORMFEED:
4777 case SCI_VCHOME:
4778 case SCI_VCHOMEEXTEND:
4779 case SCI_VCHOMEWRAP:
4780 case SCI_VCHOMEWRAPEXTEND:
4781 case SCI_VCHOMEDISPLAY:
4782 case SCI_VCHOMEDISPLAYEXTEND:
4783 case SCI_DELWORDLEFT:
4784 case SCI_DELWORDRIGHT:
4785 case SCI_DELWORDRIGHTEND:
4786 case SCI_DELLINELEFT:
4787 case SCI_DELLINERIGHT:
4788 case SCI_LINECOPY:
4789 case SCI_LINECUT:
4790 case SCI_LINEDELETE:
4791 case SCI_LINETRANSPOSE:
4792 case SCI_LINEDUPLICATE:
4793 case SCI_LOWERCASE:
4794 case SCI_UPPERCASE:
4795 case SCI_LINESCROLLDOWN:
4796 case SCI_LINESCROLLUP:
4797 case SCI_DELETEBACKNOTLINE:
4798 case SCI_HOMEDISPLAY:
4799 case SCI_HOMEDISPLAYEXTEND:
4800 case SCI_LINEENDDISPLAY:
4801 case SCI_LINEENDDISPLAYEXTEND:
4802 case SCI_SETSELECTIONMODE:
4803 case SCI_LINEDOWNRECTEXTEND:
4804 case SCI_LINEUPRECTEXTEND:
4805 case SCI_CHARLEFTRECTEXTEND:
4806 case SCI_CHARRIGHTRECTEXTEND:
4807 case SCI_HOMERECTEXTEND:
4808 case SCI_VCHOMERECTEXTEND:
4809 case SCI_LINEENDRECTEXTEND:
4810 case SCI_PAGEUPRECTEXTEND:
4811 case SCI_PAGEDOWNRECTEXTEND:
4812 case SCI_SELECTIONDUPLICATE:
4813 case SCI_COPYALLOWLINE:
4814 case SCI_VERTICALCENTRECARET:
4815 case SCI_MOVESELECTEDLINESUP:
4816 case SCI_MOVESELECTEDLINESDOWN:
4817 case SCI_SCROLLTOSTART:
4818 case SCI_SCROLLTOEND:
4819 break;
4821 // Filter out all others like display changes. Also, newlines are redundant
4822 // with char insert messages.
4823 case SCI_NEWLINE:
4824 default:
4825 // printf("Filtered out %ld of macro recording\n", iMessage);
4826 return ;
4829 // Send notification
4830 SCNotification scn = {0};
4831 scn.nmhdr.code = SCN_MACRORECORD;
4832 scn.message = iMessage;
4833 scn.wParam = wParam;
4834 scn.lParam = lParam;
4835 NotifyParent(scn);
4838 // Something has changed that the container should know about
4839 void Editor::ContainerNeedsUpdate(int flags) {
4840 needUpdateUI |= flags;
4844 * Force scroll and keep position relative to top of window.
4846 * If stuttered = true and not already at first/last row, move to first/last row of window.
4847 * If stuttered = true and already at first/last row, scroll as normal.
4849 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
4850 int topLineNew;
4851 SelectionPosition newPos;
4853 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
4854 int topStutterLine = topLine + caretYSlop;
4855 int bottomStutterLine =
4856 pdoc->LineFromPosition(PositionFromLocation(
4857 Point(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
4858 - caretYSlop - 1;
4860 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
4861 topLineNew = topLine;
4862 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
4863 false, false, UserVirtualSpace());
4865 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
4866 topLineNew = topLine;
4867 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
4868 false, false, UserVirtualSpace());
4870 } else {
4871 Point pt = LocationFromPosition(sel.MainCaret());
4873 topLineNew = Platform::Clamp(
4874 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
4875 newPos = SPositionFromLocation(
4876 Point(lastXChosen - xOffset, pt.y + direction * (vs.lineHeight * LinesToScroll())),
4877 false, false, UserVirtualSpace());
4880 if (topLineNew != topLine) {
4881 SetTopLine(topLineNew);
4882 MovePositionTo(newPos, selt);
4883 Redraw();
4884 SetVerticalScrollPos();
4885 } else {
4886 MovePositionTo(newPos, selt);
4890 void Editor::ChangeCaseOfSelection(int caseMapping) {
4891 UndoGroup ug(pdoc);
4892 for (size_t r=0; r<sel.Count(); r++) {
4893 SelectionRange current = sel.Range(r);
4894 SelectionRange currentNoVS = current;
4895 currentNoVS.ClearVirtualSpace();
4896 char *text = CopyRange(currentNoVS.Start().Position(), currentNoVS.End().Position());
4897 size_t rangeBytes = currentNoVS.Length();
4898 if (rangeBytes > 0) {
4899 std::string sText(text, rangeBytes);
4901 std::string sMapped = CaseMapString(sText, caseMapping);
4903 if (sMapped != sText) {
4904 size_t firstDifference = 0;
4905 while (sMapped[firstDifference] == sText[firstDifference])
4906 firstDifference++;
4907 size_t lastDifference = sMapped.size() - 1;
4908 while (sMapped[lastDifference] == sText[lastDifference])
4909 lastDifference--;
4910 size_t endSame = sMapped.size() - 1 - lastDifference;
4911 pdoc->DeleteChars(
4912 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
4913 static_cast<int>(rangeBytes - firstDifference - endSame));
4914 pdoc->InsertString(
4915 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
4916 sMapped.c_str() + firstDifference,
4917 static_cast<int>(lastDifference - firstDifference + 1));
4918 // Automatic movement changes selection so reset to exactly the same as it was.
4919 sel.Range(r) = current;
4922 delete []text;
4926 void Editor::LineTranspose() {
4927 int line = pdoc->LineFromPosition(sel.MainCaret());
4928 if (line > 0) {
4929 UndoGroup ug(pdoc);
4930 int startPrev = pdoc->LineStart(line - 1);
4931 int endPrev = pdoc->LineEnd(line - 1);
4932 int start = pdoc->LineStart(line);
4933 int end = pdoc->LineEnd(line);
4934 char *line1 = CopyRange(startPrev, endPrev);
4935 int len1 = endPrev - startPrev;
4936 char *line2 = CopyRange(start, end);
4937 int len2 = end - start;
4938 pdoc->DeleteChars(start, len2);
4939 pdoc->DeleteChars(startPrev, len1);
4940 pdoc->InsertString(startPrev, line2, len2);
4941 pdoc->InsertString(start - len1 + len2, line1, len1);
4942 MovePositionTo(SelectionPosition(start - len1 + len2));
4943 delete []line1;
4944 delete []line2;
4948 void Editor::Duplicate(bool forLine) {
4949 if (sel.Empty()) {
4950 forLine = true;
4952 UndoGroup ug(pdoc);
4953 const char *eol = "";
4954 int eolLen = 0;
4955 if (forLine) {
4956 eol = StringFromEOLMode(pdoc->eolMode);
4957 eolLen = istrlen(eol);
4959 for (size_t r=0; r<sel.Count(); r++) {
4960 SelectionPosition start = sel.Range(r).Start();
4961 SelectionPosition end = sel.Range(r).End();
4962 if (forLine) {
4963 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4964 start = SelectionPosition(pdoc->LineStart(line));
4965 end = SelectionPosition(pdoc->LineEnd(line));
4967 char *text = CopyRange(start.Position(), end.Position());
4968 if (forLine)
4969 pdoc->InsertString(end.Position(), eol, eolLen);
4970 pdoc->InsertString(end.Position() + eolLen, text, SelectionRange(end, start).Length());
4971 delete []text;
4973 if (sel.Count() && sel.IsRectangular()) {
4974 SelectionPosition last = sel.Last();
4975 if (forLine) {
4976 int line = pdoc->LineFromPosition(last.Position());
4977 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
4979 if (sel.Rectangular().anchor > sel.Rectangular().caret)
4980 sel.Rectangular().anchor = last;
4981 else
4982 sel.Rectangular().caret = last;
4983 SetRectangularRange();
4987 void Editor::CancelModes() {
4988 sel.SetMoveExtends(false);
4991 void Editor::NewLine() {
4992 // Remove non-main ranges
4993 InvalidateSelection(sel.RangeMain(), true);
4994 sel.SetSelection(sel.RangeMain());
4996 // Clear main range and insert line end
4997 bool needGroupUndo = !sel.Empty();
4998 if (needGroupUndo)
4999 pdoc->BeginUndoAction();
5001 if (!sel.Empty())
5002 ClearSelection();
5003 const char *eol = "\n";
5004 if (pdoc->eolMode == SC_EOL_CRLF) {
5005 eol = "\r\n";
5006 } else if (pdoc->eolMode == SC_EOL_CR) {
5007 eol = "\r";
5008 } // else SC_EOL_LF -> "\n" already set
5009 bool inserted = pdoc->InsertCString(sel.MainCaret(), eol);
5010 // Want to end undo group before NotifyChar as applications often modify text here
5011 if (needGroupUndo)
5012 pdoc->EndUndoAction();
5013 if (inserted) {
5014 SetEmptySelection(sel.MainCaret() + istrlen(eol));
5015 while (*eol) {
5016 NotifyChar(*eol);
5017 if (recordingMacro) {
5018 char txt[2];
5019 txt[0] = *eol;
5020 txt[1] = '\0';
5021 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
5023 eol++;
5026 SetLastXChosen();
5027 SetScrollBars();
5028 EnsureCaretVisible();
5029 // Avoid blinking during rapid typing:
5030 ShowCaretAtCurrentPosition();
5033 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
5034 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
5035 if (sel.IsRectangular()) {
5036 if (selt == Selection::noSel) {
5037 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
5038 } else {
5039 caretToUse = sel.Rectangular().caret;
5043 Point pt = LocationFromPosition(caretToUse);
5044 int skipLines = 0;
5046 if (vs.annotationVisible) {
5047 int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
5048 Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
5049 int subLine = (pt.y - ptStartLine.y) / vs.lineHeight;
5051 if (direction < 0 && subLine == 0) {
5052 int lineDisplay = cs.DisplayFromDoc(lineDoc);
5053 if (lineDisplay > 0) {
5054 skipLines = pdoc->AnnotationLines(cs.DocFromDisplay(lineDisplay - 1));
5056 } else if (direction > 0 && subLine >= (cs.GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
5057 skipLines = pdoc->AnnotationLines(lineDoc);
5061 int newY = pt.y + (1 + skipLines) * direction * vs.lineHeight;
5062 SelectionPosition posNew = SPositionFromLocation(
5063 Point(lastXChosen - xOffset, newY), false, false, UserVirtualSpace());
5065 if (direction < 0) {
5066 // Line wrapping may lead to a location on the same line, so
5067 // seek back if that is the case.
5068 Point ptNew = LocationFromPosition(posNew.Position());
5069 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
5070 posNew.Add(-1);
5071 posNew.SetVirtualSpace(0);
5072 ptNew = LocationFromPosition(posNew.Position());
5074 } else if (direction > 0 && posNew.Position() != pdoc->Length()) {
5075 // There is an equivalent case when moving down which skips
5076 // over a line.
5077 Point ptNew = LocationFromPosition(posNew.Position());
5078 while ((posNew.Position() > caretToUse.Position()) && (ptNew.y > newY)) {
5079 posNew.Add(-1);
5080 posNew.SetVirtualSpace(0);
5081 ptNew = LocationFromPosition(posNew.Position());
5085 MovePositionTo(MovePositionSoVisible(posNew, direction), selt);
5088 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
5089 int lineDoc, savedPos = sel.MainCaret();
5090 do {
5091 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
5092 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
5093 if (direction > 0) {
5094 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
5095 if (selt == Selection::noSel) {
5096 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
5098 break;
5101 } while (!cs.GetVisible(lineDoc));
5104 int Editor::StartEndDisplayLine(int pos, bool start) {
5105 RefreshStyleData();
5106 int line = pdoc->LineFromPosition(pos);
5107 AutoSurface surface(this);
5108 AutoLineLayout ll(llc, RetrieveLineLayout(line));
5109 int posRet = INVALID_POSITION;
5110 if (surface && ll) {
5111 unsigned int posLineStart = pdoc->LineStart(line);
5112 LayoutLine(line, surface, vs, ll, wrapWidth);
5113 int posInLine = pos - posLineStart;
5114 if (posInLine <= ll->maxLineLength) {
5115 for (int subLine = 0; subLine < ll->lines; subLine++) {
5116 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
5117 if (start) {
5118 posRet = ll->LineStart(subLine) + posLineStart;
5119 } else {
5120 if (subLine == ll->lines - 1)
5121 posRet = ll->LineStart(subLine + 1) + posLineStart;
5122 else
5123 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
5129 if (posRet == INVALID_POSITION) {
5130 return pos;
5131 } else {
5132 return posRet;
5136 int Editor::KeyCommand(unsigned int iMessage) {
5137 switch (iMessage) {
5138 case SCI_LINEDOWN:
5139 CursorUpOrDown(1);
5140 break;
5141 case SCI_LINEDOWNEXTEND:
5142 CursorUpOrDown(1, Selection::selStream);
5143 break;
5144 case SCI_LINEDOWNRECTEXTEND:
5145 CursorUpOrDown(1, Selection::selRectangle);
5146 break;
5147 case SCI_PARADOWN:
5148 ParaUpOrDown(1);
5149 break;
5150 case SCI_PARADOWNEXTEND:
5151 ParaUpOrDown(1, Selection::selStream);
5152 break;
5153 case SCI_LINESCROLLDOWN:
5154 ScrollTo(topLine + 1);
5155 MoveCaretInsideView(false);
5156 break;
5157 case SCI_LINEUP:
5158 CursorUpOrDown(-1);
5159 break;
5160 case SCI_LINEUPEXTEND:
5161 CursorUpOrDown(-1, Selection::selStream);
5162 break;
5163 case SCI_LINEUPRECTEXTEND:
5164 CursorUpOrDown(-1, Selection::selRectangle);
5165 break;
5166 case SCI_PARAUP:
5167 ParaUpOrDown(-1);
5168 break;
5169 case SCI_PARAUPEXTEND:
5170 ParaUpOrDown(-1, Selection::selStream);
5171 break;
5172 case SCI_LINESCROLLUP:
5173 ScrollTo(topLine - 1);
5174 MoveCaretInsideView(false);
5175 break;
5176 case SCI_CHARLEFT:
5177 if (SelectionEmpty() || sel.MoveExtends()) {
5178 if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5179 SelectionPosition spCaret = sel.RangeMain().caret;
5180 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5181 MovePositionTo(spCaret);
5182 } else {
5183 MovePositionTo(MovePositionSoVisible(
5184 SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
5186 } else {
5187 MovePositionTo(sel.LimitsForRectangularElseMain().start);
5189 SetLastXChosen();
5190 break;
5191 case SCI_CHARLEFTEXTEND:
5192 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5193 SelectionPosition spCaret = sel.RangeMain().caret;
5194 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5195 MovePositionTo(spCaret, Selection::selStream);
5196 } else {
5197 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
5199 SetLastXChosen();
5200 break;
5201 case SCI_CHARLEFTRECTEXTEND:
5202 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5203 SelectionPosition spCaret = sel.RangeMain().caret;
5204 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5205 MovePositionTo(spCaret, Selection::selRectangle);
5206 } else {
5207 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
5209 SetLastXChosen();
5210 break;
5211 case SCI_CHARRIGHT:
5212 if (SelectionEmpty() || sel.MoveExtends()) {
5213 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5214 SelectionPosition spCaret = sel.RangeMain().caret;
5215 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5216 MovePositionTo(spCaret);
5217 } else {
5218 MovePositionTo(MovePositionSoVisible(
5219 SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
5221 } else {
5222 MovePositionTo(sel.LimitsForRectangularElseMain().end);
5224 SetLastXChosen();
5225 break;
5226 case SCI_CHARRIGHTEXTEND:
5227 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5228 SelectionPosition spCaret = sel.RangeMain().caret;
5229 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5230 MovePositionTo(spCaret, Selection::selStream);
5231 } else {
5232 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
5234 SetLastXChosen();
5235 break;
5236 case SCI_CHARRIGHTRECTEXTEND:
5237 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5238 SelectionPosition spCaret = sel.RangeMain().caret;
5239 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5240 MovePositionTo(spCaret, Selection::selRectangle);
5241 } else {
5242 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
5244 SetLastXChosen();
5245 break;
5246 case SCI_WORDLEFT:
5247 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
5248 SetLastXChosen();
5249 break;
5250 case SCI_WORDLEFTEXTEND:
5251 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
5252 SetLastXChosen();
5253 break;
5254 case SCI_WORDRIGHT:
5255 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
5256 SetLastXChosen();
5257 break;
5258 case SCI_WORDRIGHTEXTEND:
5259 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
5260 SetLastXChosen();
5261 break;
5263 case SCI_WORDLEFTEND:
5264 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
5265 SetLastXChosen();
5266 break;
5267 case SCI_WORDLEFTENDEXTEND:
5268 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
5269 SetLastXChosen();
5270 break;
5271 case SCI_WORDRIGHTEND:
5272 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
5273 SetLastXChosen();
5274 break;
5275 case SCI_WORDRIGHTENDEXTEND:
5276 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
5277 SetLastXChosen();
5278 break;
5280 case SCI_HOME:
5281 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5282 SetLastXChosen();
5283 break;
5284 case SCI_HOMEEXTEND:
5285 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
5286 SetLastXChosen();
5287 break;
5288 case SCI_HOMERECTEXTEND:
5289 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
5290 SetLastXChosen();
5291 break;
5292 case SCI_LINEEND:
5293 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
5294 SetLastXChosen();
5295 break;
5296 case SCI_LINEENDEXTEND:
5297 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
5298 SetLastXChosen();
5299 break;
5300 case SCI_LINEENDRECTEXTEND:
5301 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
5302 SetLastXChosen();
5303 break;
5304 case SCI_HOMEWRAP: {
5305 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5306 if (sel.RangeMain().caret <= homePos)
5307 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5308 MovePositionTo(homePos);
5309 SetLastXChosen();
5311 break;
5312 case SCI_HOMEWRAPEXTEND: {
5313 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5314 if (sel.RangeMain().caret <= homePos)
5315 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5316 MovePositionTo(homePos, Selection::selStream);
5317 SetLastXChosen();
5319 break;
5320 case SCI_LINEENDWRAP: {
5321 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5322 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5323 if (endPos > realEndPos // if moved past visible EOLs
5324 || sel.RangeMain().caret >= endPos) // if at end of display line already
5325 endPos = realEndPos;
5326 MovePositionTo(endPos);
5327 SetLastXChosen();
5329 break;
5330 case SCI_LINEENDWRAPEXTEND: {
5331 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5332 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5333 if (endPos > realEndPos // if moved past visible EOLs
5334 || sel.RangeMain().caret >= endPos) // if at end of display line already
5335 endPos = realEndPos;
5336 MovePositionTo(endPos, Selection::selStream);
5337 SetLastXChosen();
5339 break;
5340 case SCI_DOCUMENTSTART:
5341 MovePositionTo(0);
5342 SetLastXChosen();
5343 break;
5344 case SCI_DOCUMENTSTARTEXTEND:
5345 MovePositionTo(0, Selection::selStream);
5346 SetLastXChosen();
5347 break;
5348 case SCI_DOCUMENTEND:
5349 MovePositionTo(pdoc->Length());
5350 SetLastXChosen();
5351 break;
5352 case SCI_DOCUMENTENDEXTEND:
5353 MovePositionTo(pdoc->Length(), Selection::selStream);
5354 SetLastXChosen();
5355 break;
5356 case SCI_STUTTEREDPAGEUP:
5357 PageMove(-1, Selection::noSel, true);
5358 break;
5359 case SCI_STUTTEREDPAGEUPEXTEND:
5360 PageMove(-1, Selection::selStream, true);
5361 break;
5362 case SCI_STUTTEREDPAGEDOWN:
5363 PageMove(1, Selection::noSel, true);
5364 break;
5365 case SCI_STUTTEREDPAGEDOWNEXTEND:
5366 PageMove(1, Selection::selStream, true);
5367 break;
5368 case SCI_PAGEUP:
5369 PageMove(-1);
5370 break;
5371 case SCI_PAGEUPEXTEND:
5372 PageMove(-1, Selection::selStream);
5373 break;
5374 case SCI_PAGEUPRECTEXTEND:
5375 PageMove(-1, Selection::selRectangle);
5376 break;
5377 case SCI_PAGEDOWN:
5378 PageMove(1);
5379 break;
5380 case SCI_PAGEDOWNEXTEND:
5381 PageMove(1, Selection::selStream);
5382 break;
5383 case SCI_PAGEDOWNRECTEXTEND:
5384 PageMove(1, Selection::selRectangle);
5385 break;
5386 case SCI_EDITTOGGLEOVERTYPE:
5387 inOverstrike = !inOverstrike;
5388 DropCaret();
5389 ShowCaretAtCurrentPosition();
5390 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
5391 NotifyUpdateUI();
5392 break;
5393 case SCI_CANCEL: // Cancel any modes - handled in subclass
5394 // Also unselect text
5395 CancelModes();
5396 break;
5397 case SCI_DELETEBACK:
5398 DelCharBack(true);
5399 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5400 SetLastXChosen();
5402 EnsureCaretVisible();
5403 break;
5404 case SCI_DELETEBACKNOTLINE:
5405 DelCharBack(false);
5406 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5407 SetLastXChosen();
5409 EnsureCaretVisible();
5410 break;
5411 case SCI_TAB:
5412 Indent(true);
5413 if (caretSticky == SC_CARETSTICKY_OFF) {
5414 SetLastXChosen();
5416 EnsureCaretVisible();
5417 ShowCaretAtCurrentPosition(); // Avoid blinking
5418 break;
5419 case SCI_BACKTAB:
5420 Indent(false);
5421 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5422 SetLastXChosen();
5424 EnsureCaretVisible();
5425 ShowCaretAtCurrentPosition(); // Avoid blinking
5426 break;
5427 case SCI_NEWLINE:
5428 NewLine();
5429 break;
5430 case SCI_FORMFEED:
5431 AddChar('\f');
5432 break;
5433 case SCI_VCHOME:
5434 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
5435 SetLastXChosen();
5436 break;
5437 case SCI_VCHOMEEXTEND:
5438 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
5439 SetLastXChosen();
5440 break;
5441 case SCI_VCHOMERECTEXTEND:
5442 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
5443 SetLastXChosen();
5444 break;
5445 case SCI_VCHOMEWRAP: {
5446 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5447 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5448 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5449 homePos = viewLineStart;
5451 MovePositionTo(homePos);
5452 SetLastXChosen();
5454 break;
5455 case SCI_VCHOMEWRAPEXTEND: {
5456 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5457 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5458 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5459 homePos = viewLineStart;
5461 MovePositionTo(homePos, Selection::selStream);
5462 SetLastXChosen();
5464 break;
5465 case SCI_ZOOMIN:
5466 if (vs.zoomLevel < 20) {
5467 vs.zoomLevel++;
5468 InvalidateStyleRedraw();
5469 NotifyZoom();
5471 break;
5472 case SCI_ZOOMOUT:
5473 if (vs.zoomLevel > -10) {
5474 vs.zoomLevel--;
5475 InvalidateStyleRedraw();
5476 NotifyZoom();
5478 break;
5479 case SCI_DELWORDLEFT: {
5480 int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
5481 pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
5482 sel.RangeMain().ClearVirtualSpace();
5483 SetLastXChosen();
5485 break;
5486 case SCI_DELWORDRIGHT: {
5487 UndoGroup ug(pdoc);
5488 sel.RangeMain().caret = SelectionPosition(
5489 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5490 sel.RangeMain().anchor = sel.RangeMain().caret;
5491 int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
5492 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5494 break;
5495 case SCI_DELWORDRIGHTEND: {
5496 UndoGroup ug(pdoc);
5497 sel.RangeMain().caret = SelectionPosition(
5498 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5499 int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
5500 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5502 break;
5503 case SCI_DELLINELEFT: {
5504 int line = pdoc->LineFromPosition(sel.MainCaret());
5505 int start = pdoc->LineStart(line);
5506 pdoc->DeleteChars(start, sel.MainCaret() - start);
5507 sel.RangeMain().ClearVirtualSpace();
5508 SetLastXChosen();
5510 break;
5511 case SCI_DELLINERIGHT: {
5512 int line = pdoc->LineFromPosition(sel.MainCaret());
5513 int end = pdoc->LineEnd(line);
5514 pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
5516 break;
5517 case SCI_LINECOPY: {
5518 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5519 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5520 CopyRangeToClipboard(pdoc->LineStart(lineStart),
5521 pdoc->LineStart(lineEnd + 1));
5523 break;
5524 case SCI_LINECUT: {
5525 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5526 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5527 int start = pdoc->LineStart(lineStart);
5528 int end = pdoc->LineStart(lineEnd + 1);
5529 SetSelection(start, end);
5530 Cut();
5531 SetLastXChosen();
5533 break;
5534 case SCI_LINEDELETE: {
5535 int line = pdoc->LineFromPosition(sel.MainCaret());
5536 int start = pdoc->LineStart(line);
5537 int end = pdoc->LineStart(line + 1);
5538 pdoc->DeleteChars(start, end - start);
5540 break;
5541 case SCI_LINETRANSPOSE:
5542 LineTranspose();
5543 break;
5544 case SCI_LINEDUPLICATE:
5545 Duplicate(true);
5546 break;
5547 case SCI_SELECTIONDUPLICATE:
5548 Duplicate(false);
5549 break;
5550 case SCI_LOWERCASE:
5551 ChangeCaseOfSelection(cmLower);
5552 break;
5553 case SCI_UPPERCASE:
5554 ChangeCaseOfSelection(cmUpper);
5555 break;
5556 case SCI_WORDPARTLEFT:
5557 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
5558 SetLastXChosen();
5559 break;
5560 case SCI_WORDPARTLEFTEXTEND:
5561 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
5562 SetLastXChosen();
5563 break;
5564 case SCI_WORDPARTRIGHT:
5565 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
5566 SetLastXChosen();
5567 break;
5568 case SCI_WORDPARTRIGHTEXTEND:
5569 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
5570 SetLastXChosen();
5571 break;
5572 case SCI_HOMEDISPLAY:
5573 MovePositionTo(MovePositionSoVisible(
5574 StartEndDisplayLine(sel.MainCaret(), true), -1));
5575 SetLastXChosen();
5576 break;
5577 case SCI_VCHOMEDISPLAY: {
5578 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5579 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5580 if (viewLineStart > homePos)
5581 homePos = viewLineStart;
5583 MovePositionTo(homePos);
5584 SetLastXChosen();
5586 break;
5587 case SCI_HOMEDISPLAYEXTEND:
5588 MovePositionTo(MovePositionSoVisible(
5589 StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
5590 SetLastXChosen();
5591 break;
5592 case SCI_VCHOMEDISPLAYEXTEND: {
5593 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5594 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5595 if (viewLineStart > homePos)
5596 homePos = viewLineStart;
5598 MovePositionTo(homePos, Selection::selStream);
5599 SetLastXChosen();
5601 break;
5602 case SCI_LINEENDDISPLAY:
5603 MovePositionTo(MovePositionSoVisible(
5604 StartEndDisplayLine(sel.MainCaret(), false), 1));
5605 SetLastXChosen();
5606 break;
5607 case SCI_LINEENDDISPLAYEXTEND:
5608 MovePositionTo(MovePositionSoVisible(
5609 StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
5610 SetLastXChosen();
5611 break;
5612 case SCI_SCROLLTOSTART:
5613 ScrollTo(0);
5614 break;
5615 case SCI_SCROLLTOEND:
5616 ScrollTo(MaxScrollPos());
5617 break;
5619 return 0;
5622 int Editor::KeyDefault(int, int) {
5623 return 0;
5626 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
5627 DwellEnd(false);
5628 int msg = kmap.Find(key, modifiers);
5629 if (msg) {
5630 if (consumed)
5631 *consumed = true;
5632 return WndProc(msg, 0, 0);
5633 } else {
5634 if (consumed)
5635 *consumed = false;
5636 return KeyDefault(key, modifiers);
5640 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
5641 int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
5642 (alt ? SCI_ALT : 0);
5643 return KeyDownWithModifiers(key, modifiers, consumed);
5646 void Editor::Indent(bool forwards) {
5647 for (size_t r=0; r<sel.Count(); r++) {
5648 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
5649 int caretPosition = sel.Range(r).caret.Position();
5650 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
5651 if (lineOfAnchor == lineCurrentPos) {
5652 if (forwards) {
5653 UndoGroup ug(pdoc);
5654 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
5655 caretPosition = sel.Range(r).caret.Position();
5656 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
5657 pdoc->tabIndents) {
5658 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5659 int indentationStep = pdoc->IndentSize();
5660 pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
5661 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5662 } else {
5663 if (pdoc->useTabs) {
5664 pdoc->InsertChar(caretPosition, '\t');
5665 sel.Range(r) = SelectionRange(caretPosition+1);
5666 } else {
5667 int numSpaces = (pdoc->tabInChars) -
5668 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
5669 if (numSpaces < 1)
5670 numSpaces = pdoc->tabInChars;
5671 for (int i = 0; i < numSpaces; i++) {
5672 pdoc->InsertChar(caretPosition + i, ' ');
5674 sel.Range(r) = SelectionRange(caretPosition+numSpaces);
5677 } else {
5678 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
5679 pdoc->tabIndents) {
5680 UndoGroup ug(pdoc);
5681 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5682 int indentationStep = pdoc->IndentSize();
5683 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
5684 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5685 } else {
5686 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
5687 pdoc->tabInChars;
5688 if (newColumn < 0)
5689 newColumn = 0;
5690 int newPos = caretPosition;
5691 while (pdoc->GetColumn(newPos) > newColumn)
5692 newPos--;
5693 sel.Range(r) = SelectionRange(newPos);
5696 } else { // Multiline
5697 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
5698 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
5699 // Multiple lines selected so indent / dedent
5700 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
5701 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
5702 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
5703 lineBottomSel--; // If not selecting any characters on a line, do not indent
5705 UndoGroup ug(pdoc);
5706 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
5708 if (lineOfAnchor < lineCurrentPos) {
5709 if (currentPosPosOnLine == 0)
5710 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5711 else
5712 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
5713 } else {
5714 if (anchorPosOnLine == 0)
5715 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5716 else
5717 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
5723 class CaseFolderASCII : public CaseFolderTable {
5724 public:
5725 CaseFolderASCII() {
5726 StandardASCII();
5728 ~CaseFolderASCII() {
5733 CaseFolder *Editor::CaseFolderForEncoding() {
5734 // Simple default that only maps ASCII upper case to lower case.
5735 return new CaseFolderASCII();
5739 * Search of a text in the document, in the given range.
5740 * @return The position of the found text, -1 if not found.
5742 long Editor::FindText(
5743 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5744 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5745 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
5747 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
5748 int lengthFound = istrlen(ft->lpstrText);
5749 if (!pdoc->HasCaseFolder())
5750 pdoc->SetCaseFolder(CaseFolderForEncoding());
5751 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
5752 (wParam & SCFIND_MATCHCASE) != 0,
5753 (wParam & SCFIND_WHOLEWORD) != 0,
5754 (wParam & SCFIND_WORDSTART) != 0,
5755 (wParam & SCFIND_REGEXP) != 0,
5756 wParam,
5757 &lengthFound);
5758 if (pos != -1) {
5759 ft->chrgText.cpMin = pos;
5760 ft->chrgText.cpMax = pos + lengthFound;
5762 return pos;
5766 * Relocatable search support : Searches relative to current selection
5767 * point and sets the selection to the found text range with
5768 * each search.
5771 * Anchor following searches at current selection start: This allows
5772 * multiple incremental interactive searches to be macro recorded
5773 * while still setting the selection to found text so the find/select
5774 * operation is self-contained.
5776 void Editor::SearchAnchor() {
5777 searchAnchor = SelectionStart().Position();
5781 * Find text from current search anchor: Must call @c SearchAnchor first.
5782 * Used for next text and previous text requests.
5783 * @return The position of the found text, -1 if not found.
5785 long Editor::SearchText(
5786 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
5787 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5788 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5789 sptr_t lParam) { ///< The text to search for.
5791 const char *txt = reinterpret_cast<char *>(lParam);
5792 int pos;
5793 int lengthFound = istrlen(txt);
5794 if (!pdoc->HasCaseFolder())
5795 pdoc->SetCaseFolder(CaseFolderForEncoding());
5796 if (iMessage == SCI_SEARCHNEXT) {
5797 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
5798 (wParam & SCFIND_MATCHCASE) != 0,
5799 (wParam & SCFIND_WHOLEWORD) != 0,
5800 (wParam & SCFIND_WORDSTART) != 0,
5801 (wParam & SCFIND_REGEXP) != 0,
5802 wParam,
5803 &lengthFound);
5804 } else {
5805 pos = pdoc->FindText(searchAnchor, 0, txt,
5806 (wParam & SCFIND_MATCHCASE) != 0,
5807 (wParam & SCFIND_WHOLEWORD) != 0,
5808 (wParam & SCFIND_WORDSTART) != 0,
5809 (wParam & SCFIND_REGEXP) != 0,
5810 wParam,
5811 &lengthFound);
5813 if (pos != -1) {
5814 SetSelection(pos, pos + lengthFound);
5817 return pos;
5820 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
5821 std::string ret(s);
5822 for (size_t i=0; i<ret.size(); i++) {
5823 switch (caseMapping) {
5824 case cmUpper:
5825 if (ret[i] >= 'a' && ret[i] <= 'z')
5826 ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
5827 break;
5828 case cmLower:
5829 if (ret[i] >= 'A' && ret[i] <= 'Z')
5830 ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
5831 break;
5834 return ret;
5838 * Search for text in the target range of the document.
5839 * @return The position of the found text, -1 if not found.
5841 long Editor::SearchInTarget(const char *text, int length) {
5842 int lengthFound = length;
5844 if (!pdoc->HasCaseFolder())
5845 pdoc->SetCaseFolder(CaseFolderForEncoding());
5846 int pos = pdoc->FindText(targetStart, targetEnd, text,
5847 (searchFlags & SCFIND_MATCHCASE) != 0,
5848 (searchFlags & SCFIND_WHOLEWORD) != 0,
5849 (searchFlags & SCFIND_WORDSTART) != 0,
5850 (searchFlags & SCFIND_REGEXP) != 0,
5851 searchFlags,
5852 &lengthFound);
5853 if (pos != -1) {
5854 targetStart = pos;
5855 targetEnd = pos + lengthFound;
5857 return pos;
5860 void Editor::GoToLine(int lineNo) {
5861 if (lineNo > pdoc->LinesTotal())
5862 lineNo = pdoc->LinesTotal();
5863 if (lineNo < 0)
5864 lineNo = 0;
5865 SetEmptySelection(pdoc->LineStart(lineNo));
5866 ShowCaretAtCurrentPosition();
5867 EnsureCaretVisible();
5870 static bool Close(Point pt1, Point pt2) {
5871 if (abs(pt1.x - pt2.x) > 3)
5872 return false;
5873 if (abs(pt1.y - pt2.y) > 3)
5874 return false;
5875 return true;
5878 char *Editor::CopyRange(int start, int end) {
5879 char *text = 0;
5880 if (start < end) {
5881 int len = end - start;
5882 text = new char[len + 1];
5883 for (int i = 0; i < len; i++) {
5884 text[i] = pdoc->CharAt(start + i);
5886 text[len] = '\0';
5888 return text;
5891 std::string Editor::RangeText(int start, int end) const {
5892 if (start < end) {
5893 int len = end - start;
5894 std::string ret(len, '\0');
5895 for (int i = 0; i < len; i++) {
5896 ret[i] = pdoc->CharAt(start + i);
5898 return ret;
5900 return std::string();
5903 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
5904 if (sel.Empty()) {
5905 if (allowLineCopy) {
5906 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
5907 int start = pdoc->LineStart(currentLine);
5908 int end = pdoc->LineEnd(currentLine);
5910 char *text = CopyRange(start, end);
5911 size_t textLen = text ? strlen(text) : 0;
5912 // include room for \r\n\0
5913 textLen += 3;
5914 char *textWithEndl = new char[textLen];
5915 textWithEndl[0] = '\0';
5916 if (text)
5917 strcat(textWithEndl, text);
5918 if (pdoc->eolMode != SC_EOL_LF)
5919 strcat(textWithEndl, "\r");
5920 if (pdoc->eolMode != SC_EOL_CR)
5921 strcat(textWithEndl, "\n");
5922 ss->Set(textWithEndl, static_cast<int>(strlen(textWithEndl) + 1),
5923 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, true);
5924 delete []text;
5926 } else {
5927 int delimiterLength = 0;
5928 if (sel.selType == Selection::selRectangle) {
5929 if (pdoc->eolMode == SC_EOL_CRLF) {
5930 delimiterLength = 2;
5931 } else {
5932 delimiterLength = 1;
5935 size_t size = sel.Length() + delimiterLength * sel.Count();
5936 char *text = new char[size + 1];
5937 int j = 0;
5938 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
5939 if (sel.selType == Selection::selRectangle)
5940 std::sort(rangesInOrder.begin(), rangesInOrder.end());
5941 for (size_t r=0; r<rangesInOrder.size(); r++) {
5942 SelectionRange current = rangesInOrder[r];
5943 for (int i = current.Start().Position();
5944 i < current.End().Position();
5945 i++) {
5946 text[j++] = pdoc->CharAt(i);
5948 if (sel.selType == Selection::selRectangle) {
5949 if (pdoc->eolMode != SC_EOL_LF) {
5950 text[j++] = '\r';
5952 if (pdoc->eolMode != SC_EOL_CR) {
5953 text[j++] = '\n';
5957 text[size] = '\0';
5958 ss->Set(text, static_cast<int>(size + 1), pdoc->dbcsCodePage,
5959 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
5963 void Editor::CopyRangeToClipboard(int start, int end) {
5964 start = pdoc->ClampPositionIntoDocument(start);
5965 end = pdoc->ClampPositionIntoDocument(end);
5966 SelectionText selectedText;
5967 selectedText.Set(CopyRange(start, end), end - start + 1,
5968 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5969 CopyToClipboard(selectedText);
5972 void Editor::CopyText(int length, const char *text) {
5973 SelectionText selectedText;
5974 selectedText.Copy(text, length + 1,
5975 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5976 CopyToClipboard(selectedText);
5979 void Editor::SetDragPosition(SelectionPosition newPos) {
5980 if (newPos.Position() >= 0) {
5981 newPos = MovePositionOutsideChar(newPos, 1);
5982 posDrop = newPos;
5984 if (!(posDrag == newPos)) {
5985 caret.on = true;
5986 SetTicking(true);
5987 InvalidateCaret();
5988 posDrag = newPos;
5989 InvalidateCaret();
5993 void Editor::DisplayCursor(Window::Cursor c) {
5994 if (cursorMode == SC_CURSORNORMAL)
5995 wMain.SetCursor(c);
5996 else
5997 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
6000 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
6001 int xMove = ptStart.x - ptNow.x;
6002 int yMove = ptStart.y - ptNow.y;
6003 int distanceSquared = xMove * xMove + yMove * yMove;
6004 return distanceSquared > 16;
6007 void Editor::StartDrag() {
6008 // Always handled by subclasses
6009 //SetMouseCapture(true);
6010 //DisplayCursor(Window::cursorArrow);
6013 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
6014 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
6015 if (inDragDrop == ddDragging)
6016 dropWentOutside = false;
6018 bool positionWasInSelection = PositionInSelection(position.Position());
6020 bool positionOnEdgeOfSelection =
6021 (position == SelectionStart()) || (position == SelectionEnd());
6023 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
6024 (positionOnEdgeOfSelection && !moving)) {
6026 SelectionPosition selStart = SelectionStart();
6027 SelectionPosition selEnd = SelectionEnd();
6029 UndoGroup ug(pdoc);
6031 SelectionPosition positionAfterDeletion = position;
6032 if ((inDragDrop == ddDragging) && moving) {
6033 // Remove dragged out text
6034 if (rectangular || sel.selType == Selection::selLines) {
6035 for (size_t r=0; r<sel.Count(); r++) {
6036 if (position >= sel.Range(r).Start()) {
6037 if (position > sel.Range(r).End()) {
6038 positionAfterDeletion.Add(-sel.Range(r).Length());
6039 } else {
6040 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
6044 } else {
6045 if (position > selStart) {
6046 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
6049 ClearSelection();
6051 position = positionAfterDeletion;
6053 if (rectangular) {
6054 PasteRectangular(position, value, istrlen(value));
6055 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
6056 SetEmptySelection(position);
6057 } else {
6058 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
6059 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
6060 if (pdoc->InsertCString(position.Position(), value)) {
6061 SelectionPosition posAfterInsertion = position;
6062 posAfterInsertion.Add(istrlen(value));
6063 SetSelection(posAfterInsertion, position);
6066 } else if (inDragDrop == ddDragging) {
6067 SetEmptySelection(position);
6072 * @return true if given position is inside the selection,
6074 bool Editor::PositionInSelection(int pos) {
6075 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
6076 for (size_t r=0; r<sel.Count(); r++) {
6077 if (sel.Range(r).Contains(pos))
6078 return true;
6080 return false;
6083 bool Editor::PointInSelection(Point pt) {
6084 SelectionPosition pos = SPositionFromLocation(pt, false, true);
6085 Point ptPos = LocationFromPosition(pos);
6086 for (size_t r=0; r<sel.Count(); r++) {
6087 SelectionRange range = sel.Range(r);
6088 if (range.Contains(pos)) {
6089 bool hit = true;
6090 if (pos == range.Start()) {
6091 // see if just before selection
6092 if (pt.x < ptPos.x) {
6093 hit = false;
6096 if (pos == range.End()) {
6097 // see if just after selection
6098 if (pt.x > ptPos.x) {
6099 hit = false;
6102 if (hit)
6103 return true;
6106 return false;
6109 bool Editor::PointInSelMargin(Point pt) {
6110 // Really means: "Point in a margin"
6111 if (vs.fixedColumnWidth > 0) { // There is a margin
6112 PRectangle rcSelMargin = GetClientRectangle();
6113 rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth;
6114 return rcSelMargin.Contains(pt);
6115 } else {
6116 return false;
6120 Window::Cursor Editor::GetMarginCursor(Point pt) {
6121 int x = 0;
6122 for (int margin = 0; margin < ViewStyle::margins; margin++) {
6123 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
6124 return static_cast<Window::Cursor>(vs.ms[margin].cursor);
6125 x += vs.ms[margin].width;
6127 return Window::cursorReverseArrow;
6130 void Editor::TrimAndSetSelection(int currentPos_, int anchor_) {
6131 sel.TrimSelection(SelectionRange(currentPos_, anchor_));
6132 SetSelection(currentPos_, anchor_);
6135 void Editor::LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine) {
6136 int selCurrentPos, selAnchorPos;
6137 if (wholeLine) {
6138 int lineCurrent_ = pdoc->LineFromPosition(lineCurrentPos_);
6139 int lineAnchor_ = pdoc->LineFromPosition(lineAnchorPos_);
6140 if (lineAnchorPos_ < lineCurrentPos_) {
6141 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
6142 selAnchorPos = pdoc->LineStart(lineAnchor_);
6143 } else if (lineAnchorPos_ > lineCurrentPos_) {
6144 selCurrentPos = pdoc->LineStart(lineCurrent_);
6145 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
6146 } else { // Same line, select it
6147 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
6148 selAnchorPos = pdoc->LineStart(lineAnchor_);
6150 } else {
6151 if (lineAnchorPos_ < lineCurrentPos_) {
6152 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
6153 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6154 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6155 } else if (lineAnchorPos_ > lineCurrentPos_) {
6156 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
6157 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6158 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
6159 } else { // Same line, select it
6160 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6161 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6162 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6165 TrimAndSetSelection(selCurrentPos, selAnchorPos);
6168 void Editor::WordSelection(int pos) {
6169 if (pos < wordSelectAnchorStartPos) {
6170 // Extend backward to the word containing pos.
6171 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
6172 // This ensures that a series of empty lines isn't counted as a single "word".
6173 if (!pdoc->IsLineEndPosition(pos))
6174 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
6175 TrimAndSetSelection(pos, wordSelectAnchorEndPos);
6176 } else if (pos > wordSelectAnchorEndPos) {
6177 // Extend forward to the word containing the character to the left of pos.
6178 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
6179 // This ensures that a series of empty lines isn't counted as a single "word".
6180 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
6181 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
6182 TrimAndSetSelection(pos, wordSelectAnchorStartPos);
6183 } else {
6184 // Select only the anchored word
6185 if (pos >= originalAnchorPos)
6186 TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
6187 else
6188 TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
6192 void Editor::DwellEnd(bool mouseMoved) {
6193 if (mouseMoved)
6194 ticksToDwell = dwellDelay;
6195 else
6196 ticksToDwell = SC_TIME_FOREVER;
6197 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
6198 dwelling = false;
6199 NotifyDwelling(ptMouseLast, dwelling);
6203 void Editor::MouseLeave() {
6204 SetHotSpotRange(NULL);
6205 if (!HaveMouseCapture()) {
6206 ptMouseLast = Point(-1,-1);
6207 DwellEnd(true);
6211 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
6212 return (!rectangular && ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0))
6213 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
6216 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
6217 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
6218 ptMouseLast = pt;
6219 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
6220 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6221 inDragDrop = ddNone;
6222 sel.SetMoveExtends(false);
6224 if (NotifyMarginClick(pt, shift, ctrl, alt))
6225 return;
6227 NotifyIndicatorClick(true, newPos.Position(), shift, ctrl, alt);
6229 bool inSelMargin = PointInSelMargin(pt);
6230 // In margin ctrl+(double)click should always select everything
6231 if (ctrl && inSelMargin) {
6232 SelectAll();
6233 lastClickTime = curTime;
6234 lastClick = pt;
6235 return;
6237 if (shift && !inSelMargin) {
6238 SetSelection(newPos);
6240 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
6241 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
6242 SetMouseCapture(true);
6243 if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))
6244 SetEmptySelection(newPos.Position());
6245 bool doubleClick = false;
6246 // Stop mouse button bounce changing selection type
6247 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
6248 if (inSelMargin) {
6249 // Inside margin selection type should be either selSubLine or selWholeLine.
6250 if (selectionType == selSubLine) {
6251 // If it is selSubLine, we're inside a *double* click and word wrap is enabled,
6252 // so we switch to selWholeLine in order to select whole line.
6253 selectionType = selWholeLine;
6254 } else if (selectionType != selSubLine && selectionType != selWholeLine) {
6255 // If it is neither, reset selection type to line selection.
6256 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6258 } else {
6259 if (selectionType == selChar) {
6260 selectionType = selWord;
6261 doubleClick = true;
6262 } else if (selectionType == selWord) {
6263 // Since we ended up here, we're inside a *triple* click, which should always select
6264 // whole line irregardless of word wrap being enabled or not.
6265 selectionType = selWholeLine;
6266 } else {
6267 selectionType = selChar;
6268 originalAnchorPos = sel.MainCaret();
6273 if (selectionType == selWord) {
6274 int charPos = originalAnchorPos;
6275 if (sel.MainCaret() == originalAnchorPos) {
6276 charPos = PositionFromLocation(pt, false, true);
6277 charPos = MovePositionOutsideChar(charPos, -1);
6280 int startWord, endWord;
6281 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
6282 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
6283 endWord = pdoc->ExtendWordSelect(charPos, 1);
6284 } else {
6285 // Selecting backwards, or anchor beyond last character on line. In these cases,
6286 // we select the word containing the character to the *left* of the anchor.
6287 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
6288 startWord = pdoc->ExtendWordSelect(charPos, -1);
6289 endWord = pdoc->ExtendWordSelect(startWord, 1);
6290 } else {
6291 // Anchor at start of line; select nothing to begin with.
6292 startWord = charPos;
6293 endWord = charPos;
6297 wordSelectAnchorStartPos = startWord;
6298 wordSelectAnchorEndPos = endWord;
6299 wordSelectInitialCaretPos = sel.MainCaret();
6300 WordSelection(wordSelectInitialCaretPos);
6301 } else if (selectionType == selSubLine || selectionType == selWholeLine) {
6302 lineAnchorPos = newPos.Position();
6303 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6304 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
6305 } else {
6306 SetEmptySelection(sel.MainCaret());
6308 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
6309 if (doubleClick) {
6310 NotifyDoubleClick(pt, shift, ctrl, alt);
6311 if (PositionIsHotspot(newPos.Position()))
6312 NotifyHotSpotDoubleClicked(newPos.Position(), shift, ctrl, alt);
6314 } else { // Single click
6315 if (inSelMargin) {
6316 sel.selType = Selection::selStream;
6317 if (!shift) {
6318 // Single click in margin: select whole line or only subline if word wrap is enabled
6319 lineAnchorPos = newPos.Position();
6320 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6321 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6322 } else {
6323 // Single shift+click in margin: select from line anchor to clicked line
6324 if (sel.MainAnchor() > sel.MainCaret())
6325 lineAnchorPos = sel.MainAnchor() - 1;
6326 else
6327 lineAnchorPos = sel.MainAnchor();
6328 // Reset selection type if there is an empty selection.
6329 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
6330 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
6331 // This ensures that we continue selecting in the same selection mode.
6332 if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
6333 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6334 LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
6337 SetDragPosition(SelectionPosition(invalidPosition));
6338 SetMouseCapture(true);
6339 } else {
6340 if (PointIsHotspot(pt)) {
6341 NotifyHotSpotClicked(newPos.Position(), shift, ctrl, alt);
6342 hotSpotClickPos = PositionFromLocation(pt,true,false);
6344 if (!shift) {
6345 if (PointInSelection(pt) && !SelectionEmpty())
6346 inDragDrop = ddInitial;
6347 else
6348 inDragDrop = ddNone;
6350 SetMouseCapture(true);
6351 if (inDragDrop != ddInitial) {
6352 SetDragPosition(SelectionPosition(invalidPosition));
6353 if (!shift) {
6354 if (ctrl && multipleSelection) {
6355 SelectionRange range(newPos);
6356 sel.TentativeSelection(range);
6357 InvalidateSelection(range, true);
6358 } else {
6359 InvalidateSelection(SelectionRange(newPos), true);
6360 if (sel.Count() > 1)
6361 Redraw();
6362 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
6363 sel.Clear();
6364 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6365 SetSelection(newPos, newPos);
6368 SelectionPosition anchorCurrent = newPos;
6369 if (shift)
6370 anchorCurrent = sel.IsRectangular() ?
6371 sel.Rectangular().anchor : sel.RangeMain().anchor;
6372 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6373 selectionType = selChar;
6374 originalAnchorPos = sel.MainCaret();
6375 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
6376 SetRectangularRange();
6380 lastClickTime = curTime;
6381 lastClick = pt;
6382 lastXChosen = pt.x + xOffset;
6383 ShowCaretAtCurrentPosition();
6386 bool Editor::PositionIsHotspot(int position) {
6387 return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
6390 bool Editor::PointIsHotspot(Point pt) {
6391 int pos = PositionFromLocation(pt, true);
6392 if (pos == INVALID_POSITION)
6393 return false;
6394 return PositionIsHotspot(pos);
6397 void Editor::SetHotSpotRange(Point *pt) {
6398 if (pt) {
6399 int pos = PositionFromLocation(*pt);
6401 // If we don't limit this to word characters then the
6402 // range can encompass more than the run range and then
6403 // the underline will not be drawn properly.
6404 int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
6405 int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
6407 // Only invalidate the range if the hotspot range has changed...
6408 if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
6409 if (hsStart != -1) {
6410 InvalidateRange(hsStart, hsEnd);
6412 hsStart = hsStart_;
6413 hsEnd = hsEnd_;
6414 InvalidateRange(hsStart, hsEnd);
6416 } else {
6417 if (hsStart != -1) {
6418 int hsStart_ = hsStart;
6419 int hsEnd_ = hsEnd;
6420 hsStart = -1;
6421 hsEnd = -1;
6422 InvalidateRange(hsStart_, hsEnd_);
6423 } else {
6424 hsStart = -1;
6425 hsEnd = -1;
6430 void Editor::GetHotSpotRange(int &hsStart_, int &hsEnd_) {
6431 hsStart_ = hsStart;
6432 hsEnd_ = hsEnd;
6435 void Editor::ButtonMove(Point pt) {
6436 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
6437 DwellEnd(true);
6440 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
6441 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6442 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
6444 if (inDragDrop == ddInitial) {
6445 if (DragThreshold(ptMouseLast, pt)) {
6446 SetMouseCapture(false);
6447 SetDragPosition(movePos);
6448 CopySelectionRange(&drag);
6449 StartDrag();
6451 return;
6454 ptMouseLast = pt;
6455 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
6456 if (HaveMouseCapture()) {
6458 // Slow down autoscrolling/selection
6459 autoScrollTimer.ticksToWait -= timer.tickSize;
6460 if (autoScrollTimer.ticksToWait > 0)
6461 return;
6462 autoScrollTimer.ticksToWait = autoScrollDelay;
6464 // Adjust selection
6465 if (posDrag.IsValid()) {
6466 SetDragPosition(movePos);
6467 } else {
6468 if (selectionType == selChar) {
6469 if (sel.IsRectangular()) {
6470 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
6471 SetSelection(movePos, sel.RangeMain().anchor);
6472 } else if (sel.Count() > 1) {
6473 SelectionRange range(movePos, sel.RangeMain().anchor);
6474 sel.TentativeSelection(range);
6475 InvalidateSelection(range, true);
6476 } else {
6477 SetSelection(movePos, sel.RangeMain().anchor);
6479 } else if (selectionType == selWord) {
6480 // Continue selecting by word
6481 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
6482 // No need to do anything. Previously this case was lumped
6483 // in with "Moved forward", but that can be harmful in this
6484 // case: a handler for the NotifyDoubleClick re-adjusts
6485 // the selection for a fancier definition of "word" (for
6486 // example, in Perl it is useful to include the leading
6487 // '$', '%' or '@' on variables for word selection). In this
6488 // the ButtonMove() called via Tick() for auto-scrolling
6489 // could result in the fancier word selection adjustment
6490 // being unmade.
6491 } else {
6492 wordSelectInitialCaretPos = -1;
6493 WordSelection(movePos.Position());
6495 } else {
6496 // Continue selecting by line
6497 LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
6501 // Autoscroll
6502 PRectangle rcClient = GetClientRectangle();
6503 int lineMove = DisplayFromPosition(movePos.Position());
6504 if (pt.y > rcClient.bottom) {
6505 ScrollTo(lineMove - LinesOnScreen() + 1);
6506 Redraw();
6507 } else if (pt.y < rcClient.top) {
6508 ScrollTo(lineMove);
6509 Redraw();
6511 EnsureCaretVisible(false, false, true);
6513 if (hsStart != -1 && !PositionIsHotspot(movePos.Position()))
6514 SetHotSpotRange(NULL);
6516 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,false) != hotSpotClickPos) {
6517 if (inDragDrop == ddNone) {
6518 DisplayCursor(Window::cursorText);
6520 hotSpotClickPos = INVALID_POSITION;
6523 } else {
6524 if (vs.fixedColumnWidth > 0) { // There is a margin
6525 if (PointInSelMargin(pt)) {
6526 DisplayCursor(GetMarginCursor(pt));
6527 SetHotSpotRange(NULL);
6528 return; // No need to test for selection
6531 // Display regular (drag) cursor over selection
6532 if (PointInSelection(pt) && !SelectionEmpty()) {
6533 DisplayCursor(Window::cursorArrow);
6534 } else if (PointIsHotspot(pt)) {
6535 DisplayCursor(Window::cursorHand);
6536 SetHotSpotRange(&pt);
6537 } else {
6538 DisplayCursor(Window::cursorText);
6539 SetHotSpotRange(NULL);
6544 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
6545 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
6546 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
6547 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6548 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6549 if (inDragDrop == ddInitial) {
6550 inDragDrop = ddNone;
6551 SetEmptySelection(newPos);
6552 selectionType = selChar;
6553 originalAnchorPos = sel.MainCaret();
6555 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
6556 hotSpotClickPos = INVALID_POSITION;
6557 NotifyHotSpotReleaseClick(newPos.Position(), false, ctrl, false);
6559 if (HaveMouseCapture()) {
6560 if (PointInSelMargin(pt)) {
6561 DisplayCursor(GetMarginCursor(pt));
6562 } else {
6563 DisplayCursor(Window::cursorText);
6564 SetHotSpotRange(NULL);
6566 ptMouseLast = pt;
6567 SetMouseCapture(false);
6568 NotifyIndicatorClick(false, newPos.Position(), false, false, false);
6569 if (inDragDrop == ddDragging) {
6570 SelectionPosition selStart = SelectionStart();
6571 SelectionPosition selEnd = SelectionEnd();
6572 if (selStart < selEnd) {
6573 if (drag.len) {
6574 if (ctrl) {
6575 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6576 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6578 } else if (newPos < selStart) {
6579 pdoc->DeleteChars(selStart.Position(), drag.len);
6580 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6581 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6583 } else if (newPos > selEnd) {
6584 pdoc->DeleteChars(selStart.Position(), drag.len);
6585 newPos.Add(-drag.len);
6586 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6587 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6589 } else {
6590 SetEmptySelection(newPos.Position());
6592 drag.Free();
6594 selectionType = selChar;
6596 } else {
6597 if (selectionType == selChar) {
6598 if (sel.Count() > 1) {
6599 sel.RangeMain() =
6600 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
6601 InvalidateSelection(sel.RangeMain(), true);
6602 } else {
6603 SetSelection(newPos, sel.RangeMain().anchor);
6606 sel.CommitTentative();
6608 SetRectangularRange();
6609 lastClickTime = curTime;
6610 lastClick = pt;
6611 lastXChosen = pt.x + xOffset;
6612 if (sel.selType == Selection::selStream) {
6613 SetLastXChosen();
6615 inDragDrop = ddNone;
6616 EnsureCaretVisible(false);
6620 // Called frequently to perform background UI including
6621 // caret blinking and automatic scrolling.
6622 void Editor::Tick() {
6623 if (HaveMouseCapture()) {
6624 // Auto scroll
6625 ButtonMove(ptMouseLast);
6627 if (caret.period > 0) {
6628 timer.ticksToWait -= timer.tickSize;
6629 if (timer.ticksToWait <= 0) {
6630 caret.on = !caret.on;
6631 timer.ticksToWait = caret.period;
6632 if (caret.active) {
6633 InvalidateCaret();
6637 if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) {
6638 scrollWidth = lineWidthMaxSeen;
6639 SetScrollBars();
6641 if ((dwellDelay < SC_TIME_FOREVER) &&
6642 (ticksToDwell > 0) &&
6643 (!HaveMouseCapture()) &&
6644 (ptMouseLast.y >= 0)) {
6645 ticksToDwell -= timer.tickSize;
6646 if (ticksToDwell <= 0) {
6647 dwelling = true;
6648 NotifyDwelling(ptMouseLast, dwelling);
6653 bool Editor::Idle() {
6655 bool idleDone;
6657 bool wrappingDone = wrapState == eWrapNone;
6659 if (!wrappingDone) {
6660 // Wrap lines during idle.
6661 WrapLines(false, -1);
6662 // No more wrapping
6663 if (wrapStart == wrapEnd)
6664 wrappingDone = true;
6667 // Add more idle things to do here, but make sure idleDone is
6668 // set correctly before the function returns. returning
6669 // false will stop calling this idle funtion until SetIdle() is
6670 // called again.
6672 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
6674 return !idleDone;
6677 void Editor::SetFocusState(bool focusState) {
6678 hasFocus = focusState;
6679 NotifyFocus(hasFocus);
6680 if (hasFocus) {
6681 ShowCaretAtCurrentPosition();
6682 } else {
6683 CancelModes();
6684 DropCaret();
6688 int Editor::PositionAfterArea(PRectangle rcArea) {
6689 // The start of the document line after the display line after the area
6690 // This often means that the line after a modification is restyled which helps
6691 // detect multiline comment additions and heals single line comments
6692 int lineAfter = topLine + (rcArea.bottom - 1) / vs.lineHeight + 1;
6693 if (lineAfter < cs.LinesDisplayed())
6694 return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
6695 else
6696 return pdoc->Length();
6699 // Style to a position within the view. If this causes a change at end of last line then
6700 // affects later lines so style all the viewed text.
6701 void Editor::StyleToPositionInView(Position pos) {
6702 int endWindow = PositionAfterArea(GetClientRectangle());
6703 if (pos > endWindow)
6704 pos = endWindow;
6705 int styleAtEnd = pdoc->StyleAt(pos-1);
6706 pdoc->EnsureStyledTo(pos);
6707 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleAt(pos-1))) {
6708 // Style at end of line changed so is multi-line change like starting a comment
6709 // so require rest of window to be styled.
6710 pdoc->EnsureStyledTo(endWindow);
6714 void Editor::IdleStyling() {
6715 // Style the line after the modification as this allows modifications that change just the
6716 // line of the modification to heal instead of propagating to the rest of the window.
6717 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(styleNeeded.upTo) + 2));
6719 if (needUpdateUI) {
6720 NotifyUpdateUI();
6721 needUpdateUI = 0;
6723 styleNeeded.Reset();
6726 void Editor::QueueStyling(int upTo) {
6727 styleNeeded.NeedUpTo(upTo);
6730 bool Editor::PaintContains(PRectangle rc) {
6731 if (rc.Empty()) {
6732 return true;
6733 } else {
6734 return rcPaint.Contains(rc);
6738 bool Editor::PaintContainsMargin() {
6739 PRectangle rcSelMargin = GetClientRectangle();
6740 rcSelMargin.right = vs.fixedColumnWidth;
6741 return PaintContains(rcSelMargin);
6744 void Editor::CheckForChangeOutsidePaint(Range r) {
6745 if (paintState == painting && !paintingAllText) {
6746 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
6747 if (!r.Valid())
6748 return;
6750 PRectangle rcRange = RectangleFromRange(r.start, r.end);
6751 PRectangle rcText = GetTextRectangle();
6752 if (rcRange.top < rcText.top) {
6753 rcRange.top = rcText.top;
6755 if (rcRange.bottom > rcText.bottom) {
6756 rcRange.bottom = rcText.bottom;
6759 if (!PaintContains(rcRange)) {
6760 AbandonPaint();
6765 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
6766 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
6767 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
6768 CheckForChangeOutsidePaint(Range(braces[0]));
6769 CheckForChangeOutsidePaint(Range(pos0));
6770 braces[0] = pos0;
6772 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
6773 CheckForChangeOutsidePaint(Range(braces[1]));
6774 CheckForChangeOutsidePaint(Range(pos1));
6775 braces[1] = pos1;
6777 bracesMatchStyle = matchStyle;
6778 if (paintState == notPainting) {
6779 Redraw();
6784 void Editor::SetAnnotationHeights(int start, int end) {
6785 if (vs.annotationVisible) {
6786 bool changedHeight = false;
6787 for (int line=start; line<end && line<pdoc->LinesTotal(); line++) {
6788 int linesWrapped = 1;
6789 if (wrapState != eWrapNone) {
6790 AutoSurface surface(this);
6791 AutoLineLayout ll(llc, RetrieveLineLayout(line));
6792 if (surface && ll) {
6793 LayoutLine(line, surface, vs, ll, wrapWidth);
6794 linesWrapped = ll->lines;
6797 if (cs.SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
6798 changedHeight = true;
6800 if (changedHeight) {
6801 Redraw();
6806 void Editor::SetDocPointer(Document *document) {
6807 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
6808 pdoc->RemoveWatcher(this, 0);
6809 pdoc->Release();
6810 if (document == NULL) {
6811 pdoc = new Document();
6812 } else {
6813 pdoc = document;
6815 pdoc->AddRef();
6817 // Ensure all positions within document
6818 sel.Clear();
6819 targetStart = 0;
6820 targetEnd = 0;
6822 braces[0] = invalidPosition;
6823 braces[1] = invalidPosition;
6825 // Reset the contraction state to fully shown.
6826 cs.Clear();
6827 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6828 SetAnnotationHeights(0, pdoc->LinesTotal());
6829 llc.Deallocate();
6830 NeedWrapping();
6832 pdoc->AddWatcher(this, 0);
6833 SetScrollBars();
6834 Redraw();
6837 void Editor::SetAnnotationVisible(int visible) {
6838 if (vs.annotationVisible != visible) {
6839 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
6840 vs.annotationVisible = visible;
6841 if (changedFromOrToHidden) {
6842 int dir = vs.annotationVisible ? 1 : -1;
6843 for (int line=0; line<pdoc->LinesTotal(); line++) {
6844 int annotationLines = pdoc->AnnotationLines(line);
6845 if (annotationLines > 0) {
6846 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
6850 Redraw();
6855 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
6857 void Editor::Expand(int &line, bool doExpand) {
6858 int lineMaxSubord = pdoc->GetLastChild(line);
6859 line++;
6860 while (line <= lineMaxSubord) {
6861 if (doExpand)
6862 cs.SetVisible(line, line, true);
6863 int level = pdoc->GetLevel(line);
6864 if (level & SC_FOLDLEVELHEADERFLAG) {
6865 if (doExpand && cs.GetExpanded(line)) {
6866 Expand(line, true);
6867 } else {
6868 Expand(line, false);
6870 } else {
6871 line++;
6876 void Editor::ToggleContraction(int line) {
6877 if (line >= 0) {
6878 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
6879 line = pdoc->GetFoldParent(line);
6880 if (line < 0)
6881 return;
6884 if (cs.GetExpanded(line)) {
6885 int lineMaxSubord = pdoc->GetLastChild(line);
6886 if (lineMaxSubord > line) {
6887 cs.SetExpanded(line, 0);
6888 cs.SetVisible(line + 1, lineMaxSubord, false);
6890 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
6891 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
6892 // This does not re-expand the fold
6893 EnsureCaretVisible();
6896 SetScrollBars();
6897 Redraw();
6900 } else {
6901 if (!(cs.GetVisible(line))) {
6902 EnsureLineVisible(line, false);
6903 GoToLine(line);
6905 cs.SetExpanded(line, 1);
6906 Expand(line, true);
6907 SetScrollBars();
6908 Redraw();
6913 int Editor::ContractedFoldNext(int lineStart) {
6914 for (int line = lineStart; line<pdoc->LinesTotal();) {
6915 if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
6916 return line;
6917 line = cs.ContractedNext(line+1);
6918 if (line < 0)
6919 return -1;
6922 return -1;
6926 * Recurse up from this line to find any folds that prevent this line from being visible
6927 * and unfold them all.
6929 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
6931 // In case in need of wrapping to ensure DisplayFromDoc works.
6932 if (lineDoc >= wrapStart)
6933 WrapLines(true, -1);
6935 if (!cs.GetVisible(lineDoc)) {
6936 int lookLine = lineDoc;
6937 int lookLineLevel = pdoc->GetLevel(lookLine);
6938 while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
6939 lookLineLevel = pdoc->GetLevel(--lookLine);
6941 int lineParent = pdoc->GetFoldParent(lookLine);
6942 if (lineParent >= 0) {
6943 if (lineDoc != lineParent)
6944 EnsureLineVisible(lineParent, enforcePolicy);
6945 if (!cs.GetExpanded(lineParent)) {
6946 cs.SetExpanded(lineParent, 1);
6947 Expand(lineParent, true);
6950 SetScrollBars();
6951 Redraw();
6953 if (enforcePolicy) {
6954 int lineDisplay = cs.DisplayFromDoc(lineDoc);
6955 if (visiblePolicy & VISIBLE_SLOP) {
6956 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
6957 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
6958 SetVerticalScrollPos();
6959 Redraw();
6960 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
6961 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
6962 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
6963 SetVerticalScrollPos();
6964 Redraw();
6966 } else {
6967 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
6968 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
6969 SetVerticalScrollPos();
6970 Redraw();
6976 int Editor::GetTag(char *tagValue, int tagNumber) {
6977 const char *text = 0;
6978 int length = 0;
6979 if ((tagNumber >= 1) && (tagNumber <= 9)) {
6980 char name[3] = "\\?";
6981 name[1] = static_cast<char>(tagNumber + '0');
6982 length = 2;
6983 text = pdoc->SubstituteByPosition(name, &length);
6985 if (tagValue) {
6986 if (text)
6987 memcpy(tagValue, text, length + 1);
6988 else
6989 *tagValue = '\0';
6991 return length;
6994 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
6995 UndoGroup ug(pdoc);
6996 if (length == -1)
6997 length = istrlen(text);
6998 if (replacePatterns) {
6999 text = pdoc->SubstituteByPosition(text, &length);
7000 if (!text) {
7001 return 0;
7004 if (targetStart != targetEnd)
7005 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
7006 targetEnd = targetStart;
7007 pdoc->InsertString(targetStart, text, length);
7008 targetEnd = targetStart + length;
7009 return length;
7012 bool Editor::IsUnicodeMode() const {
7013 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
7016 int Editor::CodePage() const {
7017 if (pdoc)
7018 return pdoc->dbcsCodePage;
7019 else
7020 return 0;
7023 int Editor::WrapCount(int line) {
7024 AutoSurface surface(this);
7025 AutoLineLayout ll(llc, RetrieveLineLayout(line));
7027 if (surface && ll) {
7028 LayoutLine(line, surface, vs, ll, wrapWidth);
7029 return ll->lines;
7030 } else {
7031 return 1;
7035 void Editor::AddStyledText(char *buffer, int appendLength) {
7036 // The buffer consists of alternating character bytes and style bytes
7037 int textLength = appendLength / 2;
7038 char *text = new char[textLength];
7039 int i;
7040 for (i = 0; i < textLength; i++) {
7041 text[i] = buffer[i*2];
7043 pdoc->InsertString(CurrentPosition(), text, textLength);
7044 for (i = 0; i < textLength; i++) {
7045 text[i] = buffer[i*2+1];
7047 pdoc->StartStyling(CurrentPosition(), static_cast<char>(0xff));
7048 pdoc->SetStyles(textLength, text);
7049 delete []text;
7050 SetEmptySelection(sel.MainCaret() + textLength);
7053 static bool ValidMargin(unsigned long wParam) {
7054 return wParam < ViewStyle::margins;
7057 static char *CharPtrFromSPtr(sptr_t lParam) {
7058 return reinterpret_cast<char *>(lParam);
7061 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7062 vs.EnsureStyle(wParam);
7063 switch (iMessage) {
7064 case SCI_STYLESETFORE:
7065 vs.styles[wParam].fore = ColourDesired(lParam);
7066 break;
7067 case SCI_STYLESETBACK:
7068 vs.styles[wParam].back = ColourDesired(lParam);
7069 break;
7070 case SCI_STYLESETBOLD:
7071 vs.styles[wParam].weight = lParam != 0 ? SC_WEIGHT_BOLD : SC_WEIGHT_NORMAL;
7072 break;
7073 case SCI_STYLESETWEIGHT:
7074 vs.styles[wParam].weight = lParam;
7075 break;
7076 case SCI_STYLESETITALIC:
7077 vs.styles[wParam].italic = lParam != 0;
7078 break;
7079 case SCI_STYLESETEOLFILLED:
7080 vs.styles[wParam].eolFilled = lParam != 0;
7081 break;
7082 case SCI_STYLESETSIZE:
7083 vs.styles[wParam].size = lParam * SC_FONT_SIZE_MULTIPLIER;
7084 break;
7085 case SCI_STYLESETSIZEFRACTIONAL:
7086 vs.styles[wParam].size = lParam;
7087 break;
7088 case SCI_STYLESETFONT:
7089 if (lParam != 0) {
7090 vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam));
7092 break;
7093 case SCI_STYLESETUNDERLINE:
7094 vs.styles[wParam].underline = lParam != 0;
7095 break;
7096 case SCI_STYLESETCASE:
7097 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
7098 break;
7099 case SCI_STYLESETCHARACTERSET:
7100 vs.styles[wParam].characterSet = lParam;
7101 pdoc->SetCaseFolder(NULL);
7102 break;
7103 case SCI_STYLESETVISIBLE:
7104 vs.styles[wParam].visible = lParam != 0;
7105 break;
7106 case SCI_STYLESETCHANGEABLE:
7107 vs.styles[wParam].changeable = lParam != 0;
7108 break;
7109 case SCI_STYLESETHOTSPOT:
7110 vs.styles[wParam].hotspot = lParam != 0;
7111 break;
7113 InvalidateStyleRedraw();
7116 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7117 vs.EnsureStyle(wParam);
7118 switch (iMessage) {
7119 case SCI_STYLEGETFORE:
7120 return vs.styles[wParam].fore.AsLong();
7121 case SCI_STYLEGETBACK:
7122 return vs.styles[wParam].back.AsLong();
7123 case SCI_STYLEGETBOLD:
7124 return vs.styles[wParam].weight > SC_WEIGHT_NORMAL;
7125 case SCI_STYLEGETWEIGHT:
7126 return vs.styles[wParam].weight;
7127 case SCI_STYLEGETITALIC:
7128 return vs.styles[wParam].italic ? 1 : 0;
7129 case SCI_STYLEGETEOLFILLED:
7130 return vs.styles[wParam].eolFilled ? 1 : 0;
7131 case SCI_STYLEGETSIZE:
7132 return vs.styles[wParam].size / SC_FONT_SIZE_MULTIPLIER;
7133 case SCI_STYLEGETSIZEFRACTIONAL:
7134 return vs.styles[wParam].size;
7135 case SCI_STYLEGETFONT:
7136 if (!vs.styles[wParam].fontName)
7137 return 0;
7138 if (lParam != 0)
7139 strcpy(CharPtrFromSPtr(lParam), vs.styles[wParam].fontName);
7140 return strlen(vs.styles[wParam].fontName);
7141 case SCI_STYLEGETUNDERLINE:
7142 return vs.styles[wParam].underline ? 1 : 0;
7143 case SCI_STYLEGETCASE:
7144 return static_cast<int>(vs.styles[wParam].caseForce);
7145 case SCI_STYLEGETCHARACTERSET:
7146 return vs.styles[wParam].characterSet;
7147 case SCI_STYLEGETVISIBLE:
7148 return vs.styles[wParam].visible ? 1 : 0;
7149 case SCI_STYLEGETCHANGEABLE:
7150 return vs.styles[wParam].changeable ? 1 : 0;
7151 case SCI_STYLEGETHOTSPOT:
7152 return vs.styles[wParam].hotspot ? 1 : 0;
7154 return 0;
7157 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
7158 const size_t n = strlen(val);
7159 if (lParam != 0) {
7160 char *ptr = reinterpret_cast<char *>(lParam);
7161 strcpy(ptr, val);
7163 return n; // Not including NUL
7166 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7167 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
7169 // Optional macro recording hook
7170 if (recordingMacro)
7171 NotifyMacroRecord(iMessage, wParam, lParam);
7173 switch (iMessage) {
7175 case SCI_GETTEXT: {
7176 if (lParam == 0)
7177 return pdoc->Length() + 1;
7178 if (wParam == 0)
7179 return 0;
7180 char *ptr = CharPtrFromSPtr(lParam);
7181 unsigned int iChar = 0;
7182 for (; iChar < wParam - 1; iChar++)
7183 ptr[iChar] = pdoc->CharAt(iChar);
7184 ptr[iChar] = '\0';
7185 return iChar;
7188 case SCI_SETTEXT: {
7189 if (lParam == 0)
7190 return 0;
7191 UndoGroup ug(pdoc);
7192 pdoc->DeleteChars(0, pdoc->Length());
7193 SetEmptySelection(0);
7194 pdoc->InsertCString(0, CharPtrFromSPtr(lParam));
7195 return 1;
7198 case SCI_GETTEXTLENGTH:
7199 return pdoc->Length();
7201 case SCI_CUT:
7202 Cut();
7203 SetLastXChosen();
7204 break;
7206 case SCI_COPY:
7207 Copy();
7208 break;
7210 case SCI_COPYALLOWLINE:
7211 CopyAllowLine();
7212 break;
7214 case SCI_VERTICALCENTRECARET:
7215 VerticalCentreCaret();
7216 break;
7218 case SCI_MOVESELECTEDLINESUP:
7219 MoveSelectedLinesUp();
7220 break;
7222 case SCI_MOVESELECTEDLINESDOWN:
7223 MoveSelectedLinesDown();
7224 break;
7226 case SCI_COPYRANGE:
7227 CopyRangeToClipboard(wParam, lParam);
7228 break;
7230 case SCI_COPYTEXT:
7231 CopyText(wParam, CharPtrFromSPtr(lParam));
7232 break;
7234 case SCI_PASTE:
7235 Paste();
7236 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
7237 SetLastXChosen();
7239 EnsureCaretVisible();
7240 break;
7242 case SCI_CLEAR:
7243 Clear();
7244 SetLastXChosen();
7245 EnsureCaretVisible();
7246 break;
7248 case SCI_UNDO:
7249 Undo();
7250 SetLastXChosen();
7251 break;
7253 case SCI_CANUNDO:
7254 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
7256 case SCI_EMPTYUNDOBUFFER:
7257 pdoc->DeleteUndoHistory();
7258 return 0;
7260 case SCI_GETFIRSTVISIBLELINE:
7261 return topLine;
7263 case SCI_SETFIRSTVISIBLELINE:
7264 ScrollTo(wParam);
7265 break;
7267 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
7268 int lineStart = pdoc->LineStart(wParam);
7269 int lineEnd = pdoc->LineStart(wParam + 1);
7270 if (lParam == 0) {
7271 return lineEnd - lineStart;
7273 char *ptr = CharPtrFromSPtr(lParam);
7274 int iPlace = 0;
7275 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
7276 ptr[iPlace++] = pdoc->CharAt(iChar);
7278 return iPlace;
7281 case SCI_GETLINECOUNT:
7282 if (pdoc->LinesTotal() == 0)
7283 return 1;
7284 else
7285 return pdoc->LinesTotal();
7287 case SCI_GETMODIFY:
7288 return !pdoc->IsSavePoint();
7290 case SCI_SETSEL: {
7291 int nStart = static_cast<int>(wParam);
7292 int nEnd = static_cast<int>(lParam);
7293 if (nEnd < 0)
7294 nEnd = pdoc->Length();
7295 if (nStart < 0)
7296 nStart = nEnd; // Remove selection
7297 InvalidateSelection(SelectionRange(nStart, nEnd));
7298 sel.Clear();
7299 sel.selType = Selection::selStream;
7300 SetSelection(nEnd, nStart);
7301 EnsureCaretVisible();
7303 break;
7305 case SCI_GETSELTEXT: {
7306 SelectionText selectedText;
7307 CopySelectionRange(&selectedText);
7308 if (lParam == 0) {
7309 return selectedText.len ? selectedText.len : 1;
7310 } else {
7311 char *ptr = CharPtrFromSPtr(lParam);
7312 int iChar = 0;
7313 if (selectedText.len) {
7314 for (; iChar < selectedText.len; iChar++)
7315 ptr[iChar] = selectedText.s[iChar];
7316 } else {
7317 ptr[0] = '\0';
7319 return iChar;
7323 case SCI_LINEFROMPOSITION:
7324 if (static_cast<int>(wParam) < 0)
7325 return 0;
7326 return pdoc->LineFromPosition(wParam);
7328 case SCI_POSITIONFROMLINE:
7329 if (static_cast<int>(wParam) < 0)
7330 wParam = pdoc->LineFromPosition(SelectionStart().Position());
7331 if (wParam == 0)
7332 return 0; // Even if there is no text, there is a first line that starts at 0
7333 if (static_cast<int>(wParam) > pdoc->LinesTotal())
7334 return -1;
7335 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
7336 // return -1;
7337 return pdoc->LineStart(wParam);
7339 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
7340 case SCI_LINELENGTH:
7341 if ((static_cast<int>(wParam) < 0) ||
7342 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
7343 return 0;
7344 return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
7346 case SCI_REPLACESEL: {
7347 if (lParam == 0)
7348 return 0;
7349 UndoGroup ug(pdoc);
7350 ClearSelection();
7351 char *replacement = CharPtrFromSPtr(lParam);
7352 pdoc->InsertCString(sel.MainCaret(), replacement);
7353 SetEmptySelection(sel.MainCaret() + istrlen(replacement));
7354 EnsureCaretVisible();
7356 break;
7358 case SCI_SETTARGETSTART:
7359 targetStart = wParam;
7360 break;
7362 case SCI_GETTARGETSTART:
7363 return targetStart;
7365 case SCI_SETTARGETEND:
7366 targetEnd = wParam;
7367 break;
7369 case SCI_GETTARGETEND:
7370 return targetEnd;
7372 case SCI_TARGETFROMSELECTION:
7373 if (sel.MainCaret() < sel.MainAnchor()) {
7374 targetStart = sel.MainCaret();
7375 targetEnd = sel.MainAnchor();
7376 } else {
7377 targetStart = sel.MainAnchor();
7378 targetEnd = sel.MainCaret();
7380 break;
7382 case SCI_REPLACETARGET:
7383 PLATFORM_ASSERT(lParam);
7384 return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam);
7386 case SCI_REPLACETARGETRE:
7387 PLATFORM_ASSERT(lParam);
7388 return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam);
7390 case SCI_SEARCHINTARGET:
7391 PLATFORM_ASSERT(lParam);
7392 return SearchInTarget(CharPtrFromSPtr(lParam), wParam);
7394 case SCI_SETSEARCHFLAGS:
7395 searchFlags = wParam;
7396 break;
7398 case SCI_GETSEARCHFLAGS:
7399 return searchFlags;
7401 case SCI_GETTAG:
7402 return GetTag(CharPtrFromSPtr(lParam), wParam);
7404 case SCI_POSITIONBEFORE:
7405 return pdoc->MovePositionOutsideChar(wParam - 1, -1, true);
7407 case SCI_POSITIONAFTER:
7408 return pdoc->MovePositionOutsideChar(wParam + 1, 1, true);
7410 case SCI_LINESCROLL:
7411 ScrollTo(topLine + lParam);
7412 HorizontalScrollTo(xOffset + wParam * vs.spaceWidth);
7413 return 1;
7415 case SCI_SETXOFFSET:
7416 xOffset = wParam;
7417 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7418 SetHorizontalScrollPos();
7419 Redraw();
7420 break;
7422 case SCI_GETXOFFSET:
7423 return xOffset;
7425 case SCI_CHOOSECARETX:
7426 SetLastXChosen();
7427 break;
7429 case SCI_SCROLLCARET:
7430 EnsureCaretVisible();
7431 break;
7433 case SCI_SETREADONLY:
7434 pdoc->SetReadOnly(wParam != 0);
7435 return 1;
7437 case SCI_GETREADONLY:
7438 return pdoc->IsReadOnly();
7440 case SCI_CANPASTE:
7441 return CanPaste();
7443 case SCI_POINTXFROMPOSITION:
7444 if (lParam < 0) {
7445 return 0;
7446 } else {
7447 Point pt = LocationFromPosition(lParam);
7448 return pt.x;
7451 case SCI_POINTYFROMPOSITION:
7452 if (lParam < 0) {
7453 return 0;
7454 } else {
7455 Point pt = LocationFromPosition(lParam);
7456 return pt.y;
7459 case SCI_FINDTEXT:
7460 return FindText(wParam, lParam);
7462 case SCI_GETTEXTRANGE: {
7463 if (lParam == 0)
7464 return 0;
7465 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7466 int cpMax = tr->chrg.cpMax;
7467 if (cpMax == -1)
7468 cpMax = pdoc->Length();
7469 PLATFORM_ASSERT(cpMax <= pdoc->Length());
7470 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
7471 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
7472 // Spec says copied text is terminated with a NUL
7473 tr->lpstrText[len] = '\0';
7474 return len; // Not including NUL
7477 case SCI_HIDESELECTION:
7478 hideSelection = wParam != 0;
7479 Redraw();
7480 break;
7482 case SCI_FORMATRANGE:
7483 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
7485 case SCI_GETMARGINLEFT:
7486 return vs.leftMarginWidth;
7488 case SCI_GETMARGINRIGHT:
7489 return vs.rightMarginWidth;
7491 case SCI_SETMARGINLEFT:
7492 vs.leftMarginWidth = lParam;
7493 InvalidateStyleRedraw();
7494 break;
7496 case SCI_SETMARGINRIGHT:
7497 vs.rightMarginWidth = lParam;
7498 InvalidateStyleRedraw();
7499 break;
7501 // Control specific mesages
7503 case SCI_ADDTEXT: {
7504 if (lParam == 0)
7505 return 0;
7506 pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam);
7507 SetEmptySelection(sel.MainCaret() + wParam);
7508 return 0;
7511 case SCI_ADDSTYLEDTEXT:
7512 if (lParam)
7513 AddStyledText(CharPtrFromSPtr(lParam), wParam);
7514 return 0;
7516 case SCI_INSERTTEXT: {
7517 if (lParam == 0)
7518 return 0;
7519 int insertPos = wParam;
7520 if (static_cast<int>(wParam) == -1)
7521 insertPos = CurrentPosition();
7522 int newCurrent = CurrentPosition();
7523 char *sz = CharPtrFromSPtr(lParam);
7524 pdoc->InsertCString(insertPos, sz);
7525 if (newCurrent > insertPos)
7526 newCurrent += istrlen(sz);
7527 SetEmptySelection(newCurrent);
7528 return 0;
7531 case SCI_APPENDTEXT:
7532 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam);
7533 return 0;
7535 case SCI_CLEARALL:
7536 ClearAll();
7537 return 0;
7539 case SCI_DELETERANGE:
7540 pdoc->DeleteChars(wParam, lParam);
7541 return 0;
7543 case SCI_CLEARDOCUMENTSTYLE:
7544 ClearDocumentStyle();
7545 return 0;
7547 case SCI_SETUNDOCOLLECTION:
7548 pdoc->SetUndoCollection(wParam != 0);
7549 return 0;
7551 case SCI_GETUNDOCOLLECTION:
7552 return pdoc->IsCollectingUndo();
7554 case SCI_BEGINUNDOACTION:
7555 pdoc->BeginUndoAction();
7556 return 0;
7558 case SCI_ENDUNDOACTION:
7559 pdoc->EndUndoAction();
7560 return 0;
7562 case SCI_GETCARETPERIOD:
7563 return caret.period;
7565 case SCI_SETCARETPERIOD:
7566 caret.period = wParam;
7567 break;
7569 case SCI_GETWORDCHARS:
7570 return pdoc->GetCharsOfClass(CharClassify::ccWord, reinterpret_cast<unsigned char *>(lParam));
7572 case SCI_SETWORDCHARS: {
7573 pdoc->SetDefaultCharClasses(false);
7574 if (lParam == 0)
7575 return 0;
7576 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
7578 break;
7580 case SCI_GETWHITESPACECHARS:
7581 return pdoc->GetCharsOfClass(CharClassify::ccSpace, reinterpret_cast<unsigned char *>(lParam));
7583 case SCI_SETWHITESPACECHARS: {
7584 if (lParam == 0)
7585 return 0;
7586 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
7588 break;
7590 case SCI_GETPUNCTUATIONCHARS:
7591 return pdoc->GetCharsOfClass(CharClassify::ccPunctuation, reinterpret_cast<unsigned char *>(lParam));
7593 case SCI_SETPUNCTUATIONCHARS: {
7594 if (lParam == 0)
7595 return 0;
7596 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccPunctuation);
7598 break;
7600 case SCI_SETCHARSDEFAULT:
7601 pdoc->SetDefaultCharClasses(true);
7602 break;
7604 case SCI_GETLENGTH:
7605 return pdoc->Length();
7607 case SCI_ALLOCATE:
7608 pdoc->Allocate(wParam);
7609 break;
7611 case SCI_GETCHARAT:
7612 return pdoc->CharAt(wParam);
7614 case SCI_SETCURRENTPOS:
7615 if (sel.IsRectangular()) {
7616 sel.Rectangular().caret.SetPosition(wParam);
7617 SetRectangularRange();
7618 Redraw();
7619 } else {
7620 SetSelection(wParam, sel.MainAnchor());
7622 break;
7624 case SCI_GETCURRENTPOS:
7625 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
7627 case SCI_SETANCHOR:
7628 if (sel.IsRectangular()) {
7629 sel.Rectangular().anchor.SetPosition(wParam);
7630 SetRectangularRange();
7631 Redraw();
7632 } else {
7633 SetSelection(sel.MainCaret(), wParam);
7635 break;
7637 case SCI_GETANCHOR:
7638 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
7640 case SCI_SETSELECTIONSTART:
7641 SetSelection(Platform::Maximum(sel.MainCaret(), wParam), wParam);
7642 break;
7644 case SCI_GETSELECTIONSTART:
7645 return sel.LimitsForRectangularElseMain().start.Position();
7647 case SCI_SETSELECTIONEND:
7648 SetSelection(wParam, Platform::Minimum(sel.MainAnchor(), wParam));
7649 break;
7651 case SCI_GETSELECTIONEND:
7652 return sel.LimitsForRectangularElseMain().end.Position();
7654 case SCI_SETEMPTYSELECTION:
7655 SetEmptySelection(wParam);
7656 break;
7658 case SCI_SETPRINTMAGNIFICATION:
7659 printMagnification = wParam;
7660 break;
7662 case SCI_GETPRINTMAGNIFICATION:
7663 return printMagnification;
7665 case SCI_SETPRINTCOLOURMODE:
7666 printColourMode = wParam;
7667 break;
7669 case SCI_GETPRINTCOLOURMODE:
7670 return printColourMode;
7672 case SCI_SETPRINTWRAPMODE:
7673 printWrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
7674 break;
7676 case SCI_GETPRINTWRAPMODE:
7677 return printWrapState;
7679 case SCI_GETSTYLEAT:
7680 if (static_cast<int>(wParam) >= pdoc->Length())
7681 return 0;
7682 else
7683 return pdoc->StyleAt(wParam);
7685 case SCI_REDO:
7686 Redo();
7687 break;
7689 case SCI_SELECTALL:
7690 SelectAll();
7691 break;
7693 case SCI_SETSAVEPOINT:
7694 pdoc->SetSavePoint();
7695 break;
7697 case SCI_GETSTYLEDTEXT: {
7698 if (lParam == 0)
7699 return 0;
7700 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7701 int iPlace = 0;
7702 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
7703 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
7704 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
7706 tr->lpstrText[iPlace] = '\0';
7707 tr->lpstrText[iPlace + 1] = '\0';
7708 return iPlace;
7711 case SCI_CANREDO:
7712 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
7714 case SCI_MARKERLINEFROMHANDLE:
7715 return pdoc->LineFromHandle(wParam);
7717 case SCI_MARKERDELETEHANDLE:
7718 pdoc->DeleteMarkFromHandle(wParam);
7719 break;
7721 case SCI_GETVIEWWS:
7722 return vs.viewWhitespace;
7724 case SCI_SETVIEWWS:
7725 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
7726 Redraw();
7727 break;
7729 case SCI_GETWHITESPACESIZE:
7730 return vs.whitespaceSize;
7732 case SCI_SETWHITESPACESIZE:
7733 vs.whitespaceSize = static_cast<int>(wParam);
7734 Redraw();
7735 break;
7737 case SCI_POSITIONFROMPOINT:
7738 return PositionFromLocation(Point(wParam, lParam), false, false);
7740 case SCI_POSITIONFROMPOINTCLOSE:
7741 return PositionFromLocation(Point(wParam, lParam), true, false);
7743 case SCI_CHARPOSITIONFROMPOINT:
7744 return PositionFromLocation(Point(wParam, lParam), false, true);
7746 case SCI_CHARPOSITIONFROMPOINTCLOSE:
7747 return PositionFromLocation(Point(wParam, lParam), true, true);
7749 case SCI_GOTOLINE:
7750 GoToLine(wParam);
7751 break;
7753 case SCI_GOTOPOS:
7754 SetEmptySelection(wParam);
7755 EnsureCaretVisible();
7756 break;
7758 case SCI_GETCURLINE: {
7759 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
7760 int lineStart = pdoc->LineStart(lineCurrentPos);
7761 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
7762 if (lParam == 0) {
7763 return 1 + lineEnd - lineStart;
7765 PLATFORM_ASSERT(wParam > 0);
7766 char *ptr = CharPtrFromSPtr(lParam);
7767 unsigned int iPlace = 0;
7768 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
7769 ptr[iPlace++] = pdoc->CharAt(iChar);
7771 ptr[iPlace] = '\0';
7772 return sel.MainCaret() - lineStart;
7775 case SCI_GETENDSTYLED:
7776 return pdoc->GetEndStyled();
7778 case SCI_GETEOLMODE:
7779 return pdoc->eolMode;
7781 case SCI_SETEOLMODE:
7782 pdoc->eolMode = wParam;
7783 break;
7785 case SCI_STARTSTYLING:
7786 pdoc->StartStyling(wParam, static_cast<char>(lParam));
7787 break;
7789 case SCI_SETSTYLING:
7790 pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
7791 break;
7793 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
7794 if (lParam == 0)
7795 return 0;
7796 pdoc->SetStyles(wParam, CharPtrFromSPtr(lParam));
7797 break;
7799 case SCI_SETBUFFEREDDRAW:
7800 bufferedDraw = wParam != 0;
7801 break;
7803 case SCI_GETBUFFEREDDRAW:
7804 return bufferedDraw;
7806 case SCI_GETTWOPHASEDRAW:
7807 return twoPhaseDraw;
7809 case SCI_SETTWOPHASEDRAW:
7810 twoPhaseDraw = wParam != 0;
7811 InvalidateStyleRedraw();
7812 break;
7814 case SCI_SETFONTQUALITY:
7815 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
7816 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
7817 InvalidateStyleRedraw();
7818 break;
7820 case SCI_GETFONTQUALITY:
7821 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
7823 case SCI_SETTABWIDTH:
7824 if (wParam > 0) {
7825 pdoc->tabInChars = wParam;
7826 if (pdoc->indentInChars == 0)
7827 pdoc->actualIndentInChars = pdoc->tabInChars;
7829 InvalidateStyleRedraw();
7830 break;
7832 case SCI_GETTABWIDTH:
7833 return pdoc->tabInChars;
7835 case SCI_SETINDENT:
7836 pdoc->indentInChars = wParam;
7837 if (pdoc->indentInChars != 0)
7838 pdoc->actualIndentInChars = pdoc->indentInChars;
7839 else
7840 pdoc->actualIndentInChars = pdoc->tabInChars;
7841 InvalidateStyleRedraw();
7842 break;
7844 case SCI_GETINDENT:
7845 return pdoc->indentInChars;
7847 case SCI_SETUSETABS:
7848 pdoc->useTabs = wParam != 0;
7849 InvalidateStyleRedraw();
7850 break;
7852 case SCI_GETUSETABS:
7853 return pdoc->useTabs;
7855 case SCI_SETLINEINDENTATION:
7856 pdoc->SetLineIndentation(wParam, lParam);
7857 break;
7859 case SCI_GETLINEINDENTATION:
7860 return pdoc->GetLineIndentation(wParam);
7862 case SCI_GETLINEINDENTPOSITION:
7863 return pdoc->GetLineIndentPosition(wParam);
7865 case SCI_SETTABINDENTS:
7866 pdoc->tabIndents = wParam != 0;
7867 break;
7869 case SCI_GETTABINDENTS:
7870 return pdoc->tabIndents;
7872 case SCI_SETBACKSPACEUNINDENTS:
7873 pdoc->backspaceUnindents = wParam != 0;
7874 break;
7876 case SCI_GETBACKSPACEUNINDENTS:
7877 return pdoc->backspaceUnindents;
7879 case SCI_SETMOUSEDWELLTIME:
7880 dwellDelay = wParam;
7881 ticksToDwell = dwellDelay;
7882 break;
7884 case SCI_GETMOUSEDWELLTIME:
7885 return dwellDelay;
7887 case SCI_WORDSTARTPOSITION:
7888 return pdoc->ExtendWordSelect(wParam, -1, lParam != 0);
7890 case SCI_WORDENDPOSITION:
7891 return pdoc->ExtendWordSelect(wParam, 1, lParam != 0);
7893 case SCI_SETWRAPMODE:
7894 switch (wParam) {
7895 case SC_WRAP_WORD:
7896 wrapState = eWrapWord;
7897 break;
7898 case SC_WRAP_CHAR:
7899 wrapState = eWrapChar;
7900 break;
7901 default:
7902 wrapState = eWrapNone;
7903 break;
7905 xOffset = 0;
7906 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7907 InvalidateStyleRedraw();
7908 ReconfigureScrollBars();
7909 break;
7911 case SCI_GETWRAPMODE:
7912 return wrapState;
7914 case SCI_SETWRAPVISUALFLAGS:
7915 if (wrapVisualFlags != static_cast<int>(wParam)) {
7916 wrapVisualFlags = wParam;
7917 InvalidateStyleRedraw();
7918 ReconfigureScrollBars();
7920 break;
7922 case SCI_GETWRAPVISUALFLAGS:
7923 return wrapVisualFlags;
7925 case SCI_SETWRAPVISUALFLAGSLOCATION:
7926 wrapVisualFlagsLocation = wParam;
7927 InvalidateStyleRedraw();
7928 break;
7930 case SCI_GETWRAPVISUALFLAGSLOCATION:
7931 return wrapVisualFlagsLocation;
7933 case SCI_SETWRAPSTARTINDENT:
7934 if (wrapVisualStartIndent != static_cast<int>(wParam)) {
7935 wrapVisualStartIndent = wParam;
7936 InvalidateStyleRedraw();
7937 ReconfigureScrollBars();
7939 break;
7941 case SCI_GETWRAPSTARTINDENT:
7942 return wrapVisualStartIndent;
7944 case SCI_SETWRAPINDENTMODE:
7945 if (wrapIndentMode != static_cast<int>(wParam)) {
7946 wrapIndentMode = wParam;
7947 InvalidateStyleRedraw();
7948 ReconfigureScrollBars();
7950 break;
7952 case SCI_GETWRAPINDENTMODE:
7953 return wrapIndentMode;
7955 case SCI_SETLAYOUTCACHE:
7956 llc.SetLevel(wParam);
7957 break;
7959 case SCI_GETLAYOUTCACHE:
7960 return llc.GetLevel();
7962 case SCI_SETPOSITIONCACHE:
7963 posCache.SetSize(wParam);
7964 break;
7966 case SCI_GETPOSITIONCACHE:
7967 return posCache.GetSize();
7969 case SCI_SETSCROLLWIDTH:
7970 PLATFORM_ASSERT(wParam > 0);
7971 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
7972 lineWidthMaxSeen = 0;
7973 scrollWidth = wParam;
7974 SetScrollBars();
7976 break;
7978 case SCI_GETSCROLLWIDTH:
7979 return scrollWidth;
7981 case SCI_SETSCROLLWIDTHTRACKING:
7982 trackLineWidth = wParam != 0;
7983 break;
7985 case SCI_GETSCROLLWIDTHTRACKING:
7986 return trackLineWidth;
7988 case SCI_LINESJOIN:
7989 LinesJoin();
7990 break;
7992 case SCI_LINESSPLIT:
7993 LinesSplit(wParam);
7994 break;
7996 case SCI_TEXTWIDTH:
7997 PLATFORM_ASSERT(wParam < vs.stylesSize);
7998 PLATFORM_ASSERT(lParam);
7999 return TextWidth(wParam, CharPtrFromSPtr(lParam));
8001 case SCI_TEXTHEIGHT:
8002 return vs.lineHeight;
8004 case SCI_SETENDATLASTLINE:
8005 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
8006 if (endAtLastLine != (wParam != 0)) {
8007 endAtLastLine = wParam != 0;
8008 SetScrollBars();
8010 break;
8012 case SCI_GETENDATLASTLINE:
8013 return endAtLastLine;
8015 case SCI_SETCARETSTICKY:
8016 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
8017 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
8018 caretSticky = wParam;
8020 break;
8022 case SCI_GETCARETSTICKY:
8023 return caretSticky;
8025 case SCI_TOGGLECARETSTICKY:
8026 caretSticky = !caretSticky;
8027 break;
8029 case SCI_GETCOLUMN:
8030 return pdoc->GetColumn(wParam);
8032 case SCI_FINDCOLUMN:
8033 return pdoc->FindColumn(wParam, lParam);
8035 case SCI_SETHSCROLLBAR :
8036 if (horizontalScrollBarVisible != (wParam != 0)) {
8037 horizontalScrollBarVisible = wParam != 0;
8038 SetScrollBars();
8039 ReconfigureScrollBars();
8041 break;
8043 case SCI_GETHSCROLLBAR:
8044 return horizontalScrollBarVisible;
8046 case SCI_SETVSCROLLBAR:
8047 if (verticalScrollBarVisible != (wParam != 0)) {
8048 verticalScrollBarVisible = wParam != 0;
8049 SetScrollBars();
8050 ReconfigureScrollBars();
8052 break;
8054 case SCI_GETVSCROLLBAR:
8055 return verticalScrollBarVisible;
8057 case SCI_SETINDENTATIONGUIDES:
8058 vs.viewIndentationGuides = IndentView(wParam);
8059 Redraw();
8060 break;
8062 case SCI_GETINDENTATIONGUIDES:
8063 return vs.viewIndentationGuides;
8065 case SCI_SETHIGHLIGHTGUIDE:
8066 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
8067 highlightGuideColumn = wParam;
8068 Redraw();
8070 break;
8072 case SCI_GETHIGHLIGHTGUIDE:
8073 return highlightGuideColumn;
8075 case SCI_GETLINEENDPOSITION:
8076 return pdoc->LineEnd(wParam);
8078 case SCI_SETCODEPAGE:
8079 if (ValidCodePage(wParam)) {
8080 if (pdoc->SetDBCSCodePage(wParam)) {
8081 InvalidateStyleRedraw();
8084 break;
8086 case SCI_GETCODEPAGE:
8087 return pdoc->dbcsCodePage;
8089 #ifdef INCLUDE_DEPRECATED_FEATURES
8090 case SCI_SETUSEPALETTE:
8091 InvalidateStyleRedraw();
8092 break;
8094 case SCI_GETUSEPALETTE:
8095 return 0;
8096 #endif
8098 // Marker definition and setting
8099 case SCI_MARKERDEFINE:
8100 if (wParam <= MARKER_MAX) {
8101 vs.markers[wParam].markType = lParam;
8102 vs.CalcLargestMarkerHeight();
8104 InvalidateStyleData();
8105 RedrawSelMargin();
8106 break;
8108 case SCI_MARKERSYMBOLDEFINED:
8109 if (wParam <= MARKER_MAX)
8110 return vs.markers[wParam].markType;
8111 else
8112 return 0;
8114 case SCI_MARKERSETFORE:
8115 if (wParam <= MARKER_MAX)
8116 vs.markers[wParam].fore = ColourDesired(lParam);
8117 InvalidateStyleData();
8118 RedrawSelMargin();
8119 break;
8120 case SCI_MARKERSETBACKSELECTED:
8121 if (wParam <= MARKER_MAX)
8122 vs.markers[wParam].backSelected = ColourDesired(lParam);
8123 InvalidateStyleData();
8124 RedrawSelMargin();
8125 break;
8126 case SCI_MARKERENABLEHIGHLIGHT:
8127 highlightDelimiter.isEnabled = wParam == 1;
8128 RedrawSelMargin();
8129 break;
8130 case SCI_MARKERSETBACK:
8131 if (wParam <= MARKER_MAX)
8132 vs.markers[wParam].back = ColourDesired(lParam);
8133 InvalidateStyleData();
8134 RedrawSelMargin();
8135 break;
8136 case SCI_MARKERSETALPHA:
8137 if (wParam <= MARKER_MAX)
8138 vs.markers[wParam].alpha = lParam;
8139 InvalidateStyleRedraw();
8140 break;
8141 case SCI_MARKERADD: {
8142 int markerID = pdoc->AddMark(wParam, lParam);
8143 return markerID;
8145 case SCI_MARKERADDSET:
8146 if (lParam != 0)
8147 pdoc->AddMarkSet(wParam, lParam);
8148 break;
8150 case SCI_MARKERDELETE:
8151 pdoc->DeleteMark(wParam, lParam);
8152 break;
8154 case SCI_MARKERDELETEALL:
8155 pdoc->DeleteAllMarks(static_cast<int>(wParam));
8156 break;
8158 case SCI_MARKERGET:
8159 return pdoc->GetMark(wParam);
8161 case SCI_MARKERNEXT:
8162 return pdoc->MarkerNext(wParam, lParam);
8164 case SCI_MARKERPREVIOUS: {
8165 for (int iLine = wParam; iLine >= 0; iLine--) {
8166 if ((pdoc->GetMark(iLine) & lParam) != 0)
8167 return iLine;
8170 return -1;
8172 case SCI_MARKERDEFINEPIXMAP:
8173 if (wParam <= MARKER_MAX) {
8174 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
8175 vs.CalcLargestMarkerHeight();
8177 InvalidateStyleData();
8178 RedrawSelMargin();
8179 break;
8181 case SCI_RGBAIMAGESETWIDTH:
8182 sizeRGBAImage.x = wParam;
8183 break;
8185 case SCI_RGBAIMAGESETHEIGHT:
8186 sizeRGBAImage.y = wParam;
8187 break;
8189 case SCI_RGBAIMAGESETSCALE:
8190 scaleRGBAImage = wParam;
8191 break;
8193 case SCI_MARKERDEFINERGBAIMAGE:
8194 if (wParam <= MARKER_MAX) {
8195 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0, reinterpret_cast<unsigned char *>(lParam));
8196 vs.CalcLargestMarkerHeight();
8198 InvalidateStyleData();
8199 RedrawSelMargin();
8200 break;
8202 case SCI_SETMARGINTYPEN:
8203 if (ValidMargin(wParam)) {
8204 vs.ms[wParam].style = lParam;
8205 InvalidateStyleRedraw();
8207 break;
8209 case SCI_GETMARGINTYPEN:
8210 if (ValidMargin(wParam))
8211 return vs.ms[wParam].style;
8212 else
8213 return 0;
8215 case SCI_SETMARGINWIDTHN:
8216 if (ValidMargin(wParam)) {
8217 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
8218 if (vs.ms[wParam].width != lParam) {
8219 vs.ms[wParam].width = lParam;
8220 InvalidateStyleRedraw();
8223 break;
8225 case SCI_GETMARGINWIDTHN:
8226 if (ValidMargin(wParam))
8227 return vs.ms[wParam].width;
8228 else
8229 return 0;
8231 case SCI_SETMARGINMASKN:
8232 if (ValidMargin(wParam)) {
8233 vs.ms[wParam].mask = lParam;
8234 InvalidateStyleRedraw();
8236 break;
8238 case SCI_GETMARGINMASKN:
8239 if (ValidMargin(wParam))
8240 return vs.ms[wParam].mask;
8241 else
8242 return 0;
8244 case SCI_SETMARGINSENSITIVEN:
8245 if (ValidMargin(wParam)) {
8246 vs.ms[wParam].sensitive = lParam != 0;
8247 InvalidateStyleRedraw();
8249 break;
8251 case SCI_GETMARGINSENSITIVEN:
8252 if (ValidMargin(wParam))
8253 return vs.ms[wParam].sensitive ? 1 : 0;
8254 else
8255 return 0;
8257 case SCI_SETMARGINCURSORN:
8258 if (ValidMargin(wParam))
8259 vs.ms[wParam].cursor = lParam;
8260 break;
8262 case SCI_GETMARGINCURSORN:
8263 if (ValidMargin(wParam))
8264 return vs.ms[wParam].cursor;
8265 else
8266 return 0;
8268 case SCI_STYLECLEARALL:
8269 vs.ClearStyles();
8270 InvalidateStyleRedraw();
8271 break;
8273 case SCI_STYLESETFORE:
8274 case SCI_STYLESETBACK:
8275 case SCI_STYLESETBOLD:
8276 case SCI_STYLESETWEIGHT:
8277 case SCI_STYLESETITALIC:
8278 case SCI_STYLESETEOLFILLED:
8279 case SCI_STYLESETSIZE:
8280 case SCI_STYLESETSIZEFRACTIONAL:
8281 case SCI_STYLESETFONT:
8282 case SCI_STYLESETUNDERLINE:
8283 case SCI_STYLESETCASE:
8284 case SCI_STYLESETCHARACTERSET:
8285 case SCI_STYLESETVISIBLE:
8286 case SCI_STYLESETCHANGEABLE:
8287 case SCI_STYLESETHOTSPOT:
8288 StyleSetMessage(iMessage, wParam, lParam);
8289 break;
8291 case SCI_STYLEGETFORE:
8292 case SCI_STYLEGETBACK:
8293 case SCI_STYLEGETBOLD:
8294 case SCI_STYLEGETWEIGHT:
8295 case SCI_STYLEGETITALIC:
8296 case SCI_STYLEGETEOLFILLED:
8297 case SCI_STYLEGETSIZE:
8298 case SCI_STYLEGETSIZEFRACTIONAL:
8299 case SCI_STYLEGETFONT:
8300 case SCI_STYLEGETUNDERLINE:
8301 case SCI_STYLEGETCASE:
8302 case SCI_STYLEGETCHARACTERSET:
8303 case SCI_STYLEGETVISIBLE:
8304 case SCI_STYLEGETCHANGEABLE:
8305 case SCI_STYLEGETHOTSPOT:
8306 return StyleGetMessage(iMessage, wParam, lParam);
8308 case SCI_STYLERESETDEFAULT:
8309 vs.ResetDefaultStyle();
8310 InvalidateStyleRedraw();
8311 break;
8312 case SCI_SETSTYLEBITS:
8313 vs.EnsureStyle((1 << wParam) - 1);
8314 pdoc->SetStylingBits(wParam);
8315 break;
8317 case SCI_GETSTYLEBITS:
8318 return pdoc->stylingBits;
8320 case SCI_SETLINESTATE:
8321 return pdoc->SetLineState(wParam, lParam);
8323 case SCI_GETLINESTATE:
8324 return pdoc->GetLineState(wParam);
8326 case SCI_GETMAXLINESTATE:
8327 return pdoc->GetMaxLineState();
8329 case SCI_GETCARETLINEVISIBLE:
8330 return vs.showCaretLineBackground;
8331 case SCI_SETCARETLINEVISIBLE:
8332 vs.showCaretLineBackground = wParam != 0;
8333 InvalidateStyleRedraw();
8334 break;
8335 case SCI_GETCARETLINEBACK:
8336 return vs.caretLineBackground.AsLong();
8337 case SCI_SETCARETLINEBACK:
8338 vs.caretLineBackground = wParam;
8339 InvalidateStyleRedraw();
8340 break;
8341 case SCI_GETCARETLINEBACKALPHA:
8342 return vs.caretLineAlpha;
8343 case SCI_SETCARETLINEBACKALPHA:
8344 vs.caretLineAlpha = wParam;
8345 InvalidateStyleRedraw();
8346 break;
8348 // Folding messages
8350 case SCI_VISIBLEFROMDOCLINE:
8351 return cs.DisplayFromDoc(wParam);
8353 case SCI_DOCLINEFROMVISIBLE:
8354 return cs.DocFromDisplay(wParam);
8356 case SCI_WRAPCOUNT:
8357 return WrapCount(wParam);
8359 case SCI_SETFOLDLEVEL: {
8360 int prev = pdoc->SetLevel(wParam, lParam);
8361 if (prev != lParam)
8362 RedrawSelMargin();
8363 return prev;
8366 case SCI_GETFOLDLEVEL:
8367 return pdoc->GetLevel(wParam);
8369 case SCI_GETLASTCHILD:
8370 return pdoc->GetLastChild(wParam, lParam);
8372 case SCI_GETFOLDPARENT:
8373 return pdoc->GetFoldParent(wParam);
8375 case SCI_SHOWLINES:
8376 cs.SetVisible(wParam, lParam, true);
8377 SetScrollBars();
8378 Redraw();
8379 break;
8381 case SCI_HIDELINES:
8382 if (wParam > 0)
8383 cs.SetVisible(wParam, lParam, false);
8384 SetScrollBars();
8385 Redraw();
8386 break;
8388 case SCI_GETLINEVISIBLE:
8389 return cs.GetVisible(wParam);
8391 case SCI_GETALLLINESVISIBLE:
8392 return cs.HiddenLines() ? 0 : 1;
8394 case SCI_SETFOLDEXPANDED:
8395 if (cs.SetExpanded(wParam, lParam != 0)) {
8396 RedrawSelMargin();
8398 break;
8400 case SCI_GETFOLDEXPANDED:
8401 return cs.GetExpanded(wParam);
8403 case SCI_SETFOLDFLAGS:
8404 foldFlags = wParam;
8405 Redraw();
8406 break;
8408 case SCI_TOGGLEFOLD:
8409 ToggleContraction(wParam);
8410 break;
8412 case SCI_CONTRACTEDFOLDNEXT:
8413 return ContractedFoldNext(wParam);
8415 case SCI_ENSUREVISIBLE:
8416 EnsureLineVisible(wParam, false);
8417 break;
8419 case SCI_ENSUREVISIBLEENFORCEPOLICY:
8420 EnsureLineVisible(wParam, true);
8421 break;
8423 case SCI_SEARCHANCHOR:
8424 SearchAnchor();
8425 break;
8427 case SCI_SEARCHNEXT:
8428 case SCI_SEARCHPREV:
8429 return SearchText(iMessage, wParam, lParam);
8431 case SCI_SETXCARETPOLICY:
8432 caretXPolicy = wParam;
8433 caretXSlop = lParam;
8434 break;
8436 case SCI_SETYCARETPOLICY:
8437 caretYPolicy = wParam;
8438 caretYSlop = lParam;
8439 break;
8441 case SCI_SETVISIBLEPOLICY:
8442 visiblePolicy = wParam;
8443 visibleSlop = lParam;
8444 break;
8446 case SCI_LINESONSCREEN:
8447 return LinesOnScreen();
8449 case SCI_SETSELFORE:
8450 vs.selforeset = wParam != 0;
8451 vs.selforeground = ColourDesired(lParam);
8452 vs.selAdditionalForeground = ColourDesired(lParam);
8453 InvalidateStyleRedraw();
8454 break;
8456 case SCI_SETSELBACK:
8457 vs.selbackset = wParam != 0;
8458 vs.selbackground = ColourDesired(lParam);
8459 vs.selAdditionalBackground = ColourDesired(lParam);
8460 InvalidateStyleRedraw();
8461 break;
8463 case SCI_SETSELALPHA:
8464 vs.selAlpha = wParam;
8465 vs.selAdditionalAlpha = wParam;
8466 InvalidateStyleRedraw();
8467 break;
8469 case SCI_GETSELALPHA:
8470 return vs.selAlpha;
8472 case SCI_GETSELEOLFILLED:
8473 return vs.selEOLFilled;
8475 case SCI_SETSELEOLFILLED:
8476 vs.selEOLFilled = wParam != 0;
8477 InvalidateStyleRedraw();
8478 break;
8480 case SCI_SETWHITESPACEFORE:
8481 vs.whitespaceForegroundSet = wParam != 0;
8482 vs.whitespaceForeground = ColourDesired(lParam);
8483 InvalidateStyleRedraw();
8484 break;
8486 case SCI_SETWHITESPACEBACK:
8487 vs.whitespaceBackgroundSet = wParam != 0;
8488 vs.whitespaceBackground = ColourDesired(lParam);
8489 InvalidateStyleRedraw();
8490 break;
8492 case SCI_SETCARETFORE:
8493 vs.caretcolour = ColourDesired(wParam);
8494 InvalidateStyleRedraw();
8495 break;
8497 case SCI_GETCARETFORE:
8498 return vs.caretcolour.AsLong();
8500 case SCI_SETCARETSTYLE:
8501 if (wParam <= CARETSTYLE_BLOCK)
8502 vs.caretStyle = wParam;
8503 else
8504 /* Default to the line caret */
8505 vs.caretStyle = CARETSTYLE_LINE;
8506 InvalidateStyleRedraw();
8507 break;
8509 case SCI_GETCARETSTYLE:
8510 return vs.caretStyle;
8512 case SCI_SETCARETWIDTH:
8513 if (static_cast<int>(wParam) <= 0)
8514 vs.caretWidth = 0;
8515 else if (wParam >= 3)
8516 vs.caretWidth = 3;
8517 else
8518 vs.caretWidth = wParam;
8519 InvalidateStyleRedraw();
8520 break;
8522 case SCI_GETCARETWIDTH:
8523 return vs.caretWidth;
8525 case SCI_ASSIGNCMDKEY:
8526 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8527 Platform::HighShortFromLong(wParam), lParam);
8528 break;
8530 case SCI_CLEARCMDKEY:
8531 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8532 Platform::HighShortFromLong(wParam), SCI_NULL);
8533 break;
8535 case SCI_CLEARALLCMDKEYS:
8536 kmap.Clear();
8537 break;
8539 case SCI_INDICSETSTYLE:
8540 if (wParam <= INDIC_MAX) {
8541 vs.indicators[wParam].style = lParam;
8542 InvalidateStyleRedraw();
8544 break;
8546 case SCI_INDICGETSTYLE:
8547 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
8549 case SCI_INDICSETFORE:
8550 if (wParam <= INDIC_MAX) {
8551 vs.indicators[wParam].fore = ColourDesired(lParam);
8552 InvalidateStyleRedraw();
8554 break;
8556 case SCI_INDICGETFORE:
8557 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.AsLong() : 0;
8559 case SCI_INDICSETUNDER:
8560 if (wParam <= INDIC_MAX) {
8561 vs.indicators[wParam].under = lParam != 0;
8562 InvalidateStyleRedraw();
8564 break;
8566 case SCI_INDICGETUNDER:
8567 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
8569 case SCI_INDICSETALPHA:
8570 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8571 vs.indicators[wParam].fillAlpha = lParam;
8572 InvalidateStyleRedraw();
8574 break;
8576 case SCI_INDICGETALPHA:
8577 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
8579 case SCI_INDICSETOUTLINEALPHA:
8580 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8581 vs.indicators[wParam].outlineAlpha = lParam;
8582 InvalidateStyleRedraw();
8584 break;
8586 case SCI_INDICGETOUTLINEALPHA:
8587 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
8589 case SCI_SETINDICATORCURRENT:
8590 pdoc->decorations.SetCurrentIndicator(wParam);
8591 break;
8592 case SCI_GETINDICATORCURRENT:
8593 return pdoc->decorations.GetCurrentIndicator();
8594 case SCI_SETINDICATORVALUE:
8595 pdoc->decorations.SetCurrentValue(wParam);
8596 break;
8597 case SCI_GETINDICATORVALUE:
8598 return pdoc->decorations.GetCurrentValue();
8600 case SCI_INDICATORFILLRANGE:
8601 pdoc->DecorationFillRange(wParam, pdoc->decorations.GetCurrentValue(), lParam);
8602 break;
8604 case SCI_INDICATORCLEARRANGE:
8605 pdoc->DecorationFillRange(wParam, 0, lParam);
8606 break;
8608 case SCI_INDICATORALLONFOR:
8609 return pdoc->decorations.AllOnFor(wParam);
8611 case SCI_INDICATORVALUEAT:
8612 return pdoc->decorations.ValueAt(wParam, lParam);
8614 case SCI_INDICATORSTART:
8615 return pdoc->decorations.Start(wParam, lParam);
8617 case SCI_INDICATOREND:
8618 return pdoc->decorations.End(wParam, lParam);
8620 case SCI_LINEDOWN:
8621 case SCI_LINEDOWNEXTEND:
8622 case SCI_PARADOWN:
8623 case SCI_PARADOWNEXTEND:
8624 case SCI_LINEUP:
8625 case SCI_LINEUPEXTEND:
8626 case SCI_PARAUP:
8627 case SCI_PARAUPEXTEND:
8628 case SCI_CHARLEFT:
8629 case SCI_CHARLEFTEXTEND:
8630 case SCI_CHARRIGHT:
8631 case SCI_CHARRIGHTEXTEND:
8632 case SCI_WORDLEFT:
8633 case SCI_WORDLEFTEXTEND:
8634 case SCI_WORDRIGHT:
8635 case SCI_WORDRIGHTEXTEND:
8636 case SCI_WORDLEFTEND:
8637 case SCI_WORDLEFTENDEXTEND:
8638 case SCI_WORDRIGHTEND:
8639 case SCI_WORDRIGHTENDEXTEND:
8640 case SCI_HOME:
8641 case SCI_HOMEEXTEND:
8642 case SCI_LINEEND:
8643 case SCI_LINEENDEXTEND:
8644 case SCI_HOMEWRAP:
8645 case SCI_HOMEWRAPEXTEND:
8646 case SCI_LINEENDWRAP:
8647 case SCI_LINEENDWRAPEXTEND:
8648 case SCI_DOCUMENTSTART:
8649 case SCI_DOCUMENTSTARTEXTEND:
8650 case SCI_DOCUMENTEND:
8651 case SCI_DOCUMENTENDEXTEND:
8652 case SCI_SCROLLTOSTART:
8653 case SCI_SCROLLTOEND:
8655 case SCI_STUTTEREDPAGEUP:
8656 case SCI_STUTTEREDPAGEUPEXTEND:
8657 case SCI_STUTTEREDPAGEDOWN:
8658 case SCI_STUTTEREDPAGEDOWNEXTEND:
8660 case SCI_PAGEUP:
8661 case SCI_PAGEUPEXTEND:
8662 case SCI_PAGEDOWN:
8663 case SCI_PAGEDOWNEXTEND:
8664 case SCI_EDITTOGGLEOVERTYPE:
8665 case SCI_CANCEL:
8666 case SCI_DELETEBACK:
8667 case SCI_TAB:
8668 case SCI_BACKTAB:
8669 case SCI_NEWLINE:
8670 case SCI_FORMFEED:
8671 case SCI_VCHOME:
8672 case SCI_VCHOMEEXTEND:
8673 case SCI_VCHOMEWRAP:
8674 case SCI_VCHOMEWRAPEXTEND:
8675 case SCI_VCHOMEDISPLAY:
8676 case SCI_VCHOMEDISPLAYEXTEND:
8677 case SCI_ZOOMIN:
8678 case SCI_ZOOMOUT:
8679 case SCI_DELWORDLEFT:
8680 case SCI_DELWORDRIGHT:
8681 case SCI_DELWORDRIGHTEND:
8682 case SCI_DELLINELEFT:
8683 case SCI_DELLINERIGHT:
8684 case SCI_LINECOPY:
8685 case SCI_LINECUT:
8686 case SCI_LINEDELETE:
8687 case SCI_LINETRANSPOSE:
8688 case SCI_LINEDUPLICATE:
8689 case SCI_LOWERCASE:
8690 case SCI_UPPERCASE:
8691 case SCI_LINESCROLLDOWN:
8692 case SCI_LINESCROLLUP:
8693 case SCI_WORDPARTLEFT:
8694 case SCI_WORDPARTLEFTEXTEND:
8695 case SCI_WORDPARTRIGHT:
8696 case SCI_WORDPARTRIGHTEXTEND:
8697 case SCI_DELETEBACKNOTLINE:
8698 case SCI_HOMEDISPLAY:
8699 case SCI_HOMEDISPLAYEXTEND:
8700 case SCI_LINEENDDISPLAY:
8701 case SCI_LINEENDDISPLAYEXTEND:
8702 case SCI_LINEDOWNRECTEXTEND:
8703 case SCI_LINEUPRECTEXTEND:
8704 case SCI_CHARLEFTRECTEXTEND:
8705 case SCI_CHARRIGHTRECTEXTEND:
8706 case SCI_HOMERECTEXTEND:
8707 case SCI_VCHOMERECTEXTEND:
8708 case SCI_LINEENDRECTEXTEND:
8709 case SCI_PAGEUPRECTEXTEND:
8710 case SCI_PAGEDOWNRECTEXTEND:
8711 case SCI_SELECTIONDUPLICATE:
8712 return KeyCommand(iMessage);
8714 case SCI_BRACEHIGHLIGHT:
8715 SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
8716 break;
8718 case SCI_BRACEHIGHLIGHTINDICATOR:
8719 if (lParam >= 0 && lParam <= INDIC_MAX) {
8720 vs.braceHighlightIndicatorSet = wParam != 0;
8721 vs.braceHighlightIndicator = lParam;
8723 break;
8725 case SCI_BRACEBADLIGHT:
8726 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
8727 break;
8729 case SCI_BRACEBADLIGHTINDICATOR:
8730 if (lParam >= 0 && lParam <= INDIC_MAX) {
8731 vs.braceBadLightIndicatorSet = wParam != 0;
8732 vs.braceBadLightIndicator = lParam;
8734 break;
8736 case SCI_BRACEMATCH:
8737 // wParam is position of char to find brace for,
8738 // lParam is maximum amount of text to restyle to find it
8739 return pdoc->BraceMatch(wParam, lParam);
8741 case SCI_GETVIEWEOL:
8742 return vs.viewEOL;
8744 case SCI_SETVIEWEOL:
8745 vs.viewEOL = wParam != 0;
8746 InvalidateStyleRedraw();
8747 break;
8749 case SCI_SETZOOM:
8750 vs.zoomLevel = wParam;
8751 InvalidateStyleRedraw();
8752 NotifyZoom();
8753 break;
8755 case SCI_GETZOOM:
8756 return vs.zoomLevel;
8758 case SCI_GETEDGECOLUMN:
8759 return theEdge;
8761 case SCI_SETEDGECOLUMN:
8762 theEdge = wParam;
8763 InvalidateStyleRedraw();
8764 break;
8766 case SCI_GETEDGEMODE:
8767 return vs.edgeState;
8769 case SCI_SETEDGEMODE:
8770 vs.edgeState = wParam;
8771 InvalidateStyleRedraw();
8772 break;
8774 case SCI_GETEDGECOLOUR:
8775 return vs.edgecolour.AsLong();
8777 case SCI_SETEDGECOLOUR:
8778 vs.edgecolour = ColourDesired(wParam);
8779 InvalidateStyleRedraw();
8780 break;
8782 case SCI_GETDOCPOINTER:
8783 return reinterpret_cast<sptr_t>(pdoc);
8785 case SCI_SETDOCPOINTER:
8786 CancelModes();
8787 SetDocPointer(reinterpret_cast<Document *>(lParam));
8788 return 0;
8790 case SCI_CREATEDOCUMENT: {
8791 Document *doc = new Document();
8792 if (doc) {
8793 doc->AddRef();
8795 return reinterpret_cast<sptr_t>(doc);
8798 case SCI_ADDREFDOCUMENT:
8799 (reinterpret_cast<Document *>(lParam))->AddRef();
8800 break;
8802 case SCI_RELEASEDOCUMENT:
8803 (reinterpret_cast<Document *>(lParam))->Release();
8804 break;
8806 case SCI_CREATELOADER: {
8807 Document *doc = new Document();
8808 if (doc) {
8809 doc->AddRef();
8810 doc->Allocate(wParam);
8811 doc->SetUndoCollection(false);
8813 return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
8816 case SCI_SETMODEVENTMASK:
8817 modEventMask = wParam;
8818 return 0;
8820 case SCI_GETMODEVENTMASK:
8821 return modEventMask;
8823 case SCI_CONVERTEOLS:
8824 pdoc->ConvertLineEnds(wParam);
8825 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
8826 return 0;
8828 case SCI_SETLENGTHFORENCODE:
8829 lengthForEncode = wParam;
8830 return 0;
8832 case SCI_SELECTIONISRECTANGLE:
8833 return sel.selType == Selection::selRectangle ? 1 : 0;
8835 case SCI_SETSELECTIONMODE: {
8836 switch (wParam) {
8837 case SC_SEL_STREAM:
8838 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
8839 sel.selType = Selection::selStream;
8840 break;
8841 case SC_SEL_RECTANGLE:
8842 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
8843 sel.selType = Selection::selRectangle;
8844 break;
8845 case SC_SEL_LINES:
8846 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
8847 sel.selType = Selection::selLines;
8848 break;
8849 case SC_SEL_THIN:
8850 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
8851 sel.selType = Selection::selThin;
8852 break;
8853 default:
8854 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
8855 sel.selType = Selection::selStream;
8857 InvalidateSelection(sel.RangeMain(), true);
8859 case SCI_GETSELECTIONMODE:
8860 switch (sel.selType) {
8861 case Selection::selStream:
8862 return SC_SEL_STREAM;
8863 case Selection::selRectangle:
8864 return SC_SEL_RECTANGLE;
8865 case Selection::selLines:
8866 return SC_SEL_LINES;
8867 case Selection::selThin:
8868 return SC_SEL_THIN;
8869 default: // ?!
8870 return SC_SEL_STREAM;
8872 case SCI_GETLINESELSTARTPOSITION:
8873 case SCI_GETLINESELENDPOSITION: {
8874 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(wParam)),
8875 SelectionPosition(pdoc->LineEnd(wParam)));
8876 for (size_t r=0; r<sel.Count(); r++) {
8877 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
8878 if (portion.start.IsValid()) {
8879 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
8882 return INVALID_POSITION;
8885 case SCI_SETOVERTYPE:
8886 inOverstrike = wParam != 0;
8887 break;
8889 case SCI_GETOVERTYPE:
8890 return inOverstrike ? 1 : 0;
8892 case SCI_SETFOCUS:
8893 SetFocusState(wParam != 0);
8894 break;
8896 case SCI_GETFOCUS:
8897 return hasFocus;
8899 case SCI_SETSTATUS:
8900 errorStatus = wParam;
8901 break;
8903 case SCI_GETSTATUS:
8904 return errorStatus;
8906 case SCI_SETMOUSEDOWNCAPTURES:
8907 mouseDownCaptures = wParam != 0;
8908 break;
8910 case SCI_GETMOUSEDOWNCAPTURES:
8911 return mouseDownCaptures;
8913 case SCI_SETCURSOR:
8914 cursorMode = wParam;
8915 DisplayCursor(Window::cursorText);
8916 break;
8918 case SCI_GETCURSOR:
8919 return cursorMode;
8921 case SCI_SETCONTROLCHARSYMBOL:
8922 controlCharSymbol = wParam;
8923 break;
8925 case SCI_GETCONTROLCHARSYMBOL:
8926 return controlCharSymbol;
8928 case SCI_STARTRECORD:
8929 recordingMacro = true;
8930 return 0;
8932 case SCI_STOPRECORD:
8933 recordingMacro = false;
8934 return 0;
8936 case SCI_MOVECARETINSIDEVIEW:
8937 MoveCaretInsideView();
8938 break;
8940 case SCI_SETFOLDMARGINCOLOUR:
8941 vs.foldmarginColourSet = wParam != 0;
8942 vs.foldmarginColour = ColourDesired(lParam);
8943 InvalidateStyleRedraw();
8944 break;
8946 case SCI_SETFOLDMARGINHICOLOUR:
8947 vs.foldmarginHighlightColourSet = wParam != 0;
8948 vs.foldmarginHighlightColour = ColourDesired(lParam);
8949 InvalidateStyleRedraw();
8950 break;
8952 case SCI_SETHOTSPOTACTIVEFORE:
8953 vs.hotspotForegroundSet = wParam != 0;
8954 vs.hotspotForeground = ColourDesired(lParam);
8955 InvalidateStyleRedraw();
8956 break;
8958 case SCI_GETHOTSPOTACTIVEFORE:
8959 return vs.hotspotForeground.AsLong();
8961 case SCI_SETHOTSPOTACTIVEBACK:
8962 vs.hotspotBackgroundSet = wParam != 0;
8963 vs.hotspotBackground = ColourDesired(lParam);
8964 InvalidateStyleRedraw();
8965 break;
8967 case SCI_GETHOTSPOTACTIVEBACK:
8968 return vs.hotspotBackground.AsLong();
8970 case SCI_SETHOTSPOTACTIVEUNDERLINE:
8971 vs.hotspotUnderline = wParam != 0;
8972 InvalidateStyleRedraw();
8973 break;
8975 case SCI_GETHOTSPOTACTIVEUNDERLINE:
8976 return vs.hotspotUnderline ? 1 : 0;
8978 case SCI_SETHOTSPOTSINGLELINE:
8979 vs.hotspotSingleLine = wParam != 0;
8980 InvalidateStyleRedraw();
8981 break;
8983 case SCI_GETHOTSPOTSINGLELINE:
8984 return vs.hotspotSingleLine ? 1 : 0;
8986 case SCI_SETPASTECONVERTENDINGS:
8987 convertPastes = wParam != 0;
8988 break;
8990 case SCI_GETPASTECONVERTENDINGS:
8991 return convertPastes ? 1 : 0;
8993 case SCI_GETCHARACTERPOINTER:
8994 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
8996 case SCI_GETRANGEPOINTER:
8997 return reinterpret_cast<sptr_t>(pdoc->RangePointer(wParam, lParam));
8999 case SCI_GETGAPPOSITION:
9000 return pdoc->GapPosition();
9002 case SCI_SETEXTRAASCENT:
9003 vs.extraAscent = wParam;
9004 InvalidateStyleRedraw();
9005 break;
9007 case SCI_GETEXTRAASCENT:
9008 return vs.extraAscent;
9010 case SCI_SETEXTRADESCENT:
9011 vs.extraDescent = wParam;
9012 InvalidateStyleRedraw();
9013 break;
9015 case SCI_GETEXTRADESCENT:
9016 return vs.extraDescent;
9018 case SCI_MARGINSETSTYLEOFFSET:
9019 vs.marginStyleOffset = wParam;
9020 InvalidateStyleRedraw();
9021 break;
9023 case SCI_MARGINGETSTYLEOFFSET:
9024 return vs.marginStyleOffset;
9026 case SCI_SETMARGINOPTIONS:
9027 marginOptions = wParam;
9028 break;
9030 case SCI_GETMARGINOPTIONS:
9031 return marginOptions;
9033 case SCI_MARGINSETTEXT:
9034 pdoc->MarginSetText(wParam, CharPtrFromSPtr(lParam));
9035 break;
9037 case SCI_MARGINGETTEXT: {
9038 const StyledText st = pdoc->MarginStyledText(wParam);
9039 if (lParam) {
9040 if (st.text)
9041 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
9042 else
9043 strcpy(CharPtrFromSPtr(lParam), "");
9045 return st.length;
9048 case SCI_MARGINSETSTYLE:
9049 pdoc->MarginSetStyle(wParam, lParam);
9050 break;
9052 case SCI_MARGINGETSTYLE: {
9053 const StyledText st = pdoc->MarginStyledText(wParam);
9054 return st.style;
9057 case SCI_MARGINSETSTYLES:
9058 pdoc->MarginSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
9059 break;
9061 case SCI_MARGINGETSTYLES: {
9062 const StyledText st = pdoc->MarginStyledText(wParam);
9063 if (lParam) {
9064 if (st.styles)
9065 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
9066 else
9067 strcpy(CharPtrFromSPtr(lParam), "");
9069 return st.styles ? st.length : 0;
9072 case SCI_MARGINTEXTCLEARALL:
9073 pdoc->MarginClearAll();
9074 break;
9076 case SCI_ANNOTATIONSETTEXT:
9077 pdoc->AnnotationSetText(wParam, CharPtrFromSPtr(lParam));
9078 break;
9080 case SCI_ANNOTATIONGETTEXT: {
9081 const StyledText st = pdoc->AnnotationStyledText(wParam);
9082 if (lParam) {
9083 if (st.text)
9084 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
9085 else
9086 strcpy(CharPtrFromSPtr(lParam), "");
9088 return st.length;
9091 case SCI_ANNOTATIONGETSTYLE: {
9092 const StyledText st = pdoc->AnnotationStyledText(wParam);
9093 return st.style;
9096 case SCI_ANNOTATIONSETSTYLE:
9097 pdoc->AnnotationSetStyle(wParam, lParam);
9098 break;
9100 case SCI_ANNOTATIONSETSTYLES:
9101 pdoc->AnnotationSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
9102 break;
9104 case SCI_ANNOTATIONGETSTYLES: {
9105 const StyledText st = pdoc->AnnotationStyledText(wParam);
9106 if (lParam) {
9107 if (st.styles)
9108 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
9109 else
9110 strcpy(CharPtrFromSPtr(lParam), "");
9112 return st.styles ? st.length : 0;
9115 case SCI_ANNOTATIONGETLINES:
9116 return pdoc->AnnotationLines(wParam);
9118 case SCI_ANNOTATIONCLEARALL:
9119 pdoc->AnnotationClearAll();
9120 break;
9122 case SCI_ANNOTATIONSETVISIBLE:
9123 SetAnnotationVisible(wParam);
9124 break;
9126 case SCI_ANNOTATIONGETVISIBLE:
9127 return vs.annotationVisible;
9129 case SCI_ANNOTATIONSETSTYLEOFFSET:
9130 vs.annotationStyleOffset = wParam;
9131 InvalidateStyleRedraw();
9132 break;
9134 case SCI_ANNOTATIONGETSTYLEOFFSET:
9135 return vs.annotationStyleOffset;
9137 case SCI_ADDUNDOACTION:
9138 pdoc->AddUndoAction(wParam, lParam & UNDO_MAY_COALESCE);
9139 break;
9141 case SCI_SETMULTIPLESELECTION:
9142 multipleSelection = wParam != 0;
9143 InvalidateCaret();
9144 break;
9146 case SCI_GETMULTIPLESELECTION:
9147 return multipleSelection;
9149 case SCI_SETADDITIONALSELECTIONTYPING:
9150 additionalSelectionTyping = wParam != 0;
9151 InvalidateCaret();
9152 break;
9154 case SCI_GETADDITIONALSELECTIONTYPING:
9155 return additionalSelectionTyping;
9157 case SCI_SETMULTIPASTE:
9158 multiPasteMode = wParam;
9159 break;
9161 case SCI_GETMULTIPASTE:
9162 return multiPasteMode;
9164 case SCI_SETADDITIONALCARETSBLINK:
9165 additionalCaretsBlink = wParam != 0;
9166 InvalidateCaret();
9167 break;
9169 case SCI_GETADDITIONALCARETSBLINK:
9170 return additionalCaretsBlink;
9172 case SCI_SETADDITIONALCARETSVISIBLE:
9173 additionalCaretsVisible = wParam != 0;
9174 InvalidateCaret();
9175 break;
9177 case SCI_GETADDITIONALCARETSVISIBLE:
9178 return additionalCaretsVisible;
9180 case SCI_GETSELECTIONS:
9181 return sel.Count();
9183 case SCI_GETSELECTIONEMPTY:
9184 return sel.Empty();
9186 case SCI_CLEARSELECTIONS:
9187 sel.Clear();
9188 Redraw();
9189 break;
9191 case SCI_SETSELECTION:
9192 sel.SetSelection(SelectionRange(wParam, lParam));
9193 Redraw();
9194 break;
9196 case SCI_ADDSELECTION:
9197 sel.AddSelection(SelectionRange(wParam, lParam));
9198 Redraw();
9199 break;
9201 case SCI_SETMAINSELECTION:
9202 sel.SetMain(wParam);
9203 Redraw();
9204 break;
9206 case SCI_GETMAINSELECTION:
9207 return sel.Main();
9209 case SCI_SETSELECTIONNCARET:
9210 sel.Range(wParam).caret.SetPosition(lParam);
9211 Redraw();
9212 break;
9214 case SCI_GETSELECTIONNCARET:
9215 return sel.Range(wParam).caret.Position();
9217 case SCI_SETSELECTIONNANCHOR:
9218 sel.Range(wParam).anchor.SetPosition(lParam);
9219 Redraw();
9220 break;
9221 case SCI_GETSELECTIONNANCHOR:
9222 return sel.Range(wParam).anchor.Position();
9224 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
9225 sel.Range(wParam).caret.SetVirtualSpace(lParam);
9226 Redraw();
9227 break;
9229 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
9230 return sel.Range(wParam).caret.VirtualSpace();
9232 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
9233 sel.Range(wParam).anchor.SetVirtualSpace(lParam);
9234 Redraw();
9235 break;
9237 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
9238 return sel.Range(wParam).anchor.VirtualSpace();
9240 case SCI_SETSELECTIONNSTART:
9241 sel.Range(wParam).anchor.SetPosition(lParam);
9242 Redraw();
9243 break;
9245 case SCI_GETSELECTIONNSTART:
9246 return sel.Range(wParam).Start().Position();
9248 case SCI_SETSELECTIONNEND:
9249 sel.Range(wParam).caret.SetPosition(lParam);
9250 Redraw();
9251 break;
9253 case SCI_GETSELECTIONNEND:
9254 return sel.Range(wParam).End().Position();
9256 case SCI_SETRECTANGULARSELECTIONCARET:
9257 if (!sel.IsRectangular())
9258 sel.Clear();
9259 sel.selType = Selection::selRectangle;
9260 sel.Rectangular().caret.SetPosition(wParam);
9261 SetRectangularRange();
9262 Redraw();
9263 break;
9265 case SCI_GETRECTANGULARSELECTIONCARET:
9266 return sel.Rectangular().caret.Position();
9268 case SCI_SETRECTANGULARSELECTIONANCHOR:
9269 if (!sel.IsRectangular())
9270 sel.Clear();
9271 sel.selType = Selection::selRectangle;
9272 sel.Rectangular().anchor.SetPosition(wParam);
9273 SetRectangularRange();
9274 Redraw();
9275 break;
9277 case SCI_GETRECTANGULARSELECTIONANCHOR:
9278 return sel.Rectangular().anchor.Position();
9280 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9281 if (!sel.IsRectangular())
9282 sel.Clear();
9283 sel.selType = Selection::selRectangle;
9284 sel.Rectangular().caret.SetVirtualSpace(wParam);
9285 SetRectangularRange();
9286 Redraw();
9287 break;
9289 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9290 return sel.Rectangular().caret.VirtualSpace();
9292 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9293 if (!sel.IsRectangular())
9294 sel.Clear();
9295 sel.selType = Selection::selRectangle;
9296 sel.Rectangular().anchor.SetVirtualSpace(wParam);
9297 SetRectangularRange();
9298 Redraw();
9299 break;
9301 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9302 return sel.Rectangular().anchor.VirtualSpace();
9304 case SCI_SETVIRTUALSPACEOPTIONS:
9305 virtualSpaceOptions = wParam;
9306 break;
9308 case SCI_GETVIRTUALSPACEOPTIONS:
9309 return virtualSpaceOptions;
9311 case SCI_SETADDITIONALSELFORE:
9312 vs.selAdditionalForeground = ColourDesired(wParam);
9313 InvalidateStyleRedraw();
9314 break;
9316 case SCI_SETADDITIONALSELBACK:
9317 vs.selAdditionalBackground = ColourDesired(wParam);
9318 InvalidateStyleRedraw();
9319 break;
9321 case SCI_SETADDITIONALSELALPHA:
9322 vs.selAdditionalAlpha = wParam;
9323 InvalidateStyleRedraw();
9324 break;
9326 case SCI_GETADDITIONALSELALPHA:
9327 return vs.selAdditionalAlpha;
9329 case SCI_SETADDITIONALCARETFORE:
9330 vs.additionalCaretColour = ColourDesired(wParam);
9331 InvalidateStyleRedraw();
9332 break;
9334 case SCI_GETADDITIONALCARETFORE:
9335 return vs.additionalCaretColour.AsLong();
9337 case SCI_ROTATESELECTION:
9338 sel.RotateMain();
9339 InvalidateSelection(sel.RangeMain(), true);
9340 break;
9342 case SCI_SWAPMAINANCHORCARET:
9343 InvalidateSelection(sel.RangeMain());
9344 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
9345 break;
9347 case SCI_CHANGELEXERSTATE:
9348 pdoc->ChangeLexerState(wParam, lParam);
9349 break;
9351 case SCI_SETIDENTIFIER:
9352 SetCtrlID(wParam);
9353 break;
9355 case SCI_GETIDENTIFIER:
9356 return GetCtrlID();
9358 case SCI_SETTECHNOLOGY:
9359 // No action by default
9360 break;
9362 case SCI_GETTECHNOLOGY:
9363 return technology;
9365 case SCI_COUNTCHARACTERS:
9366 return pdoc->CountCharacters(wParam, lParam);
9368 default:
9369 return DefWndProc(iMessage, wParam, lParam);
9371 //Platform::DebugPrintf("end wnd proc\n");
9372 return 0l;