applied backgroundcolors.patch
[TortoiseGit.git] / ext / scintilla / src / Editor.cxx
blobc7f9d1b999c51a6b2415bade5cc3c447652aff84
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 "Selection.h"
40 #include "PositionCache.h"
41 #include "Editor.h"
43 #ifdef SCI_NAMESPACE
44 using namespace Scintilla;
45 #endif
48 return whether this modification represents an operation that
49 may reasonably be deferred (not done now OR [possibly] at all)
51 static bool CanDeferToLastStep(const DocModification &mh) {
52 if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
53 return true; // CAN skip
54 if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)))
55 return false; // MUST do
56 if (mh.modificationType & SC_MULTISTEPUNDOREDO)
57 return true; // CAN skip
58 return false; // PRESUMABLY must do
61 static bool CanEliminate(const DocModification &mh) {
62 return
63 (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
67 return whether this modification represents the FINAL step
68 in a [possibly lengthy] multi-step Undo/Redo sequence
70 static bool IsLastStep(const DocModification &mh) {
71 return
72 (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0
73 && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
74 && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
75 && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
78 Caret::Caret() :
79 active(false), on(false), period(500) {}
81 Timer::Timer() :
82 ticking(false), ticksToWait(0), tickerID(0) {}
84 Idler::Idler() :
85 state(false), idlerID(0) {}
87 static inline bool IsControlCharacter(int ch) {
88 // iscntrl returns true for lots of chars > 127 which are displayable
89 return ch >= 0 && ch < ' ';
92 static inline bool IsAllSpacesOrTabs(char *s, unsigned int len) {
93 for (unsigned int i = 0; i < len; i++) {
94 // This is safe because IsSpaceOrTab() will return false for null terminators
95 if (!IsSpaceOrTab(s[i]))
96 return false;
98 return true;
101 Editor::Editor() {
102 ctrlID = 0;
104 stylesValid = false;
106 printMagnification = 0;
107 printColourMode = SC_PRINT_NORMAL;
108 printWrapState = eWrapWord;
109 cursorMode = SC_CURSORNORMAL;
110 controlCharSymbol = 0; /* Draw the control characters */
112 hasFocus = false;
113 hideSelection = false;
114 inOverstrike = false;
115 errorStatus = 0;
116 mouseDownCaptures = true;
118 bufferedDraw = true;
119 twoPhaseDraw = true;
121 lastClickTime = 0;
122 dwellDelay = SC_TIME_FOREVER;
123 ticksToDwell = SC_TIME_FOREVER;
124 dwelling = false;
125 ptMouseLast.x = 0;
126 ptMouseLast.y = 0;
127 inDragDrop = ddNone;
128 dropWentOutside = false;
129 posDrag = SelectionPosition(invalidPosition);
130 posDrop = SelectionPosition(invalidPosition);
131 hotSpotClickPos = INVALID_POSITION;
132 selectionType = selChar;
134 lastXChosen = 0;
135 lineAnchorPos = 0;
136 originalAnchorPos = 0;
137 wordSelectAnchorStartPos = 0;
138 wordSelectAnchorEndPos = 0;
139 wordSelectInitialCaretPos = -1;
141 primarySelection = true;
143 caretXPolicy = CARET_SLOP | CARET_EVEN;
144 caretXSlop = 50;
146 caretYPolicy = CARET_EVEN;
147 caretYSlop = 0;
149 visiblePolicy = 0;
150 visibleSlop = 0;
152 searchAnchor = 0;
154 xOffset = 0;
155 xCaretMargin = 50;
156 horizontalScrollBarVisible = true;
157 scrollWidth = 2000;
158 trackLineWidth = false;
159 lineWidthMaxSeen = 0;
160 verticalScrollBarVisible = true;
161 endAtLastLine = true;
162 caretSticky = SC_CARETSTICKY_OFF;
163 marginOptions = SC_MARGINOPTION_NONE;
164 multipleSelection = false;
165 additionalSelectionTyping = false;
166 multiPasteMode = SC_MULTIPASTE_ONCE;
167 additionalCaretsBlink = true;
168 additionalCaretsVisible = true;
169 virtualSpaceOptions = SCVS_NONE;
171 pixmapLine = Surface::Allocate();
172 pixmapSelMargin = Surface::Allocate();
173 pixmapSelPattern = Surface::Allocate();
174 pixmapIndentGuide = Surface::Allocate();
175 pixmapIndentGuideHighlight = Surface::Allocate();
177 targetStart = 0;
178 targetEnd = 0;
179 searchFlags = 0;
181 topLine = 0;
182 posTopLine = 0;
184 lengthForEncode = -1;
186 needUpdateUI = 0;
187 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
188 braces[0] = invalidPosition;
189 braces[1] = invalidPosition;
190 bracesMatchStyle = STYLE_BRACEBAD;
191 highlightGuideColumn = 0;
193 theEdge = 0;
195 paintState = notPainting;
197 modEventMask = SC_MODEVENTMASKALL;
199 pdoc = new Document();
200 pdoc->AddRef();
201 pdoc->AddWatcher(this, 0);
203 recordingMacro = false;
204 foldFlags = 0;
206 wrapState = eWrapNone;
207 wrapWidth = LineLayout::wrapWidthInfinite;
208 wrapStart = wrapLineLarge;
209 wrapEnd = wrapLineLarge;
210 wrapVisualFlags = 0;
211 wrapVisualFlagsLocation = 0;
212 wrapVisualStartIndent = 0;
213 wrapIndentMode = SC_WRAPINDENT_FIXED;
214 wrapAddIndent = 0;
216 convertPastes = true;
218 hsStart = -1;
219 hsEnd = -1;
221 llc.SetLevel(LineLayoutCache::llcCaret);
222 posCache.SetSize(0x400);
225 Editor::~Editor() {
226 pdoc->RemoveWatcher(this, 0);
227 pdoc->Release();
228 pdoc = 0;
229 DropGraphics();
230 delete pixmapLine;
231 delete pixmapSelMargin;
232 delete pixmapSelPattern;
233 delete pixmapIndentGuide;
234 delete pixmapIndentGuideHighlight;
237 void Editor::Finalise() {
238 SetIdle(false);
239 CancelModes();
242 void Editor::DropGraphics() {
243 pixmapLine->Release();
244 pixmapSelMargin->Release();
245 pixmapSelPattern->Release();
246 pixmapIndentGuide->Release();
247 pixmapIndentGuideHighlight->Release();
250 void Editor::InvalidateStyleData() {
251 stylesValid = false;
252 DropGraphics();
253 palette.Release();
254 llc.Invalidate(LineLayout::llInvalid);
255 posCache.Clear();
258 void Editor::InvalidateStyleRedraw() {
259 NeedWrapping();
260 InvalidateStyleData();
261 Redraw();
264 void Editor::RefreshColourPalette(Palette &pal, bool want) {
265 vs.RefreshColourPalette(pal, want);
268 void Editor::RefreshStyleData() {
269 if (!stylesValid) {
270 stylesValid = true;
271 AutoSurface surface(this);
272 if (surface) {
273 vs.Refresh(*surface);
274 RefreshColourPalette(palette, true);
275 palette.Allocate(wMain);
276 RefreshColourPalette(palette, false);
278 if (wrapIndentMode == SC_WRAPINDENT_INDENT) {
279 wrapAddIndent = pdoc->IndentSize() * vs.spaceWidth;
280 } else if (wrapIndentMode == SC_WRAPINDENT_SAME) {
281 wrapAddIndent = 0;
282 } else { //SC_WRAPINDENT_FIXED
283 wrapAddIndent = wrapVisualStartIndent * vs.aveCharWidth;
284 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (wrapAddIndent <= 0))
285 wrapAddIndent = vs.aveCharWidth; // must indent to show start visual
287 SetScrollBars();
288 SetRectangularRange();
292 PRectangle Editor::GetClientRectangle() {
293 return wMain.GetClientPosition();
296 PRectangle Editor::GetTextRectangle() {
297 PRectangle rc = GetClientRectangle();
298 rc.left += vs.fixedColumnWidth;
299 rc.right -= vs.rightMarginWidth;
300 return rc;
303 int Editor::LinesOnScreen() {
304 PRectangle rcClient = GetClientRectangle();
305 int htClient = rcClient.bottom - rcClient.top;
306 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
307 return htClient / vs.lineHeight;
310 int Editor::LinesToScroll() {
311 int retVal = LinesOnScreen() - 1;
312 if (retVal < 1)
313 return 1;
314 else
315 return retVal;
318 int Editor::MaxScrollPos() {
319 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
320 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
321 int retVal = cs.LinesDisplayed();
322 if (endAtLastLine) {
323 retVal -= LinesOnScreen();
324 } else {
325 retVal--;
327 if (retVal < 0) {
328 return 0;
329 } else {
330 return retVal;
334 const char *ControlCharacterString(unsigned char ch) {
335 const char *reps[] = {
336 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
337 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
338 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
339 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
341 if (ch < (sizeof(reps) / sizeof(reps[0]))) {
342 return reps[ch];
343 } else {
344 return "BAD";
349 * Convenience class to ensure LineLayout objects are always disposed.
351 class AutoLineLayout {
352 LineLayoutCache &llc;
353 LineLayout *ll;
354 AutoLineLayout &operator=(const AutoLineLayout &);
355 public:
356 AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
357 ~AutoLineLayout() {
358 llc.Dispose(ll);
359 ll = 0;
361 LineLayout *operator->() const {
362 return ll;
364 operator LineLayout *() const {
365 return ll;
367 void Set(LineLayout *ll_) {
368 llc.Dispose(ll);
369 ll = ll_;
373 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
374 if (sp.Position() < 0) {
375 return SelectionPosition(0);
376 } else if (sp.Position() > pdoc->Length()) {
377 return SelectionPosition(pdoc->Length());
378 } else {
379 // If not at end of line then set offset to 0
380 if (!pdoc->IsLineEndPosition(sp.Position()))
381 sp.SetVirtualSpace(0);
382 return sp;
386 Point Editor::LocationFromPosition(SelectionPosition pos) {
387 Point pt;
388 RefreshStyleData();
389 if (pos.Position() == INVALID_POSITION)
390 return pt;
391 int line = pdoc->LineFromPosition(pos.Position());
392 int lineVisible = cs.DisplayFromDoc(line);
393 //Platform::DebugPrintf("line=%d\n", line);
394 AutoSurface surface(this);
395 AutoLineLayout ll(llc, RetrieveLineLayout(line));
396 if (surface && ll) {
397 // -1 because of adding in for visible lines in following loop.
398 pt.y = (lineVisible - topLine - 1) * vs.lineHeight;
399 pt.x = 0;
400 unsigned int posLineStart = pdoc->LineStart(line);
401 LayoutLine(line, surface, vs, ll, wrapWidth);
402 int posInLine = pos.Position() - posLineStart;
403 // In case of very long line put x at arbitrary large position
404 if (posInLine > ll->maxLineLength) {
405 pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->LineStart(ll->lines)];
408 for (int subLine = 0; subLine < ll->lines; subLine++) {
409 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
410 pt.x = ll->positions[posInLine] - ll->positions[ll->LineStart(subLine)];
411 if (ll->wrapIndent != 0) {
412 int lineStart = ll->LineStart(subLine);
413 if (lineStart != 0) // Wrapped
414 pt.x += ll->wrapIndent;
417 if (posInLine >= ll->LineStart(subLine)) {
418 pt.y += vs.lineHeight;
421 pt.x += vs.fixedColumnWidth - xOffset;
423 pt.x += pos.VirtualSpace() * static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
424 return pt;
427 Point Editor::LocationFromPosition(int pos) {
428 return LocationFromPosition(SelectionPosition(pos));
431 int Editor::XFromPosition(int pos) {
432 Point pt = LocationFromPosition(pos);
433 return pt.x - vs.fixedColumnWidth + xOffset;
436 int Editor::XFromPosition(SelectionPosition sp) {
437 Point pt = LocationFromPosition(sp);
438 return pt.x - vs.fixedColumnWidth + xOffset;
441 int Editor::LineFromLocation(Point pt) {
442 return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
445 void Editor::SetTopLine(int topLineNew) {
446 if (topLine != topLineNew) {
447 topLine = topLineNew;
448 ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
450 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
453 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
454 RefreshStyleData();
455 if (canReturnInvalid) {
456 PRectangle rcClient = GetTextRectangle();
457 if (!rcClient.Contains(pt))
458 return SelectionPosition(INVALID_POSITION);
459 if (pt.x < vs.fixedColumnWidth)
460 return SelectionPosition(INVALID_POSITION);
461 if (pt.y < 0)
462 return SelectionPosition(INVALID_POSITION);
464 pt.x = pt.x - vs.fixedColumnWidth + xOffset;
465 int visibleLine = pt.y / vs.lineHeight + topLine;
466 if (pt.y < 0) { // Division rounds towards 0
467 visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine;
469 if (!canReturnInvalid && (visibleLine < 0))
470 visibleLine = 0;
471 int lineDoc = cs.DocFromDisplay(visibleLine);
472 if (canReturnInvalid && (lineDoc < 0))
473 return SelectionPosition(INVALID_POSITION);
474 if (lineDoc >= pdoc->LinesTotal())
475 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length());
476 unsigned int posLineStart = pdoc->LineStart(lineDoc);
477 SelectionPosition retVal(canReturnInvalid ? INVALID_POSITION : static_cast<int>(posLineStart));
478 AutoSurface surface(this);
479 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
480 if (surface && ll) {
481 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
482 int lineStartSet = cs.DisplayFromDoc(lineDoc);
483 int subLine = visibleLine - lineStartSet;
484 if (subLine < ll->lines) {
485 int lineStart = ll->LineStart(subLine);
486 int lineEnd = ll->LineLastVisible(subLine);
487 int subLineStart = ll->positions[lineStart];
489 if (ll->wrapIndent != 0) {
490 if (lineStart != 0) // Wrapped
491 pt.x -= ll->wrapIndent;
493 int i = ll->FindBefore(pt.x + subLineStart, lineStart, lineEnd);
494 while (i < lineEnd) {
495 if (charPosition) {
496 if ((pt.x + subLineStart) < (ll->positions[i + 1])) {
497 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
499 } else {
500 if ((pt.x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
501 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
504 i++;
506 if (virtualSpace) {
507 const int spaceWidth = static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
508 int spaceOffset = (pt.x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) /
509 spaceWidth;
510 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
511 } else if (canReturnInvalid) {
512 if (pt.x < (ll->positions[lineEnd] - subLineStart)) {
513 return SelectionPosition(pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1));
515 } else {
516 return SelectionPosition(lineEnd + posLineStart);
519 if (!canReturnInvalid)
520 return SelectionPosition(ll->numCharsInLine + posLineStart);
522 return retVal;
525 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
526 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
530 * Find the document position corresponding to an x coordinate on a particular document line.
531 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
533 int Editor::PositionFromLineX(int lineDoc, int x) {
534 RefreshStyleData();
535 if (lineDoc >= pdoc->LinesTotal())
536 return pdoc->Length();
537 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
538 AutoSurface surface(this);
539 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
540 int retVal = 0;
541 if (surface && ll) {
542 unsigned int posLineStart = pdoc->LineStart(lineDoc);
543 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
544 retVal = ll->numCharsBeforeEOL + posLineStart;
545 int subLine = 0;
546 int lineStart = ll->LineStart(subLine);
547 int lineEnd = ll->LineLastVisible(subLine);
548 int subLineStart = ll->positions[lineStart];
550 if (ll->wrapIndent != 0) {
551 if (lineStart != 0) // Wrapped
552 x -= ll->wrapIndent;
554 int i = ll->FindBefore(x + subLineStart, lineStart, lineEnd);
555 while (i < lineEnd) {
556 if ((x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
557 retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
558 break;
560 i++;
563 return retVal;
567 * Find the document position corresponding to an x coordinate on a particular document line.
568 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
570 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
571 RefreshStyleData();
572 if (lineDoc >= pdoc->LinesTotal())
573 return SelectionPosition(pdoc->Length());
574 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
575 AutoSurface surface(this);
576 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
577 int retVal = 0;
578 if (surface && ll) {
579 unsigned int posLineStart = pdoc->LineStart(lineDoc);
580 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
581 int subLine = 0;
582 int lineStart = ll->LineStart(subLine);
583 int lineEnd = ll->LineLastVisible(subLine);
584 int subLineStart = ll->positions[lineStart];
586 if (ll->wrapIndent != 0) {
587 if (lineStart != 0) // Wrapped
588 x -= ll->wrapIndent;
590 int i = ll->FindBefore(x + subLineStart, lineStart, lineEnd);
591 while (i < lineEnd) {
592 if ((x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
593 retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
594 return SelectionPosition(retVal);
596 i++;
598 const int spaceWidth = static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
599 int spaceOffset = (x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) / spaceWidth;
600 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
602 return SelectionPosition(retVal);
606 * If painting then abandon the painting because a wider redraw is needed.
607 * @return true if calling code should stop drawing.
609 bool Editor::AbandonPaint() {
610 if ((paintState == painting) && !paintingAllText) {
611 paintState = paintAbandoned;
613 return paintState == paintAbandoned;
616 void Editor::RedrawRect(PRectangle rc) {
617 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
619 // Clip the redraw rectangle into the client area
620 PRectangle rcClient = GetClientRectangle();
621 if (rc.top < rcClient.top)
622 rc.top = rcClient.top;
623 if (rc.bottom > rcClient.bottom)
624 rc.bottom = rcClient.bottom;
625 if (rc.left < rcClient.left)
626 rc.left = rcClient.left;
627 if (rc.right > rcClient.right)
628 rc.right = rcClient.right;
630 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
631 wMain.InvalidateRectangle(rc);
635 void Editor::Redraw() {
636 //Platform::DebugPrintf("Redraw all\n");
637 PRectangle rcClient = GetClientRectangle();
638 wMain.InvalidateRectangle(rcClient);
639 //wMain.InvalidateAll();
642 void Editor::RedrawSelMargin(int line, bool allAfter) {
643 if (!AbandonPaint()) {
644 if (vs.maskInLine) {
645 Redraw();
646 } else {
647 PRectangle rcSelMargin = GetClientRectangle();
648 rcSelMargin.right = vs.fixedColumnWidth;
649 if (line != -1) {
650 int position = pdoc->LineStart(line);
651 PRectangle rcLine = RectangleFromRange(position, position);
652 rcSelMargin.top = rcLine.top;
653 if (!allAfter)
654 rcSelMargin.bottom = rcLine.bottom;
656 wMain.InvalidateRectangle(rcSelMargin);
661 PRectangle Editor::RectangleFromRange(int start, int end) {
662 int minPos = start;
663 if (minPos > end)
664 minPos = end;
665 int maxPos = start;
666 if (maxPos < end)
667 maxPos = end;
668 int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos));
669 int lineDocMax = pdoc->LineFromPosition(maxPos);
670 int maxLine = cs.DisplayFromDoc(lineDocMax) + cs.GetHeight(lineDocMax) - 1;
671 PRectangle rcClient = GetTextRectangle();
672 PRectangle rc;
673 rc.left = vs.fixedColumnWidth;
674 rc.top = (minLine - topLine) * vs.lineHeight;
675 if (rc.top < 0)
676 rc.top = 0;
677 rc.right = rcClient.right;
678 rc.bottom = (maxLine - topLine + 1) * vs.lineHeight;
679 // Ensure PRectangle is within 16 bit space
680 rc.top = Platform::Clamp(rc.top, -32000, 32000);
681 rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000);
683 return rc;
686 void Editor::InvalidateRange(int start, int end) {
687 RedrawRect(RectangleFromRange(start, end));
690 int Editor::CurrentPosition() {
691 return sel.MainCaret();
694 bool Editor::SelectionEmpty() {
695 return sel.Empty();
698 SelectionPosition Editor::SelectionStart() {
699 return sel.RangeMain().Start();
702 SelectionPosition Editor::SelectionEnd() {
703 return sel.RangeMain().End();
706 void Editor::SetRectangularRange() {
707 if (sel.IsRectangular()) {
708 int xAnchor = XFromPosition(sel.Rectangular().anchor);
709 int xCaret = XFromPosition(sel.Rectangular().caret);
710 if (sel.selType == Selection::selThin) {
711 xCaret = xAnchor;
713 int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
714 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
715 int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
716 for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
717 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
718 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
719 range.ClearVirtualSpace();
720 if (line == lineAnchorRect)
721 sel.SetSelection(range);
722 else
723 sel.AddSelectionWithoutTrim(range);
728 void Editor::ThinRectangularRange() {
729 if (sel.IsRectangular()) {
730 sel.selType = Selection::selThin;
731 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
732 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
733 } else {
734 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
736 SetRectangularRange();
740 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
741 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
742 invalidateWholeSelection = true;
744 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
745 // +1 for lastAffected ensures caret repainted
746 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
747 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
748 if (invalidateWholeSelection) {
749 for (size_t r=0; r<sel.Count(); r++) {
750 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
751 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
752 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
753 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
756 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
757 InvalidateRange(firstAffected, lastAffected);
760 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
761 currentPos_ = ClampPositionIntoDocument(currentPos_);
762 anchor_ = ClampPositionIntoDocument(anchor_);
763 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
764 /* For Line selection - ensure the anchor and caret are always
765 at the beginning and end of the region lines. */
766 if (sel.selType == Selection::selLines) {
767 if (currentPos_ > anchor_) {
768 anchor_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
769 currentPos_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
770 } else {
771 currentPos_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
772 anchor_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
775 SelectionRange rangeNew(currentPos_, anchor_);
776 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
777 InvalidateSelection(rangeNew);
779 sel.RangeMain() = rangeNew;
780 SetRectangularRange();
781 ClaimSelection();
783 if (highlightDelimiter.NeedsDrawing(currentLine)) {
784 RedrawSelMargin();
788 void Editor::SetSelection(int currentPos_, int anchor_) {
789 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
792 // Just move the caret on the main selection
793 void Editor::SetSelection(SelectionPosition currentPos_) {
794 currentPos_ = ClampPositionIntoDocument(currentPos_);
795 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
796 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
797 InvalidateSelection(SelectionRange(currentPos_));
799 if (sel.IsRectangular()) {
800 sel.Rectangular() =
801 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
802 SetRectangularRange();
803 } else {
804 sel.RangeMain() =
805 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
807 ClaimSelection();
809 if (highlightDelimiter.NeedsDrawing(currentLine)) {
810 RedrawSelMargin();
814 void Editor::SetSelection(int currentPos_) {
815 SetSelection(SelectionPosition(currentPos_));
818 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
819 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
820 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
821 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
822 InvalidateSelection(rangeNew);
824 sel.Clear();
825 sel.RangeMain() = rangeNew;
826 SetRectangularRange();
827 ClaimSelection();
829 if (highlightDelimiter.NeedsDrawing(currentLine)) {
830 RedrawSelMargin();
834 void Editor::SetEmptySelection(int currentPos_) {
835 SetEmptySelection(SelectionPosition(currentPos_));
838 bool Editor::RangeContainsProtected(int start, int end) const {
839 if (vs.ProtectionActive()) {
840 if (start > end) {
841 int t = start;
842 start = end;
843 end = t;
845 int mask = pdoc->stylingBitsMask;
846 for (int pos = start; pos < end; pos++) {
847 if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())
848 return true;
851 return false;
854 bool Editor::SelectionContainsProtected() {
855 for (size_t r=0; r<sel.Count(); r++) {
856 if (RangeContainsProtected(sel.Range(r).Start().Position(),
857 sel.Range(r).End().Position())) {
858 return true;
861 return false;
865 * Asks document to find a good position and then moves out of any invisible positions.
867 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
868 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
871 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
872 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
873 if (posMoved != pos.Position())
874 pos.SetPosition(posMoved);
875 if (vs.ProtectionActive()) {
876 int mask = pdoc->stylingBitsMask;
877 if (moveDir > 0) {
878 if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()) {
879 while ((pos.Position() < pdoc->Length()) &&
880 (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()))
881 pos.Add(1);
883 } else if (moveDir < 0) {
884 if (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()) {
885 while ((pos.Position() > 0) &&
886 (vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()))
887 pos.Add(-1);
891 return pos;
894 int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
895 bool simpleCaret = (sel.Count() == 1) && sel.Empty();
896 SelectionPosition spCaret = sel.Last();
898 int delta = newPos.Position() - sel.MainCaret();
899 newPos = ClampPositionIntoDocument(newPos);
900 newPos = MovePositionOutsideChar(newPos, delta);
901 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
902 // Can't turn into multiple selection so clear additional selections
903 InvalidateSelection(SelectionRange(newPos), true);
904 SelectionRange rangeMain = sel.RangeMain();
905 sel.SetSelection(rangeMain);
907 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
908 // Switching to rectangular
909 SelectionRange rangeMain = sel.RangeMain();
910 sel.Clear();
911 sel.Rectangular() = rangeMain;
913 if (selt != Selection::noSel) {
914 sel.selType = selt;
916 if (selt != Selection::noSel || sel.MoveExtends()) {
917 SetSelection(newPos);
918 } else {
919 SetEmptySelection(newPos);
921 ShowCaretAtCurrentPosition();
922 if (ensureVisible) {
923 XYScrollPosition newXY = XYScrollToMakeVisible(true, true, true);
924 if (simpleCaret && (newXY.xOffset == xOffset)) {
925 // simple vertical scroll then invalidate
926 ScrollTo(newXY.topLine);
927 InvalidateSelection(SelectionRange(spCaret), true);
928 } else {
929 SetXYScroll(newXY);
933 int currentLine = pdoc->LineFromPosition(newPos.Position());
935 if (highlightDelimiter.NeedsDrawing(currentLine)) {
936 RedrawSelMargin();
938 return 0;
941 int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
942 return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
945 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
946 pos = ClampPositionIntoDocument(pos);
947 pos = MovePositionOutsideChar(pos, moveDir);
948 int lineDoc = pdoc->LineFromPosition(pos.Position());
949 if (cs.GetVisible(lineDoc)) {
950 return pos;
951 } else {
952 int lineDisplay = cs.DisplayFromDoc(lineDoc);
953 if (moveDir > 0) {
954 // lineDisplay is already line before fold as lines in fold use display line of line after fold
955 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
956 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
957 } else {
958 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
959 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
964 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
965 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
968 Point Editor::PointMainCaret() {
969 return LocationFromPosition(sel.Range(sel.Main()).caret);
973 * Choose the x position that the caret will try to stick to
974 * as it moves up and down.
976 void Editor::SetLastXChosen() {
977 Point pt = PointMainCaret();
978 lastXChosen = pt.x + xOffset;
981 void Editor::ScrollTo(int line, bool moveThumb) {
982 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
983 if (topLineNew != topLine) {
984 // Try to optimise small scrolls
985 #ifndef UNDER_CE
986 int linesToMove = topLine - topLineNew;
987 #endif
988 SetTopLine(topLineNew);
989 // Optimize by styling the view as this will invalidate any needed area
990 // which could abort the initial paint if discovered later.
991 StyleToPositionInView(PositionAfterArea(GetClientRectangle()));
992 #ifndef UNDER_CE
993 // Perform redraw rather than scroll if many lines would be redrawn anyway.
994 if ((abs(linesToMove) <= 10) && (paintState == notPainting)) {
995 ScrollText(linesToMove);
996 } else {
997 Redraw();
999 #else
1000 Redraw();
1001 #endif
1002 if (moveThumb) {
1003 SetVerticalScrollPos();
1008 void Editor::ScrollText(int /* linesToMove */) {
1009 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
1010 Redraw();
1013 void Editor::HorizontalScrollTo(int xPos) {
1014 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
1015 if (xPos < 0)
1016 xPos = 0;
1017 if ((wrapState == eWrapNone) && (xOffset != xPos)) {
1018 xOffset = xPos;
1019 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1020 SetHorizontalScrollPos();
1021 RedrawRect(GetClientRectangle());
1025 void Editor::VerticalCentreCaret() {
1026 int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
1027 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1028 int newTop = lineDisplay - (LinesOnScreen() / 2);
1029 if (topLine != newTop) {
1030 SetTopLine(newTop > 0 ? newTop : 0);
1031 RedrawRect(GetClientRectangle());
1035 void Editor::MoveSelectedLines(int lineDelta) {
1037 // if selection doesn't start at the beginning of the line, set the new start
1038 int selectionStart = SelectionStart().Position();
1039 int startLine = pdoc->LineFromPosition(selectionStart);
1040 int beginningOfStartLine = pdoc->LineStart(startLine);
1041 selectionStart = beginningOfStartLine;
1043 // if selection doesn't end at the beginning of a line greater than that of the start,
1044 // then set it at the beginning of the next one
1045 int selectionEnd = SelectionEnd().Position();
1046 int endLine = pdoc->LineFromPosition(selectionEnd);
1047 int beginningOfEndLine = pdoc->LineStart(endLine);
1048 if (selectionEnd > beginningOfEndLine
1049 || selectionStart == selectionEnd) {
1050 selectionEnd = pdoc->LineStart(endLine + 1);
1053 // if there's nowhere for the selection to move
1054 // (i.e. at the beginning going up or at the end going down),
1055 // stop it right there!
1056 if ((selectionStart == 0 && lineDelta < 0)
1057 || (selectionEnd == pdoc->Length() && lineDelta > 0)
1058 || selectionStart == selectionEnd) {
1059 return;
1062 UndoGroup ug(pdoc);
1064 SetSelection(selectionStart, selectionEnd);
1066 SelectionText selectedText;
1067 CopySelectionRange(&selectedText);
1069 int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
1070 ClearSelection();
1072 Point currentLocation = LocationFromPosition(CurrentPosition());
1073 int currentLine = LineFromLocation(currentLocation);
1074 GoToLine(currentLine + lineDelta);
1076 pdoc->InsertCString(CurrentPosition(), selectedText.s);
1077 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1080 void Editor::MoveSelectedLinesUp() {
1081 MoveSelectedLines(-1);
1084 void Editor::MoveSelectedLinesDown() {
1085 MoveSelectedLines(1);
1088 void Editor::MoveCaretInsideView(bool ensureVisible) {
1089 PRectangle rcClient = GetTextRectangle();
1090 Point pt = PointMainCaret();
1091 if (pt.y < rcClient.top) {
1092 MovePositionTo(SPositionFromLocation(
1093 Point(lastXChosen - xOffset, rcClient.top)),
1094 Selection::noSel, ensureVisible);
1095 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1096 int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
1097 MovePositionTo(SPositionFromLocation(
1098 Point(lastXChosen - xOffset, rcClient.top + yOfLastLineFullyDisplayed)),
1099 Selection::noSel, ensureVisible);
1103 int Editor::DisplayFromPosition(int pos) {
1104 int lineDoc = pdoc->LineFromPosition(pos);
1105 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1106 AutoSurface surface(this);
1107 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
1108 if (surface && ll) {
1109 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
1110 unsigned int posLineStart = pdoc->LineStart(lineDoc);
1111 int posInLine = pos - posLineStart;
1112 lineDisplay--; // To make up for first increment ahead.
1113 for (int subLine = 0; subLine < ll->lines; subLine++) {
1114 if (posInLine >= ll->LineStart(subLine)) {
1115 lineDisplay++;
1119 return lineDisplay;
1123 * Ensure the caret is reasonably visible in context.
1125 Caret policy in SciTE
1127 If slop is set, we can define a slop value.
1128 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1129 This zone is defined as a number of pixels near the vertical margins,
1130 and as a number of lines near the horizontal margins.
1131 By keeping the caret away from the edges, it is seen within its context,
1132 so it is likely that the identifier that the caret is on can be completely seen,
1133 and that the current line is seen with some of the lines following it which are
1134 often dependent on that line.
1136 If strict is set, the policy is enforced... strictly.
1137 The caret is centred on the display if slop is not set,
1138 and cannot go in the UZ if slop is set.
1140 If jumps is set, the display is moved more energetically
1141 so the caret can move in the same direction longer before the policy is applied again.
1142 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1144 If even is not set, instead of having symmetrical UZs,
1145 the left and bottom UZs are extended up to right and top UZs respectively.
1146 This way, we favour the displaying of useful information: the begining of lines,
1147 where most code reside, and the lines after the caret, eg. the body of a function.
1149 | | | | |
1150 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1151 | | | | | visibility or going into the UZ) display is...
1152 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1153 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1154 0 | 0 | 0 | 1 | Yes | moved by one position
1155 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1156 0 | 0 | 1 | 1 | Yes | centred on the caret
1157 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1158 0 | 1 | - | 1 | No, caret is always centred | -
1159 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1160 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1161 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1162 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1163 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1164 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1165 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1168 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const bool useMargin, const bool vert, const bool horiz) {
1169 PRectangle rcClient = GetTextRectangle();
1170 const SelectionPosition posCaret = posDrag.IsValid() ? posDrag : sel.RangeMain().caret;
1171 const Point pt = LocationFromPosition(posCaret);
1172 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1173 const int lineCaret = DisplayFromPosition(posCaret.Position());
1175 XYScrollPosition newXY(xOffset, topLine);
1177 // Vertical positioning
1178 if (vert && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1179 const int linesOnScreen = LinesOnScreen();
1180 const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1181 const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
1182 const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
1183 const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
1184 const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
1186 // It should be possible to scroll the window to show the caret,
1187 // but this fails to remove the caret on GTK+
1188 if (bSlop) { // A margin is defined
1189 int yMoveT, yMoveB;
1190 if (bStrict) {
1191 int yMarginT, yMarginB;
1192 if (!useMargin) {
1193 // In drag mode, avoid moves
1194 // otherwise, a double click will select several lines.
1195 yMarginT = yMarginB = 0;
1196 } else {
1197 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1198 // a maximum of slightly less than half the heigth of the text area.
1199 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1200 if (bEven) {
1201 yMarginB = yMarginT;
1202 } else {
1203 yMarginB = linesOnScreen - yMarginT - 1;
1206 yMoveT = yMarginT;
1207 if (bEven) {
1208 if (bJump) {
1209 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1211 yMoveB = yMoveT;
1212 } else {
1213 yMoveB = linesOnScreen - yMoveT - 1;
1215 if (lineCaret < topLine + yMarginT) {
1216 // Caret goes too high
1217 newXY.topLine = lineCaret - yMoveT;
1218 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1219 // Caret goes too low
1220 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1222 } else { // Not strict
1223 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1224 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1225 if (bEven) {
1226 yMoveB = yMoveT;
1227 } else {
1228 yMoveB = linesOnScreen - yMoveT - 1;
1230 if (lineCaret < topLine) {
1231 // Caret goes too high
1232 newXY.topLine = lineCaret - yMoveT;
1233 } else if (lineCaret > topLine + linesOnScreen - 1) {
1234 // Caret goes too low
1235 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1238 } else { // No slop
1239 if (!bStrict && !bJump) {
1240 // Minimal move
1241 if (lineCaret < topLine) {
1242 // Caret goes too high
1243 newXY.topLine = lineCaret;
1244 } else if (lineCaret > topLine + linesOnScreen - 1) {
1245 // Caret goes too low
1246 if (bEven) {
1247 newXY.topLine = lineCaret - linesOnScreen + 1;
1248 } else {
1249 newXY.topLine = lineCaret;
1252 } else { // Strict or going out of display
1253 if (bEven) {
1254 // Always center caret
1255 newXY.topLine = lineCaret - halfScreen;
1256 } else {
1257 // Always put caret on top of display
1258 newXY.topLine = lineCaret;
1262 newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
1265 // Horizontal positioning
1266 if (horiz && (wrapState == eWrapNone)) {
1267 const int halfScreen = Platform::Maximum(rcClient.Width() - 4, 4) / 2;
1268 const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
1269 const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
1270 const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
1271 const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
1273 if (bSlop) { // A margin is defined
1274 int xMoveL, xMoveR;
1275 if (bStrict) {
1276 int xMarginL, xMarginR;
1277 if (!useMargin) {
1278 // In drag mode, avoid moves unless very near of the margin
1279 // otherwise, a simple click will select text.
1280 xMarginL = xMarginR = 2;
1281 } else {
1282 // xMargin must equal to caretXSlop, with a minimum of 2 and
1283 // a maximum of slightly less than half the width of the text area.
1284 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1285 if (bEven) {
1286 xMarginL = xMarginR;
1287 } else {
1288 xMarginL = rcClient.Width() - xMarginR - 4;
1291 if (bJump && bEven) {
1292 // Jump is used only in even mode
1293 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1294 } else {
1295 xMoveL = xMoveR = 0; // Not used, avoid a warning
1297 if (pt.x < rcClient.left + xMarginL) {
1298 // Caret is on the left of the display
1299 if (bJump && bEven) {
1300 newXY.xOffset -= xMoveL;
1301 } else {
1302 // Move just enough to allow to display the caret
1303 newXY.xOffset -= (rcClient.left + xMarginL) - pt.x;
1305 } else if (pt.x >= rcClient.right - xMarginR) {
1306 // Caret is on the right of the display
1307 if (bJump && bEven) {
1308 newXY.xOffset += xMoveR;
1309 } else {
1310 // Move just enough to allow to display the caret
1311 newXY.xOffset += pt.x - (rcClient.right - xMarginR) + 1;
1314 } else { // Not strict
1315 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1316 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1317 if (bEven) {
1318 xMoveL = xMoveR;
1319 } else {
1320 xMoveL = rcClient.Width() - xMoveR - 4;
1322 if (pt.x < rcClient.left) {
1323 // Caret is on the left of the display
1324 newXY.xOffset -= xMoveL;
1325 } else if (pt.x >= rcClient.right) {
1326 // Caret is on the right of the display
1327 newXY.xOffset += xMoveR;
1330 } else { // No slop
1331 if (bStrict ||
1332 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1333 // Strict or going out of display
1334 if (bEven) {
1335 // Center caret
1336 newXY.xOffset += pt.x - rcClient.left - halfScreen;
1337 } else {
1338 // Put caret on right
1339 newXY.xOffset += pt.x - rcClient.right + 1;
1341 } else {
1342 // Move just enough to allow to display the caret
1343 if (pt.x < rcClient.left) {
1344 // Caret is on the left of the display
1345 if (bEven) {
1346 newXY.xOffset -= rcClient.left - pt.x;
1347 } else {
1348 newXY.xOffset += pt.x - rcClient.right + 1;
1350 } else if (pt.x >= rcClient.right) {
1351 // Caret is on the right of the display
1352 newXY.xOffset += pt.x - rcClient.right + 1;
1356 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1357 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1358 newXY.xOffset = pt.x + xOffset - rcClient.left;
1359 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1360 newXY.xOffset = pt.x + xOffset - rcClient.right + 1;
1361 if (vs.caretStyle == CARETSTYLE_BLOCK) {
1362 // Ensure we can see a good portion of the block caret
1363 newXY.xOffset += vs.aveCharWidth;
1366 if (newXY.xOffset < 0) {
1367 newXY.xOffset = 0;
1371 return newXY;
1374 void Editor::SetXYScroll(XYScrollPosition newXY) {
1375 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1376 if (newXY.topLine != topLine) {
1377 SetTopLine(newXY.topLine);
1378 SetVerticalScrollPos();
1380 if (newXY.xOffset != xOffset) {
1381 xOffset = newXY.xOffset;
1382 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1383 if (newXY.xOffset > 0) {
1384 PRectangle rcText = GetTextRectangle();
1385 if (horizontalScrollBarVisible &&
1386 rcText.Width() + xOffset > scrollWidth) {
1387 scrollWidth = xOffset + rcText.Width();
1388 SetScrollBars();
1391 SetHorizontalScrollPos();
1393 Redraw();
1394 UpdateSystemCaret();
1398 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1399 SetXYScroll(XYScrollToMakeVisible(useMargin, vert, horiz));
1402 void Editor::ShowCaretAtCurrentPosition() {
1403 if (hasFocus) {
1404 caret.active = true;
1405 caret.on = true;
1406 SetTicking(true);
1407 } else {
1408 caret.active = false;
1409 caret.on = false;
1411 InvalidateCaret();
1414 void Editor::DropCaret() {
1415 caret.active = false;
1416 InvalidateCaret();
1419 void Editor::InvalidateCaret() {
1420 if (posDrag.IsValid()) {
1421 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1422 } else {
1423 for (size_t r=0; r<sel.Count(); r++) {
1424 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1427 UpdateSystemCaret();
1430 void Editor::UpdateSystemCaret() {
1433 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1434 docLineStart = Platform::Clamp(docLineStart, 0, pdoc->LinesTotal());
1435 if (wrapStart > docLineStart) {
1436 wrapStart = docLineStart;
1437 llc.Invalidate(LineLayout::llPositions);
1439 if (wrapEnd < docLineEnd) {
1440 wrapEnd = docLineEnd;
1442 wrapEnd = Platform::Clamp(wrapEnd, 0, pdoc->LinesTotal());
1443 // Wrap lines during idle.
1444 if ((wrapState != eWrapNone) && (wrapEnd != wrapStart)) {
1445 SetIdle(true);
1449 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1450 AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
1451 int linesWrapped = 1;
1452 if (ll) {
1453 LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
1454 linesWrapped = ll->lines;
1456 return cs.SetHeight(lineToWrap, linesWrapped +
1457 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1460 // Check if wrapping needed and perform any needed wrapping.
1461 // fullwrap: if true, all lines which need wrapping will be done,
1462 // in this single call.
1463 // priorityWrapLineStart: If greater than or equal to zero, all lines starting from
1464 // here to 1 page + 100 lines past will be wrapped (even if there are
1465 // more lines under wrapping process in idle).
1466 // If it is neither fullwrap, nor priorityWrap, then 1 page + 100 lines will be
1467 // wrapped, if there are any wrapping going on in idle. (Generally this
1468 // condition is called only from idler).
1469 // Return true if wrapping occurred.
1470 bool Editor::WrapLines(bool fullWrap, int priorityWrapLineStart) {
1471 // If there are any pending wraps, do them during idle if possible.
1472 int linesInOneCall = LinesOnScreen() + 100;
1473 if (wrapState != eWrapNone) {
1474 if (wrapStart < wrapEnd) {
1475 if (!SetIdle(true)) {
1476 // Idle processing not supported so full wrap required.
1477 fullWrap = true;
1480 if (!fullWrap && priorityWrapLineStart >= 0 &&
1481 // .. and if the paint window is outside pending wraps
1482 (((priorityWrapLineStart + linesInOneCall) < wrapStart) ||
1483 (priorityWrapLineStart > wrapEnd))) {
1484 // No priority wrap pending
1485 return false;
1488 int goodTopLine = topLine;
1489 bool wrapOccurred = false;
1490 if (wrapStart <= pdoc->LinesTotal()) {
1491 if (wrapState == eWrapNone) {
1492 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1493 wrapWidth = LineLayout::wrapWidthInfinite;
1494 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1495 cs.SetHeight(lineDoc, 1 +
1496 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1498 wrapOccurred = true;
1500 wrapStart = wrapLineLarge;
1501 wrapEnd = wrapLineLarge;
1502 } else {
1503 if (wrapEnd >= pdoc->LinesTotal())
1504 wrapEnd = pdoc->LinesTotal();
1505 //ElapsedTime et;
1506 int lineDocTop = cs.DocFromDisplay(topLine);
1507 int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1508 PRectangle rcTextArea = GetClientRectangle();
1509 rcTextArea.left = vs.fixedColumnWidth;
1510 rcTextArea.right -= vs.rightMarginWidth;
1511 wrapWidth = rcTextArea.Width();
1512 RefreshStyleData();
1513 AutoSurface surface(this);
1514 if (surface) {
1515 bool priorityWrap = false;
1516 int lastLineToWrap = wrapEnd;
1517 int lineToWrap = wrapStart;
1518 if (!fullWrap) {
1519 if (priorityWrapLineStart >= 0) {
1520 // This is a priority wrap.
1521 lineToWrap = priorityWrapLineStart;
1522 lastLineToWrap = priorityWrapLineStart + linesInOneCall;
1523 priorityWrap = true;
1524 } else {
1525 // This is idle wrap.
1526 lastLineToWrap = wrapStart + linesInOneCall;
1528 if (lastLineToWrap >= wrapEnd)
1529 lastLineToWrap = wrapEnd;
1530 } // else do a fullWrap.
1532 // Ensure all lines being wrapped are styled.
1533 pdoc->EnsureStyledTo(pdoc->LineEnd(lastLineToWrap));
1535 // Platform::DebugPrintf("Wraplines: full = %d, priorityStart = %d (wrapping: %d to %d)\n", fullWrap, priorityWrapLineStart, lineToWrap, lastLineToWrap);
1536 // Platform::DebugPrintf("Pending wraps: %d to %d\n", wrapStart, wrapEnd);
1537 while (lineToWrap < lastLineToWrap) {
1538 if (WrapOneLine(surface, lineToWrap)) {
1539 wrapOccurred = true;
1541 lineToWrap++;
1543 if (!priorityWrap)
1544 wrapStart = lineToWrap;
1545 // If wrapping is done, bring it to resting position
1546 if (wrapStart >= wrapEnd) {
1547 wrapStart = wrapLineLarge;
1548 wrapEnd = wrapLineLarge;
1551 goodTopLine = cs.DisplayFromDoc(lineDocTop);
1552 if (subLineTop < cs.GetHeight(lineDocTop))
1553 goodTopLine += subLineTop;
1554 else
1555 goodTopLine += cs.GetHeight(lineDocTop);
1556 //double durWrap = et.Duration(true);
1557 //Platform::DebugPrintf("Wrap:%9.6g \n", durWrap);
1560 if (wrapOccurred) {
1561 SetScrollBars();
1562 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1563 SetVerticalScrollPos();
1565 return wrapOccurred;
1568 void Editor::LinesJoin() {
1569 if (!RangeContainsProtected(targetStart, targetEnd)) {
1570 UndoGroup ug(pdoc);
1571 bool prevNonWS = true;
1572 for (int pos = targetStart; pos < targetEnd; pos++) {
1573 if (IsEOLChar(pdoc->CharAt(pos))) {
1574 targetEnd -= pdoc->LenChar(pos);
1575 pdoc->DelChar(pos);
1576 if (prevNonWS) {
1577 // Ensure at least one space separating previous lines
1578 pdoc->InsertChar(pos, ' ');
1579 targetEnd++;
1581 } else {
1582 prevNonWS = pdoc->CharAt(pos) != ' ';
1588 const char *Editor::StringFromEOLMode(int eolMode) {
1589 if (eolMode == SC_EOL_CRLF) {
1590 return "\r\n";
1591 } else if (eolMode == SC_EOL_CR) {
1592 return "\r";
1593 } else {
1594 return "\n";
1598 void Editor::LinesSplit(int pixelWidth) {
1599 if (!RangeContainsProtected(targetStart, targetEnd)) {
1600 if (pixelWidth == 0) {
1601 PRectangle rcText = GetTextRectangle();
1602 pixelWidth = rcText.Width();
1604 int lineStart = pdoc->LineFromPosition(targetStart);
1605 int lineEnd = pdoc->LineFromPosition(targetEnd);
1606 const char *eol = StringFromEOLMode(pdoc->eolMode);
1607 UndoGroup ug(pdoc);
1608 for (int line = lineStart; line <= lineEnd; line++) {
1609 AutoSurface surface(this);
1610 AutoLineLayout ll(llc, RetrieveLineLayout(line));
1611 if (surface && ll) {
1612 unsigned int posLineStart = pdoc->LineStart(line);
1613 LayoutLine(line, surface, vs, ll, pixelWidth);
1614 for (int subLine = 1; subLine < ll->lines; subLine++) {
1615 pdoc->InsertCString(
1616 static_cast<int>(posLineStart + (subLine - 1) * strlen(eol) +
1617 ll->LineStart(subLine)),
1618 eol);
1619 targetEnd += static_cast<int>(strlen(eol));
1622 lineEnd = pdoc->LineFromPosition(targetEnd);
1627 int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) {
1628 if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
1629 return markerDefault;
1630 return markerCheck;
1633 // Avoid 64 bit compiler warnings.
1634 // Scintilla does not support text buffers larger than 2**31
1635 static int istrlen(const char *s) {
1636 return static_cast<int>(strlen(s));
1639 bool ValidStyledText(ViewStyle &vs, size_t styleOffset, const StyledText &st) {
1640 if (st.multipleStyles) {
1641 for (size_t iStyle=0; iStyle<st.length; iStyle++) {
1642 if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
1643 return false;
1645 } else {
1646 if (!vs.ValidStyle(styleOffset + st.style))
1647 return false;
1649 return true;
1652 static int WidthStyledText(Surface *surface, ViewStyle &vs, int styleOffset,
1653 const char *text, const unsigned char *styles, size_t len) {
1654 int width = 0;
1655 size_t start = 0;
1656 while (start < len) {
1657 size_t style = styles[start];
1658 size_t endSegment = start;
1659 while ((endSegment+1 < len) && (static_cast<size_t>(styles[endSegment+1]) == style))
1660 endSegment++;
1661 width += surface->WidthText(vs.styles[style+styleOffset].font, text + start,
1662 static_cast<int>(endSegment - start + 1));
1663 start = endSegment + 1;
1665 return width;
1668 static int WidestLineWidth(Surface *surface, ViewStyle &vs, int styleOffset, const StyledText &st) {
1669 int widthMax = 0;
1670 size_t start = 0;
1671 while (start < st.length) {
1672 size_t lenLine = st.LineLength(start);
1673 int widthSubLine;
1674 if (st.multipleStyles) {
1675 widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
1676 } else {
1677 widthSubLine = surface->WidthText(vs.styles[styleOffset + st.style].font,
1678 st.text + start, static_cast<int>(lenLine));
1680 if (widthSubLine > widthMax)
1681 widthMax = widthSubLine;
1682 start += lenLine + 1;
1684 return widthMax;
1687 void DrawStyledText(Surface *surface, ViewStyle &vs, int styleOffset, PRectangle rcText, int ascent,
1688 const StyledText &st, size_t start, size_t length) {
1690 if (st.multipleStyles) {
1691 int x = rcText.left;
1692 size_t i = 0;
1693 while (i < length) {
1694 size_t end = i;
1695 int style = st.styles[i + start];
1696 while (end < length-1 && st.styles[start+end+1] == style)
1697 end++;
1698 style += styleOffset;
1699 int width = surface->WidthText(vs.styles[style].font,
1700 st.text + start + i, static_cast<int>(end - i + 1));
1701 PRectangle rcSegment = rcText;
1702 rcSegment.left = x;
1703 rcSegment.right = x + width + 1;
1704 surface->DrawTextNoClip(rcSegment, vs.styles[style].font,
1705 ascent, st.text + start + i,
1706 static_cast<int>(end - i + 1),
1707 vs.styles[style].fore.allocated,
1708 vs.styles[style].back.allocated);
1709 x += width;
1710 i = end + 1;
1712 } else {
1713 size_t style = st.style + styleOffset;
1714 surface->DrawTextNoClip(rcText, vs.styles[style].font,
1715 rcText.top + vs.maxAscent, st.text + start,
1716 static_cast<int>(length),
1717 vs.styles[style].fore.allocated,
1718 vs.styles[style].back.allocated);
1722 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1723 if (vs.fixedColumnWidth == 0)
1724 return;
1726 PRectangle rcMargin = GetClientRectangle();
1727 rcMargin.right = vs.fixedColumnWidth;
1729 if (!rc.Intersects(rcMargin))
1730 return;
1732 Surface *surface;
1733 if (bufferedDraw) {
1734 surface = pixmapSelMargin;
1735 } else {
1736 surface = surfWindow;
1739 PRectangle rcSelMargin = rcMargin;
1740 rcSelMargin.right = rcMargin.left;
1742 for (int margin = 0; margin < vs.margins; margin++) {
1743 if (vs.ms[margin].width > 0) {
1745 rcSelMargin.left = rcSelMargin.right;
1746 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
1748 if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
1749 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
1750 // Required because of special way brush is created for selection margin
1751 surface->FillRectangle(rcSelMargin, *pixmapSelPattern);
1752 else {
1753 ColourAllocated colour;
1754 switch (vs.ms[margin].style) {
1755 case SC_MARGIN_BACK:
1756 colour = vs.styles[STYLE_DEFAULT].back.allocated;
1757 break;
1758 case SC_MARGIN_FORE:
1759 colour = vs.styles[STYLE_DEFAULT].fore.allocated;
1760 break;
1761 default:
1762 colour = vs.styles[STYLE_LINENUMBER].back.allocated;
1763 break;
1765 surface->FillRectangle(rcSelMargin, colour);
1767 } else {
1768 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated);
1771 int visibleLine = topLine;
1772 int yposScreen = 0;
1773 // Work out whether the top line is whitespace located after a
1774 // lessening of fold level which implies a 'fold tail' but which should not
1775 // be displayed until the last of a sequence of whitespace.
1776 bool needWhiteClosure = false;
1777 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1778 int level = pdoc->GetLevel(cs.DocFromDisplay(topLine));
1779 if (level & SC_FOLDLEVELWHITEFLAG) {
1780 int lineBack = cs.DocFromDisplay(topLine);
1781 int levelPrev = level;
1782 while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
1783 lineBack--;
1784 levelPrev = pdoc->GetLevel(lineBack);
1786 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
1787 if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK))
1788 needWhiteClosure = true;
1791 if (highlightDelimiter.isEnabled) {
1792 int lastLine = cs.DocFromDisplay(topLine + LinesOnScreen()) + 1;
1793 pdoc->GetHighlightDelimiters(highlightDelimiter, pdoc->LineFromPosition(CurrentPosition()), lastLine);
1797 // Old code does not know about new markers needed to distinguish all cases
1798 int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
1799 SC_MARKNUM_FOLDEROPEN);
1800 int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
1801 SC_MARKNUM_FOLDER);
1803 while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rcMargin.bottom) {
1805 PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed());
1806 int lineDoc = cs.DocFromDisplay(visibleLine);
1807 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
1808 bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
1809 bool lastSubLine = visibleLine == (cs.DisplayFromDoc(lineDoc + 1) - 1);
1811 int marks = pdoc->GetMark(lineDoc);
1812 if (!firstSubLine)
1813 marks = 0;
1815 bool headWithTail = false;
1817 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1818 // Decide which fold indicator should be displayed
1819 int level = pdoc->GetLevel(lineDoc);
1820 int levelNext = pdoc->GetLevel(lineDoc + 1);
1821 int levelNum = level & SC_FOLDLEVELNUMBERMASK;
1822 int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
1823 if (level & SC_FOLDLEVELHEADERFLAG) {
1824 if (firstSubLine) {
1825 if (levelNum < levelNextNum) {
1826 if (cs.GetExpanded(lineDoc)) {
1827 if (levelNum == SC_FOLDLEVELBASE)
1828 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
1829 else
1830 marks |= 1 << folderOpenMid;
1831 } else {
1832 if (levelNum == SC_FOLDLEVELBASE)
1833 marks |= 1 << SC_MARKNUM_FOLDER;
1834 else
1835 marks |= 1 << folderEnd;
1837 } else if (levelNum > SC_FOLDLEVELBASE) {
1838 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1840 } else {
1841 if (levelNum < levelNextNum) {
1842 if (cs.GetExpanded(lineDoc)) {
1843 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1844 } else if (levelNum > SC_FOLDLEVELBASE) {
1845 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1847 } else if (levelNum > SC_FOLDLEVELBASE) {
1848 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1851 needWhiteClosure = false;
1852 int firstFollowupLine = cs.DocFromDisplay(cs.DisplayFromDoc(lineDoc + 1));
1853 int firstFollowupLineLevel = pdoc->GetLevel(firstFollowupLine);
1854 int secondFollowupLineLevelNum = pdoc->GetLevel(firstFollowupLine + 1) & SC_FOLDLEVELNUMBERMASK;
1855 if (!cs.GetExpanded(lineDoc)) {
1856 if ((firstFollowupLineLevel & SC_FOLDLEVELWHITEFLAG) &&
1857 (levelNum > secondFollowupLineLevelNum))
1858 needWhiteClosure = true;
1860 if (highlightDelimiter.IsFoldBlockHighlighted(firstFollowupLine))
1861 headWithTail = true;
1863 } else if (level & SC_FOLDLEVELWHITEFLAG) {
1864 if (needWhiteClosure) {
1865 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1866 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1867 } else if (levelNextNum > SC_FOLDLEVELBASE) {
1868 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1869 needWhiteClosure = false;
1870 } else {
1871 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1872 needWhiteClosure = false;
1874 } else if (levelNum > SC_FOLDLEVELBASE) {
1875 if (levelNextNum < levelNum) {
1876 if (levelNextNum > SC_FOLDLEVELBASE) {
1877 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1878 } else {
1879 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1881 } else {
1882 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1885 } else if (levelNum > SC_FOLDLEVELBASE) {
1886 if (levelNextNum < levelNum) {
1887 needWhiteClosure = false;
1888 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1889 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1890 needWhiteClosure = true;
1891 } else if (lastSubLine) {
1892 if (levelNextNum > SC_FOLDLEVELBASE) {
1893 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1894 } else {
1895 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1897 } else {
1898 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1900 } else {
1901 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1906 marks &= vs.ms[margin].mask;
1908 PRectangle rcMarker = rcSelMargin;
1909 rcMarker.top = yposScreen;
1910 rcMarker.bottom = yposScreen + vs.lineHeight;
1911 if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
1912 char number[100];
1913 number[0] = '\0';
1914 if (firstSubLine)
1915 sprintf(number, "%d", lineDoc + 1);
1916 if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
1917 int lev = pdoc->GetLevel(lineDoc);
1918 sprintf(number, "%c%c %03X %03X",
1919 (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
1920 (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
1921 lev & SC_FOLDLEVELNUMBERMASK,
1922 lev >> 16
1925 PRectangle rcNumber = rcMarker;
1926 // Right justify
1927 int width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
1928 int xpos = rcNumber.right - width - 3;
1929 rcNumber.left = xpos;
1930 surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font,
1931 rcNumber.top + vs.maxAscent, number, istrlen(number),
1932 vs.styles[STYLE_LINENUMBER].fore.allocated,
1933 vs.styles[STYLE_LINENUMBER].back.allocated);
1934 } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
1935 if (firstSubLine) {
1936 const StyledText stMargin = pdoc->MarginStyledText(lineDoc);
1937 if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
1938 surface->FillRectangle(rcMarker,
1939 vs.styles[stMargin.StyleAt(0)+vs.marginStyleOffset].back.allocated);
1940 if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
1941 int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
1942 rcMarker.left = rcMarker.right - width - 3;
1944 DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, rcMarker.top + vs.maxAscent,
1945 stMargin, 0, stMargin.length);
1950 if (marks) {
1951 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1952 if (marks & 1) {
1953 LineMarker::typeOfFold tFold = LineMarker::undefined;
1954 if ((vs.ms[margin].mask & SC_MASK_FOLDERS) && highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) {
1955 if (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) {
1956 tFold = LineMarker::body;
1957 } else if (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) {
1958 if (firstSubLine) {
1959 tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head;
1960 } else {
1961 if (cs.GetExpanded(lineDoc) || headWithTail) {
1962 tFold = LineMarker::body;
1963 } else {
1964 tFold = LineMarker::undefined;
1967 } else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) {
1968 tFold = LineMarker::tail;
1971 vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font, tFold);
1973 marks >>= 1;
1977 visibleLine++;
1978 yposScreen += vs.lineHeight;
1983 PRectangle rcBlankMargin = rcMargin;
1984 rcBlankMargin.left = rcSelMargin.right;
1985 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back.allocated);
1987 if (bufferedDraw) {
1988 surfWindow->Copy(rcMargin, Point(), *pixmapSelMargin);
1992 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
1993 int ydiff = (rcTab.bottom - rcTab.top) / 2;
1994 int xhead = rcTab.right - 1 - ydiff;
1995 if (xhead <= rcTab.left) {
1996 ydiff -= rcTab.left - xhead - 1;
1997 xhead = rcTab.left - 1;
1999 if ((rcTab.left + 2) < (rcTab.right - 1))
2000 surface->MoveTo(rcTab.left + 2, ymid);
2001 else
2002 surface->MoveTo(rcTab.right - 1, ymid);
2003 surface->LineTo(rcTab.right - 1, ymid);
2004 surface->LineTo(xhead, ymid - ydiff);
2005 surface->MoveTo(rcTab.right - 1, ymid);
2006 surface->LineTo(xhead, ymid + ydiff);
2009 LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
2010 int posLineStart = pdoc->LineStart(lineNumber);
2011 int posLineEnd = pdoc->LineStart(lineNumber + 1);
2012 PLATFORM_ASSERT(posLineEnd >= posLineStart);
2013 int lineCaret = pdoc->LineFromPosition(sel.MainCaret());
2014 return llc.Retrieve(lineNumber, lineCaret,
2015 posLineEnd - posLineStart, pdoc->GetStyleClock(),
2016 LinesOnScreen() + 1, pdoc->LinesTotal());
2019 static bool GoodTrailByte(int v) {
2020 return (v >= 0x80) && (v < 0xc0);
2023 bool BadUTF(const char *s, int len, int &trailBytes) {
2024 // For the rules: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
2025 if (trailBytes) {
2026 trailBytes--;
2027 return false;
2029 const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
2030 if (*us < 0x80) {
2031 // Single bytes easy
2032 return false;
2033 } else if (*us > 0xF4) {
2034 // Characters longer than 4 bytes not possible in current UTF-8
2035 return true;
2036 } else if (*us >= 0xF0) {
2037 // 4 bytes
2038 if (len < 4)
2039 return true;
2040 if (GoodTrailByte(us[1]) && GoodTrailByte(us[2]) && GoodTrailByte(us[3])) {
2041 if (*us == 0xf4) {
2042 // Check if encoding a value beyond the last Unicode character 10FFFF
2043 if (us[1] > 0x8f) {
2044 return true;
2045 } else if (us[1] == 0x8f) {
2046 if (us[2] > 0xbf) {
2047 return true;
2048 } else if (us[2] == 0xbf) {
2049 if (us[3] > 0xbf) {
2050 return true;
2054 } else if ((*us == 0xf0) && ((us[1] & 0xf0) == 0x80)) {
2055 // Overlong
2056 return true;
2058 trailBytes = 3;
2059 return false;
2060 } else {
2061 return true;
2063 } else if (*us >= 0xE0) {
2064 // 3 bytes
2065 if (len < 3)
2066 return true;
2067 if (GoodTrailByte(us[1]) && GoodTrailByte(us[2])) {
2068 if ((*us == 0xe0) && ((us[1] & 0xe0) == 0x80)) {
2069 // Overlong
2070 return true;
2072 if ((*us == 0xed) && ((us[1] & 0xe0) == 0xa0)) {
2073 // Surrogate
2074 return true;
2076 if ((*us == 0xef) && (us[1] == 0xbf) && (us[2] == 0xbe)) {
2077 // U+FFFE
2078 return true;
2080 if ((*us == 0xef) && (us[1] == 0xbf) && (us[2] == 0xbf)) {
2081 // U+FFFF
2082 return true;
2084 trailBytes = 2;
2085 return false;
2086 } else {
2087 return true;
2089 } else if (*us >= 0xC2) {
2090 // 2 bytes
2091 if (len < 2)
2092 return true;
2093 if (GoodTrailByte(us[1])) {
2094 trailBytes = 1;
2095 return false;
2096 } else {
2097 return true;
2099 } else if (*us >= 0xC0) {
2100 // Overlong encoding
2101 return true;
2102 } else {
2103 // Trail byte
2104 return true;
2109 * Fill in the LineLayout data for the given line.
2110 * Copy the given @a line and its styles from the document into local arrays.
2111 * Also determine the x position at which each character starts.
2113 void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) {
2114 if (!ll)
2115 return;
2117 PLATFORM_ASSERT(line < pdoc->LinesTotal());
2118 PLATFORM_ASSERT(ll->chars != NULL);
2119 int posLineStart = pdoc->LineStart(line);
2120 int posLineEnd = pdoc->LineStart(line + 1);
2121 // If the line is very long, limit the treatment to a length that should fit in the viewport
2122 if (posLineEnd > (posLineStart + ll->maxLineLength)) {
2123 posLineEnd = posLineStart + ll->maxLineLength;
2125 if (ll->validity == LineLayout::llCheckTextAndStyle) {
2126 int lineLength = posLineEnd - posLineStart;
2127 if (!vstyle.viewEOL) {
2128 int cid = posLineEnd - 1;
2129 while ((cid > posLineStart) && IsEOLChar(pdoc->CharAt(cid))) {
2130 cid--;
2131 lineLength--;
2134 if (lineLength == ll->numCharsInLine) {
2135 // See if chars, styles, indicators, are all the same
2136 bool allSame = true;
2137 const int styleMask = pdoc->stylingBitsMask;
2138 // Check base line layout
2139 char styleByte = 0;
2140 int numCharsInLine = 0;
2141 while (numCharsInLine < lineLength) {
2142 int charInDoc = numCharsInLine + posLineStart;
2143 char chDoc = pdoc->CharAt(charInDoc);
2144 styleByte = pdoc->StyleAt(charInDoc);
2145 allSame = allSame &&
2146 (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte & styleMask));
2147 allSame = allSame &&
2148 (ll->indicators[numCharsInLine] == static_cast<char>(styleByte & ~styleMask));
2149 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
2150 allSame = allSame &&
2151 (ll->chars[numCharsInLine] == chDoc);
2152 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
2153 allSame = allSame &&
2154 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
2155 else // Style::caseUpper
2156 allSame = allSame &&
2157 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
2158 numCharsInLine++;
2160 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
2161 if (allSame) {
2162 ll->validity = LineLayout::llPositions;
2163 } else {
2164 ll->validity = LineLayout::llInvalid;
2166 } else {
2167 ll->validity = LineLayout::llInvalid;
2170 if (ll->validity == LineLayout::llInvalid) {
2171 ll->widthLine = LineLayout::wrapWidthInfinite;
2172 ll->lines = 1;
2173 if (vstyle.edgeState == EDGE_BACKGROUND) {
2174 ll->edgeColumn = pdoc->FindColumn(line, theEdge);
2175 if (ll->edgeColumn >= posLineStart) {
2176 ll->edgeColumn -= posLineStart;
2178 } else {
2179 ll->edgeColumn = -1;
2182 char styleByte;
2183 const int styleMask = pdoc->stylingBitsMask;
2184 ll->styleBitsSet = 0;
2185 // Fill base line layout
2186 const int lineLength = posLineEnd - posLineStart;
2187 pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
2188 pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
2189 int numCharsBeforeEOL = lineLength;
2190 while ((numCharsBeforeEOL > 0) && IsEOLChar(ll->chars[numCharsBeforeEOL-1])) {
2191 numCharsBeforeEOL--;
2193 const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
2194 for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
2195 styleByte = ll->styles[styleInLine];
2196 ll->styleBitsSet |= styleByte;
2197 ll->styles[styleInLine] = static_cast<char>(styleByte & styleMask);
2198 ll->indicators[styleInLine] = static_cast<char>(styleByte & ~styleMask);
2200 styleByte = static_cast<char>(((lineLength > 0) ? ll->styles[lineLength-1] : 0) & styleMask);
2201 if (vstyle.someStylesForceCase) {
2202 for (int charInLine = 0; charInLine<lineLength; charInLine++) {
2203 char chDoc = ll->chars[charInLine];
2204 if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
2205 ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
2206 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
2207 ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
2210 ll->xHighlightGuide = 0;
2211 // Extra element at the end of the line to hold end x position and act as
2212 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
2213 ll->styles[numCharsInLine] = styleByte; // For eolFilled
2214 ll->indicators[numCharsInLine] = 0;
2216 // Layout the line, determining the position of each character,
2217 // with an extra element at the end for the end of the line.
2218 int startseg = 0; // Start of the current segment, in char. number
2219 int startsegx = 0; // Start of the current segment, in pixels
2220 ll->positions[0] = 0;
2221 unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
2222 bool lastSegItalics = false;
2223 Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
2225 int ctrlCharWidth[32] = {0};
2226 bool isControlNext = IsControlCharacter(ll->chars[0]);
2227 int trailBytes = 0;
2228 bool isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars, numCharsInLine, trailBytes);
2229 for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
2230 bool isControl = isControlNext;
2231 isControlNext = IsControlCharacter(ll->chars[charInLine + 1]);
2232 bool isBadUTF = isBadUTFNext;
2233 isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars + charInLine + 1, numCharsInLine - charInLine - 1, trailBytes);
2234 if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) ||
2235 isControl || isControlNext || isBadUTF || isBadUTFNext) {
2236 ll->positions[startseg] = 0;
2237 if (vstyle.styles[ll->styles[charInLine]].visible) {
2238 if (isControl) {
2239 if (ll->chars[charInLine] == '\t') {
2240 ll->positions[charInLine + 1] = ((((startsegx + 2) /
2241 tabWidth) + 1) * tabWidth) - startsegx;
2242 } else if (controlCharSymbol < 32) {
2243 if (ctrlCharWidth[ll->chars[charInLine]] == 0) {
2244 const char *ctrlChar = ControlCharacterString(ll->chars[charInLine]);
2245 // +3 For a blank on front and rounded edge each side:
2246 ctrlCharWidth[ll->chars[charInLine]] =
2247 surface->WidthText(ctrlCharsFont, ctrlChar, istrlen(ctrlChar)) + 3;
2249 ll->positions[charInLine + 1] = ctrlCharWidth[ll->chars[charInLine]];
2250 } else {
2251 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
2252 surface->MeasureWidths(ctrlCharsFont, cc, 1,
2253 ll->positions + startseg + 1);
2255 lastSegItalics = false;
2256 } else if (isBadUTF) {
2257 char hexits[4];
2258 sprintf(hexits, "x%2X", ll->chars[charInLine] & 0xff);
2259 ll->positions[charInLine + 1] =
2260 surface->WidthText(ctrlCharsFont, hexits, istrlen(hexits)) + 3;
2261 } else { // Regular character
2262 int lenSeg = charInLine - startseg + 1;
2263 if ((lenSeg == 1) && (' ' == ll->chars[startseg])) {
2264 lastSegItalics = false;
2265 // Over half the segments are single characters and of these about half are space characters.
2266 ll->positions[charInLine + 1] = vstyle.styles[ll->styles[charInLine]].spaceWidth;
2267 } else {
2268 lastSegItalics = vstyle.styles[ll->styles[charInLine]].italic;
2269 posCache.MeasureWidths(surface, vstyle, ll->styles[charInLine], ll->chars + startseg,
2270 lenSeg, ll->positions + startseg + 1, pdoc);
2273 } else { // invisible
2274 for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
2275 ll->positions[posToZero] = 0;
2278 for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
2279 ll->positions[posToIncrease] += startsegx;
2281 startsegx = ll->positions[charInLine + 1];
2282 startseg = charInLine + 1;
2285 // Small hack to make lines that end with italics not cut off the edge of the last character
2286 if ((startseg > 0) && lastSegItalics) {
2287 ll->positions[startseg] += 2;
2289 ll->numCharsInLine = numCharsInLine;
2290 ll->numCharsBeforeEOL = numCharsBeforeEOL;
2291 ll->validity = LineLayout::llPositions;
2293 // Hard to cope when too narrow, so just assume there is space
2294 if (width < 20) {
2295 width = 20;
2297 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
2298 ll->widthLine = width;
2299 if (width == LineLayout::wrapWidthInfinite) {
2300 ll->lines = 1;
2301 } else if (width > ll->positions[ll->numCharsInLine]) {
2302 // Simple common case where line does not need wrapping.
2303 ll->lines = 1;
2304 } else {
2305 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2306 width -= vstyle.aveCharWidth; // take into account the space for end wrap mark
2308 ll->wrapIndent = wrapAddIndent;
2309 if (wrapIndentMode != SC_WRAPINDENT_FIXED)
2310 for (int i = 0; i < ll->numCharsInLine; i++) {
2311 if (!IsSpaceOrTab(ll->chars[i])) {
2312 ll->wrapIndent += ll->positions[i]; // Add line indent
2313 break;
2316 // Check for text width minimum
2317 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
2318 ll->wrapIndent = wrapAddIndent;
2319 // Check for wrapIndent minimum
2320 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < static_cast<int>(vstyle.aveCharWidth)))
2321 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
2322 ll->lines = 0;
2323 // Calculate line start positions based upon width.
2324 int lastGoodBreak = 0;
2325 int lastLineStart = 0;
2326 int startOffset = 0;
2327 int p = 0;
2328 while (p < ll->numCharsInLine) {
2329 if ((ll->positions[p + 1] - startOffset) >= width) {
2330 if (lastGoodBreak == lastLineStart) {
2331 // Try moving to start of last character
2332 if (p > 0) {
2333 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2334 - posLineStart;
2336 if (lastGoodBreak == lastLineStart) {
2337 // Ensure at least one character on line.
2338 lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
2339 - posLineStart;
2342 lastLineStart = lastGoodBreak;
2343 ll->lines++;
2344 ll->SetLineStart(ll->lines, lastGoodBreak);
2345 startOffset = ll->positions[lastGoodBreak];
2346 // take into account the space for start wrap mark and indent
2347 startOffset -= ll->wrapIndent;
2348 p = lastGoodBreak + 1;
2349 continue;
2351 if (p > 0) {
2352 if (wrapState == eWrapChar) {
2353 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2354 - posLineStart;
2355 p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
2356 continue;
2357 } else if (ll->styles[p] != ll->styles[p - 1]) {
2358 lastGoodBreak = p;
2359 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
2360 lastGoodBreak = p;
2363 p++;
2365 ll->lines++;
2367 ll->validity = LineLayout::llLines;
2371 ColourAllocated Editor::SelectionBackground(ViewStyle &vsDraw, bool main) {
2372 return main ?
2373 (primarySelection ? vsDraw.selbackground.allocated : vsDraw.selbackground2.allocated) :
2374 vsDraw.selAdditionalBackground.allocated;
2377 ColourAllocated Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
2378 ColourAllocated background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) {
2379 if (inSelection == 1) {
2380 if (vsDraw.selbackset && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
2381 return SelectionBackground(vsDraw, true);
2383 } else if (inSelection == 2) {
2384 if (vsDraw.selbackset && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
2385 return SelectionBackground(vsDraw, false);
2387 } else {
2388 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
2389 (i >= ll->edgeColumn) &&
2390 !IsEOLChar(ll->chars[i]))
2391 return vsDraw.edgecolour.allocated;
2392 if (inHotspot && vsDraw.hotspotBackgroundSet)
2393 return vsDraw.hotspotBackground.allocated;
2395 if (overrideBackground && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
2396 return background;
2397 } else {
2398 return vsDraw.styles[styleMain].back.allocated;
2402 void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
2403 Point from(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
2404 PRectangle rcCopyArea(start + 1, rcSegment.top, start + 2, rcSegment.bottom);
2405 surface->Copy(rcCopyArea, from,
2406 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
2409 void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
2410 bool isEndMarker, ColourAllocated wrapColour) {
2411 surface->PenColour(wrapColour);
2413 enum { xa = 1 }; // gap before start
2414 int w = rcPlace.right - rcPlace.left - xa - 1;
2416 bool xStraight = isEndMarker; // x-mirrored symbol for start marker
2417 bool yStraight = true;
2418 //bool yStraight= isEndMarker; // comment in for start marker y-mirrowed
2420 int x0 = xStraight ? rcPlace.left : rcPlace.right - 1;
2421 int y0 = yStraight ? rcPlace.top : rcPlace.bottom - 1;
2423 int dy = (rcPlace.bottom - rcPlace.top) / 5;
2424 int y = (rcPlace.bottom - rcPlace.top) / 2 + dy;
2426 struct Relative {
2427 Surface *surface;
2428 int xBase;
2429 int xDir;
2430 int yBase;
2431 int yDir;
2432 void MoveTo(int xRelative, int yRelative) {
2433 surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2435 void LineTo(int xRelative, int yRelative) {
2436 surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2439 Relative rel = {surface, x0, xStraight ? 1 : -1, y0, yStraight ? 1 : -1};
2441 // arrow head
2442 rel.MoveTo(xa, y);
2443 rel.LineTo(xa + 2*w / 3, y - dy);
2444 rel.MoveTo(xa, y);
2445 rel.LineTo(xa + 2*w / 3, y + dy);
2447 // arrow body
2448 rel.MoveTo(xa, y);
2449 rel.LineTo(xa + w, y);
2450 rel.LineTo(xa + w, y - 2 * dy);
2451 rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
2452 y - 2 * dy);
2455 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourAllocated fill, int alpha) {
2456 if (alpha != SC_ALPHA_NOALPHA) {
2457 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
2461 void DrawTextBlob(Surface *surface, ViewStyle &vsDraw, PRectangle rcSegment,
2462 const char *s, ColourAllocated textBack, ColourAllocated textFore, bool twoPhaseDraw) {
2463 if (!twoPhaseDraw) {
2464 surface->FillRectangle(rcSegment, textBack);
2466 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2467 int normalCharHeight = surface->Ascent(ctrlCharsFont) -
2468 surface->InternalLeading(ctrlCharsFont);
2469 PRectangle rcCChar = rcSegment;
2470 rcCChar.left = rcCChar.left + 1;
2471 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
2472 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
2473 PRectangle rcCentral = rcCChar;
2474 rcCentral.top++;
2475 rcCentral.bottom--;
2476 surface->FillRectangle(rcCentral, textFore);
2477 PRectangle rcChar = rcCChar;
2478 rcChar.left++;
2479 rcChar.right--;
2480 surface->DrawTextClipped(rcChar, ctrlCharsFont,
2481 rcSegment.top + vsDraw.maxAscent, s, istrlen(s),
2482 textBack, textFore);
2485 void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
2486 int line, int lineEnd, int xStart, int subLine, int subLineStart,
2487 bool overrideBackground, ColourAllocated background,
2488 bool drawWrapMarkEnd, ColourAllocated wrapColour) {
2490 const int posLineStart = pdoc->LineStart(line);
2491 const int styleMask = pdoc->stylingBitsMask;
2492 PRectangle rcSegment = rcLine;
2494 const bool lastSubLine = subLine == (ll->lines - 1);
2495 int virtualSpace = 0;
2496 if (lastSubLine) {
2497 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
2498 virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * spaceWidth;
2501 // Fill in a PRectangle representing the end of line characters
2503 int xEol = ll->positions[lineEnd] - subLineStart;
2505 // Fill the virtual space and show selections within it
2506 if (virtualSpace) {
2507 rcSegment.left = xEol + xStart;
2508 rcSegment.right = xEol + xStart + virtualSpace;
2509 surface->FillRectangle(rcSegment, overrideBackground ? background : vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2510 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
2511 SelectionSegment virtualSpaceRange(SelectionPosition(pdoc->LineEnd(line)), SelectionPosition(pdoc->LineEnd(line), sel.VirtualSpaceFor(pdoc->LineEnd(line))));
2512 for (size_t r=0; r<sel.Count(); r++) {
2513 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2514 if (alpha == SC_ALPHA_NOALPHA) {
2515 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
2516 if (!portion.Empty()) {
2517 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
2518 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
2519 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
2520 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2521 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2522 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == sel.Main()));
2529 int posAfterLineEnd = pdoc->LineStart(line + 1);
2530 int eolInSelection = (subLine == (ll->lines - 1)) ? sel.InSelectionForEOL(posAfterLineEnd) : 0;
2531 int alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2533 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
2534 int blobsWidth = 0;
2535 if (lastSubLine) {
2536 for (int eolPos=ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
2537 rcSegment.left = xStart + ll->positions[eolPos] - subLineStart + virtualSpace;
2538 rcSegment.right = xStart + ll->positions[eolPos+1] - subLineStart + virtualSpace;
2539 blobsWidth += rcSegment.Width();
2540 const char *ctrlChar = ControlCharacterString(ll->chars[eolPos]);
2541 int inSelection = 0;
2542 bool inHotspot = false;
2543 int styleMain = ll->styles[eolPos];
2544 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, eolPos, ll);
2545 ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
2546 if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1)) {
2547 if (alpha == SC_ALPHA_NOALPHA) {
2548 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2549 } else {
2550 surface->FillRectangle(rcSegment, textBack);
2551 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2553 } else {
2554 surface->FillRectangle(rcSegment, textBack);
2556 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
2560 // Draw the eol-is-selected rectangle
2561 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
2562 rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
2564 if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2565 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2566 } else {
2567 if (overrideBackground) {
2568 surface->FillRectangle(rcSegment, background);
2569 } else if (line < pdoc->LinesTotal() - 1) {
2570 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2571 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2572 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2573 } else {
2574 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
2576 if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2577 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2581 // Fill the remainder of the line
2582 rcSegment.left = rcSegment.right;
2583 if (rcSegment.left < rcLine.left)
2584 rcSegment.left = rcLine.left;
2585 rcSegment.right = rcLine.right;
2587 if (!hideSelection && vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2588 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2589 } else {
2590 if (overrideBackground) {
2591 surface->FillRectangle(rcSegment, background);
2592 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2593 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2594 } else {
2595 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
2597 if (!hideSelection && vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2598 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2602 if (drawWrapMarkEnd) {
2603 PRectangle rcPlace = rcSegment;
2605 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
2606 rcPlace.left = xEol + xStart + virtualSpace;
2607 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2608 } else {
2609 // draw left of the right text margin, to avoid clipping by the current clip rect
2610 rcPlace.right = rcLine.right - vs.rightMarginWidth;
2611 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2613 DrawWrapMarker(surface, rcPlace, true, wrapColour);
2617 void Editor::DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, ViewStyle &vsDraw,
2618 int xStart, PRectangle rcLine, LineLayout *ll, int subLine) {
2619 const int subLineStart = ll->positions[ll->LineStart(subLine)];
2620 PRectangle rcIndic(
2621 ll->positions[startPos] + xStart - subLineStart,
2622 rcLine.top + vsDraw.maxAscent,
2623 ll->positions[endPos] + xStart - subLineStart,
2624 rcLine.top + vsDraw.maxAscent + 3);
2625 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine);
2628 void Editor::DrawIndicators(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2629 PRectangle rcLine, LineLayout *ll, int subLine, int lineEnd, bool under) {
2630 // Draw decorators
2631 const int posLineStart = pdoc->LineStart(line);
2632 const int lineStart = ll->LineStart(subLine);
2633 const int posLineEnd = posLineStart + lineEnd;
2635 if (!under) {
2636 // Draw indicators
2637 // foreach indicator...
2638 for (int indicnum = 0, mask = 1 << pdoc->stylingBits; mask < 0x100; indicnum++) {
2639 if (!(mask & ll->styleBitsSet)) {
2640 mask <<= 1;
2641 continue;
2643 int startPos = -1;
2644 // foreach style pos in line...
2645 for (int indicPos = lineStart; indicPos <= lineEnd; indicPos++) {
2646 // look for starts...
2647 if (startPos < 0) {
2648 // NOT in indicator run, looking for START
2649 if (indicPos < lineEnd && (ll->indicators[indicPos] & mask))
2650 startPos = indicPos;
2652 // ... or ends
2653 if (startPos >= 0) {
2654 // IN indicator run, looking for END
2655 if (indicPos >= lineEnd || !(ll->indicators[indicPos] & mask)) {
2656 // AT end of indicator run, DRAW it!
2657 DrawIndicator(indicnum, startPos, indicPos, surface, vsDraw, xStart, rcLine, ll, subLine);
2658 // RESET control var
2659 startPos = -1;
2663 mask <<= 1;
2667 for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
2668 if (under == vsDraw.indicators[deco->indicator].under) {
2669 int startPos = posLineStart + lineStart;
2670 if (!deco->rs.ValueAt(startPos)) {
2671 startPos = deco->rs.EndRun(startPos);
2673 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
2674 int endPos = deco->rs.EndRun(startPos);
2675 if (endPos > posLineEnd)
2676 endPos = posLineEnd;
2677 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
2678 surface, vsDraw, xStart, rcLine, ll, subLine);
2679 startPos = deco->rs.EndRun(endPos);
2684 // Use indicators to highlight matching braces
2685 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
2686 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
2687 int braceIndicator = (bracesMatchStyle == STYLE_BRACELIGHT) ? vs.braceHighlightIndicator : vs.braceBadLightIndicator;
2688 if (under == vsDraw.indicators[braceIndicator].under) {
2689 Range rangeLine(posLineStart + lineStart, posLineEnd);
2690 if (rangeLine.ContainsCharacter(braces[0])) {
2691 int braceOffset = braces[0] - posLineStart;
2692 if (braceOffset < ll->numCharsInLine) {
2693 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2696 if (rangeLine.ContainsCharacter(braces[1])) {
2697 int braceOffset = braces[1] - posLineStart;
2698 if (braceOffset < ll->numCharsInLine) {
2699 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2706 void Editor::DrawAnnotation(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2707 PRectangle rcLine, LineLayout *ll, int subLine) {
2708 int indent = pdoc->GetLineIndentation(line) * vsDraw.spaceWidth;
2709 PRectangle rcSegment = rcLine;
2710 int annotationLine = subLine - ll->lines;
2711 const StyledText stAnnotation = pdoc->AnnotationStyledText(line);
2712 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
2713 surface->FillRectangle(rcSegment, vsDraw.styles[0].back.allocated);
2714 if (vs.annotationVisible == ANNOTATION_BOXED) {
2715 // Only care about calculating width if need to draw box
2716 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
2717 widthAnnotation += vsDraw.spaceWidth * 2; // Margins
2718 rcSegment.left = xStart + indent;
2719 rcSegment.right = rcSegment.left + widthAnnotation;
2720 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore.allocated);
2721 } else {
2722 rcSegment.left = xStart;
2724 const int annotationLines = pdoc->AnnotationLines(line);
2725 size_t start = 0;
2726 size_t lengthAnnotation = stAnnotation.LineLength(start);
2727 int lineInAnnotation = 0;
2728 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
2729 start += lengthAnnotation + 1;
2730 lengthAnnotation = stAnnotation.LineLength(start);
2731 lineInAnnotation++;
2733 PRectangle rcText = rcSegment;
2734 if (vs.annotationVisible == ANNOTATION_BOXED) {
2735 surface->FillRectangle(rcText,
2736 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back.allocated);
2737 rcText.left += vsDraw.spaceWidth;
2739 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText, rcText.top + vsDraw.maxAscent,
2740 stAnnotation, start, lengthAnnotation);
2741 if (vs.annotationVisible == ANNOTATION_BOXED) {
2742 surface->MoveTo(rcSegment.left, rcSegment.top);
2743 surface->LineTo(rcSegment.left, rcSegment.bottom);
2744 surface->MoveTo(rcSegment.right, rcSegment.top);
2745 surface->LineTo(rcSegment.right, rcSegment.bottom);
2746 if (subLine == ll->lines) {
2747 surface->MoveTo(rcSegment.left, rcSegment.top);
2748 surface->LineTo(rcSegment.right, rcSegment.top);
2750 if (subLine == ll->lines+annotationLines-1) {
2751 surface->MoveTo(rcSegment.left, rcSegment.bottom - 1);
2752 surface->LineTo(rcSegment.right, rcSegment.bottom - 1);
2758 void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
2759 PRectangle rcLine, LineLayout *ll, int subLine) {
2761 PRectangle rcSegment = rcLine;
2763 // Using one font for all control characters so it can be controlled independently to ensure
2764 // the box goes around the characters tightly. Seems to be no way to work out what height
2765 // is taken by an individual character - internal leading gives varying results.
2766 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2768 // See if something overrides the line background color: Either if caret is on the line
2769 // and background color is set for that, or if a marker is defined that forces its background
2770 // color onto the line, or if a marker is defined but has no selection margin in which to
2771 // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
2772 // with the earlier taking precedence. When multiple markers cause background override,
2773 // the color for the highest numbered one is used.
2774 bool overrideBackground = false;
2775 ColourAllocated background;
2776 if (caret.active && vsDraw.showCaretLineBackground && (vsDraw.caretLineAlpha == SC_ALPHA_NOALPHA) && ll->containsCaret) {
2777 overrideBackground = true;
2778 background = vsDraw.caretLineBackground.allocated;
2780 if (!overrideBackground) {
2781 int marks = pdoc->GetMark(line);
2782 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
2783 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) &&
2784 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2785 background = vsDraw.markers[markBit].back.allocated;
2786 overrideBackground = true;
2788 marks >>= 1;
2791 if (!overrideBackground) {
2792 if (vsDraw.maskInLine) {
2793 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
2794 if (marksMasked) {
2795 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
2796 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY) &&
2797 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2798 overrideBackground = true;
2799 background = vsDraw.markers[markBit].back.allocated;
2801 marksMasked >>= 1;
2807 SCNotification scn = {0};
2808 scn.nmhdr.code = SCN_GETBKCOLOR;
2809 scn.line = line;
2810 scn.lParam = -1;
2811 NotifyParent(&scn);
2812 if (scn.lParam != -1)
2814 background = scn.lParam;
2815 overrideBackground = true;
2818 bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
2819 (!overrideBackground) && (vsDraw.whitespaceBackgroundSet);
2821 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2822 int indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
2824 int posLineStart = pdoc->LineStart(line);
2826 int startseg = ll->LineStart(subLine);
2827 int subLineStart = ll->positions[startseg];
2828 if (subLine >= ll->lines) {
2829 DrawAnnotation(surface, vsDraw, line, xStart, rcLine, ll, subLine);
2830 return; // No further drawing
2832 int lineStart = 0;
2833 int lineEnd = 0;
2834 if (subLine < ll->lines) {
2835 lineStart = ll->LineStart(subLine);
2836 lineEnd = ll->LineStart(subLine + 1);
2837 if (subLine == ll->lines - 1) {
2838 lineEnd = ll->numCharsBeforeEOL;
2842 ColourAllocated wrapColour = vsDraw.styles[STYLE_DEFAULT].fore.allocated;
2843 if (vsDraw.whitespaceForegroundSet)
2844 wrapColour = vsDraw.whitespaceForeground.allocated;
2846 bool drawWrapMarkEnd = false;
2848 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2849 if (subLine + 1 < ll->lines) {
2850 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
2854 if (ll->wrapIndent != 0) {
2856 bool continuedWrapLine = false;
2857 if (subLine < ll->lines) {
2858 continuedWrapLine = ll->LineStart(subLine) != 0;
2861 if (continuedWrapLine) {
2862 // draw continuation rect
2863 PRectangle rcPlace = rcSegment;
2865 rcPlace.left = ll->positions[startseg] + xStart - subLineStart;
2866 rcPlace.right = rcPlace.left + ll->wrapIndent;
2868 // default bgnd here..
2869 surface->FillRectangle(rcSegment, overrideBackground ? background :
2870 vsDraw.styles[STYLE_DEFAULT].back.allocated);
2872 // main line style would be below but this would be inconsistent with end markers
2873 // also would possibly not be the style at wrap point
2874 //int styleMain = ll->styles[lineStart];
2875 //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back.allocated);
2877 if (wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
2879 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
2880 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2881 else
2882 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2884 DrawWrapMarker(surface, rcPlace, false, wrapColour);
2887 xStart += ll->wrapIndent;
2891 bool selBackDrawn = vsDraw.selbackset &&
2892 ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA));
2894 // Does not take margin into account but not significant
2895 int xStartVisible = subLineStart - xStart;
2897 ll->psel = &sel;
2899 BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, xStartVisible, selBackDrawn, pdoc);
2900 int next = bfBack.First();
2902 // Background drawing loop
2903 while (twoPhaseDraw && (next < lineEnd)) {
2905 startseg = next;
2906 next = bfBack.Next();
2907 int i = next - 1;
2908 int iDoc = i + posLineStart;
2910 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2911 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2912 // Only try to draw if really visible - enhances performance by not calling environment to
2913 // draw strings that are completely past the right side of the window.
2914 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2915 // Clip to line rectangle, since may have a huge position which will not work with some platforms
2916 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2917 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2919 int styleMain = ll->styles[i];
2920 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2921 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2922 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2923 if (ll->chars[i] == '\t') {
2924 // Tab display
2925 if (drawWhitespaceBackground &&
2926 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2927 textBack = vsDraw.whitespaceBackground.allocated;
2928 surface->FillRectangle(rcSegment, textBack);
2929 } else if (IsControlCharacter(ll->chars[i])) {
2930 // Control character display
2931 inIndentation = false;
2932 surface->FillRectangle(rcSegment, textBack);
2933 } else {
2934 // Normal text display
2935 surface->FillRectangle(rcSegment, textBack);
2936 if (vsDraw.viewWhitespace != wsInvisible ||
2937 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
2938 for (int cpos = 0; cpos <= i - startseg; cpos++) {
2939 if (ll->chars[cpos + startseg] == ' ') {
2940 if (drawWhitespaceBackground &&
2941 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
2942 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
2943 rcSegment.top,
2944 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
2945 rcSegment.bottom);
2946 surface->FillRectangle(rcSpace, vsDraw.whitespaceBackground.allocated);
2948 } else {
2949 inIndentation = false;
2954 } else if (rcSegment.left > rcLine.right) {
2955 break;
2959 if (twoPhaseDraw) {
2960 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
2961 xStart, subLine, subLineStart, overrideBackground, background,
2962 drawWrapMarkEnd, wrapColour);
2965 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, true);
2967 if (vsDraw.edgeState == EDGE_LINE) {
2968 int edgeX = theEdge * vsDraw.spaceWidth;
2969 rcSegment.left = edgeX + xStart;
2970 if ((ll->wrapIndent != 0) && (lineStart != 0))
2971 rcSegment.left -= ll->wrapIndent;
2972 rcSegment.right = rcSegment.left + 1;
2973 surface->FillRectangle(rcSegment, vsDraw.edgecolour.allocated);
2976 // Draw underline mark as part of background if not transparent
2977 int marks = pdoc->GetMark(line);
2978 int markBit;
2979 for (markBit = 0; (markBit < 32) && marks; markBit++) {
2980 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
2981 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2982 PRectangle rcUnderline = rcLine;
2983 rcUnderline.top = rcUnderline.bottom - 2;
2984 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back.allocated);
2986 marks >>= 1;
2989 inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2990 // Foreground drawing loop
2991 BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, xStartVisible,
2992 ((!twoPhaseDraw && selBackDrawn) || vsDraw.selforeset), pdoc);
2993 next = bfFore.First();
2995 while (next < lineEnd) {
2997 startseg = next;
2998 next = bfFore.Next();
2999 int i = next - 1;
3001 int iDoc = i + posLineStart;
3003 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
3004 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
3005 // Only try to draw if really visible - enhances performance by not calling environment to
3006 // draw strings that are completely past the right side of the window.
3007 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
3008 int styleMain = ll->styles[i];
3009 ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
3010 Font &textFont = vsDraw.styles[styleMain].font;
3011 //hotspot foreground
3012 if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) {
3013 if (vsDraw.hotspotForegroundSet)
3014 textFore = vsDraw.hotspotForeground.allocated;
3016 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
3017 if (inSelection && (vsDraw.selforeset)) {
3018 textFore = (inSelection == 1) ? vsDraw.selforeground.allocated : vsDraw.selAdditionalForeground.allocated;
3020 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
3021 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
3022 if (ll->chars[i] == '\t') {
3023 // Tab display
3024 if (!twoPhaseDraw) {
3025 if (drawWhitespaceBackground &&
3026 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
3027 textBack = vsDraw.whitespaceBackground.allocated;
3028 surface->FillRectangle(rcSegment, textBack);
3030 if ((vsDraw.viewWhitespace != wsInvisible) ||
3031 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
3032 if (vsDraw.whitespaceForegroundSet)
3033 textFore = vsDraw.whitespaceForeground.allocated;
3034 surface->PenColour(textFore);
3036 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
3037 for (int xIG = ll->positions[i] / indentWidth * indentWidth; xIG < ll->positions[i + 1]; xIG += indentWidth) {
3038 if (xIG >= ll->positions[i] && xIG > 0) {
3039 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIG + xStart, rcSegment,
3040 (ll->xHighlightGuide == xIG));
3044 if (vsDraw.viewWhitespace != wsInvisible) {
3045 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3046 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
3047 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
3048 DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
3051 } else if (IsControlCharacter(ll->chars[i])) {
3052 // Control character display
3053 inIndentation = false;
3054 if (controlCharSymbol < 32) {
3055 // Draw the character
3056 const char *ctrlChar = ControlCharacterString(ll->chars[i]);
3057 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
3058 } else {
3059 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
3060 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
3061 rcSegment.top + vsDraw.maxAscent,
3062 cc, 1, textBack, textFore);
3064 } else if ((i == startseg) && (static_cast<unsigned char>(ll->chars[i]) >= 0x80) && IsUnicodeMode()) {
3065 // A single byte >= 0x80 in UTF-8 is a bad byte and is displayed as its hex value
3066 char hexits[4];
3067 sprintf(hexits, "x%2X", ll->chars[i] & 0xff);
3068 DrawTextBlob(surface, vsDraw, rcSegment, hexits, textBack, textFore, twoPhaseDraw);
3069 } else {
3070 // Normal text display
3071 if (vsDraw.styles[styleMain].visible) {
3072 if (twoPhaseDraw) {
3073 surface->DrawTextTransparent(rcSegment, textFont,
3074 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
3075 i - startseg + 1, textFore);
3076 } else {
3077 surface->DrawTextNoClip(rcSegment, textFont,
3078 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
3079 i - startseg + 1, textFore, textBack);
3082 if (vsDraw.viewWhitespace != wsInvisible ||
3083 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
3084 for (int cpos = 0; cpos <= i - startseg; cpos++) {
3085 if (ll->chars[cpos + startseg] == ' ') {
3086 if (vsDraw.viewWhitespace != wsInvisible) {
3087 if (vsDraw.whitespaceForegroundSet)
3088 textFore = vsDraw.whitespaceForeground.allocated;
3089 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3090 int xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2;
3091 if (!twoPhaseDraw && drawWhitespaceBackground &&
3092 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
3093 textBack = vsDraw.whitespaceBackground.allocated;
3094 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
3095 rcSegment.top,
3096 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
3097 rcSegment.bottom);
3098 surface->FillRectangle(rcSpace, textBack);
3100 PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
3101 rcDot.right = rcDot.left + vs.whitespaceSize;
3102 rcDot.bottom = rcDot.top + vs.whitespaceSize;
3103 surface->FillRectangle(rcDot, textFore);
3106 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
3107 int startSpace = ll->positions[cpos + startseg];
3108 if (startSpace > 0 && (startSpace % indentWidth == 0)) {
3109 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, startSpace + xStart, rcSegment,
3110 (ll->xHighlightGuide == ll->positions[cpos + startseg]));
3113 } else {
3114 inIndentation = false;
3119 if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd) {
3120 PRectangle rcUL = rcSegment;
3121 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3122 rcUL.bottom = rcUL.top + 1;
3123 if (vsDraw.hotspotForegroundSet)
3124 surface->FillRectangle(rcUL, vsDraw.hotspotForeground.allocated);
3125 else
3126 surface->FillRectangle(rcUL, textFore);
3127 } else if (vsDraw.styles[styleMain].underline) {
3128 PRectangle rcUL = rcSegment;
3129 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3130 rcUL.bottom = rcUL.top + 1;
3131 surface->FillRectangle(rcUL, textFore);
3133 } else if (rcSegment.left > rcLine.right) {
3134 break;
3137 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
3138 && (subLine == 0)) {
3139 int indentSpace = pdoc->GetLineIndentation(line);
3140 int xStartText = ll->positions[pdoc->GetLineIndentPosition(line) - posLineStart];
3142 // Find the most recent line with some text
3144 int lineLastWithText = line;
3145 while (lineLastWithText > Platform::Maximum(line-20, 0) && pdoc->IsWhiteLine(lineLastWithText)) {
3146 lineLastWithText--;
3148 if (lineLastWithText < line) {
3149 xStartText = 100000; // Don't limit to visible indentation on empty line
3150 // This line is empty, so use indentation of last line with text
3151 int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText);
3152 int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
3153 if (isFoldHeader) {
3154 // Level is one more level than parent
3155 indentLastWithText += pdoc->IndentSize();
3157 if (vsDraw.viewIndentationGuides == ivLookForward) {
3158 // In viLookForward mode, previous line only used if it is a fold header
3159 if (isFoldHeader) {
3160 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3162 } else { // viLookBoth
3163 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3167 int lineNextWithText = line;
3168 while (lineNextWithText < Platform::Minimum(line+20, pdoc->LinesTotal()) && pdoc->IsWhiteLine(lineNextWithText)) {
3169 lineNextWithText++;
3171 if (lineNextWithText > line) {
3172 xStartText = 100000; // Don't limit to visible indentation on empty line
3173 // This line is empty, so use indentation of first next line with text
3174 indentSpace = Platform::Maximum(indentSpace,
3175 pdoc->GetLineIndentation(lineNextWithText));
3178 for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) {
3179 int xIndent = indentPos * vsDraw.spaceWidth;
3180 if (xIndent < xStartText) {
3181 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3182 (ll->xHighlightGuide == xIndent));
3187 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, false);
3189 // End of the drawing of the current line
3190 if (!twoPhaseDraw) {
3191 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
3192 xStart, subLine, subLineStart, overrideBackground, background,
3193 drawWrapMarkEnd, wrapColour);
3195 if (!hideSelection && ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA))) {
3196 // For each selection draw
3197 int virtualSpaces = 0;
3198 if (subLine == (ll->lines - 1)) {
3199 virtualSpaces = sel.VirtualSpaceFor(pdoc->LineEnd(line));
3201 SelectionPosition posStart(posLineStart);
3202 SelectionPosition posEnd(posLineStart + lineEnd, virtualSpaces);
3203 SelectionSegment virtualSpaceRange(posStart, posEnd);
3204 for (size_t r=0; r<sel.Count(); r++) {
3205 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
3206 if (alpha != SC_ALPHA_NOALPHA) {
3207 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
3208 if (!portion.Empty()) {
3209 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
3210 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
3211 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
3212 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
3213 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
3214 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == sel.Main()), alpha);
3220 // Draw any translucent whole line states
3221 rcSegment.left = 0;
3222 rcSegment.right = rcLine.right - 1;
3223 if (caret.active && vsDraw.showCaretLineBackground && ll->containsCaret) {
3224 SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground.allocated, vsDraw.caretLineAlpha);
3226 marks = pdoc->GetMark(line);
3227 for (markBit = 0; (markBit < 32) && marks; markBit++) {
3228 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
3229 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
3230 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
3231 PRectangle rcUnderline = rcSegment;
3232 rcUnderline.top = rcUnderline.bottom - 2;
3233 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
3235 marks >>= 1;
3237 if (vsDraw.maskInLine) {
3238 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
3239 if (marksMasked) {
3240 for (markBit = 0; (markBit < 32) && marksMasked; markBit++) {
3241 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
3242 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
3244 marksMasked >>= 1;
3250 void Editor::DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine,
3251 int xStart, int offset, int posCaret, PRectangle rcCaret, ColourAllocated caretColour) {
3253 int lineStart = ll->LineStart(subLine);
3254 int posBefore = posCaret;
3255 int posAfter = MovePositionOutsideChar(posCaret + 1, 1);
3256 int numCharsToDraw = posAfter - posCaret;
3258 // Work out where the starting and ending offsets are. We need to
3259 // see if the previous character shares horizontal space, such as a
3260 // glyph / combining character. If so we'll need to draw that too.
3261 int offsetFirstChar = offset;
3262 int offsetLastChar = offset + (posAfter - posCaret);
3263 while ((offsetLastChar - numCharsToDraw) >= lineStart) {
3264 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
3265 // The char does not share horizontal space
3266 break;
3268 // Char shares horizontal space, update the numChars to draw
3269 // Update posBefore to point to the prev char
3270 posBefore = MovePositionOutsideChar(posBefore - 1, -1);
3271 numCharsToDraw = posAfter - posBefore;
3272 offsetFirstChar = offset - (posCaret - posBefore);
3275 // See if the next character shares horizontal space, if so we'll
3276 // need to draw that too.
3277 numCharsToDraw = offsetLastChar - offsetFirstChar;
3278 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
3279 // Update posAfter to point to the 2nd next char, this is where
3280 // the next character ends, and 2nd next begins. We'll need
3281 // to compare these two
3282 posBefore = posAfter;
3283 posAfter = MovePositionOutsideChar(posAfter + 1, 1);
3284 offsetLastChar = offset + (posAfter - posCaret);
3285 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
3286 // The char does not share horizontal space
3287 break;
3289 // Char shares horizontal space, update the numChars to draw
3290 numCharsToDraw = offsetLastChar - offsetFirstChar;
3293 // We now know what to draw, update the caret drawing rectangle
3294 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
3295 rcCaret.right = ll->positions[offsetFirstChar+numCharsToDraw] - ll->positions[lineStart] + xStart;
3297 // Adjust caret position to take into account any word wrapping symbols.
3298 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3299 int wordWrapCharWidth = ll->wrapIndent;
3300 rcCaret.left += wordWrapCharWidth;
3301 rcCaret.right += wordWrapCharWidth;
3304 // This character is where the caret block is, we override the colours
3305 // (inversed) for drawing the caret here.
3306 int styleMain = ll->styles[offsetFirstChar];
3307 surface->DrawTextClipped(rcCaret, vsDraw.styles[styleMain].font,
3308 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
3309 numCharsToDraw, vsDraw.styles[styleMain].back.allocated,
3310 caretColour);
3313 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
3314 if (!pixmapSelPattern->Initialised()) {
3315 const int patternSize = 8;
3316 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3317 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
3318 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
3319 // way between the chrome colour and the chrome highlight colour making a nice transition
3320 // between the window chrome and the content area. And it works in low colour depths.
3321 PRectangle rcPattern(0, 0, patternSize, patternSize);
3323 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
3324 ColourAllocated colourFMFill = vs.selbar.allocated;
3325 ColourAllocated colourFMStripes = vs.selbarlight.allocated;
3327 if (!(vs.selbarlight.desired == ColourDesired(0xff, 0xff, 0xff))) {
3328 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
3329 // (Typically, the highlight colour is white.)
3330 colourFMFill = vs.selbarlight.allocated;
3333 if (vs.foldmarginColourSet) {
3334 // override default fold margin colour
3335 colourFMFill = vs.foldmarginColour.allocated;
3337 if (vs.foldmarginHighlightColourSet) {
3338 // override default fold margin highlight colour
3339 colourFMStripes = vs.foldmarginHighlightColour.allocated;
3342 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
3343 for (int y = 0; y < patternSize; y++) {
3344 for (int x = y % 2; x < patternSize; x+=2) {
3345 PRectangle rcPixel(x, y, x+1, y+1);
3346 pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);
3351 if (!pixmapIndentGuide->Initialised()) {
3352 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
3353 pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3354 pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3355 PRectangle rcIG(0, 0, 1, vs.lineHeight);
3356 pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back.allocated);
3357 pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore.allocated);
3358 pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back.allocated);
3359 pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore.allocated);
3360 for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
3361 PRectangle rcPixel(0, stripe, 1, stripe+1);
3362 pixmapIndentGuide->FillRectangle(rcPixel, vs.styles[STYLE_INDENTGUIDE].fore.allocated);
3363 pixmapIndentGuideHighlight->FillRectangle(rcPixel, vs.styles[STYLE_BRACELIGHT].fore.allocated);
3367 if (bufferedDraw) {
3368 if (!pixmapLine->Initialised()) {
3369 PRectangle rcClient = GetClientRectangle();
3370 pixmapLine->InitPixMap(rcClient.Width(), vs.lineHeight,
3371 surfaceWindow, wMain.GetID());
3372 pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
3373 rcClient.Height(), surfaceWindow, wMain.GetID());
3378 void Editor::DrawCarets(Surface *surface, ViewStyle &vsDraw, int lineDoc, int xStart,
3379 PRectangle rcLine, LineLayout *ll, int subLine) {
3380 // When drag is active it is the only caret drawn
3381 bool drawDrag = posDrag.IsValid();
3382 if (hideSelection && !drawDrag)
3383 return;
3384 const int posLineStart = pdoc->LineStart(lineDoc);
3385 // For each selection draw
3386 for (size_t r=0; (r<sel.Count()) || drawDrag; r++) {
3387 const bool mainCaret = r == sel.Main();
3388 const SelectionPosition posCaret = (drawDrag ? posDrag : sel.Range(r).caret);
3389 const int offset = posCaret.Position() - posLineStart;
3390 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
3391 const int virtualOffset = posCaret.VirtualSpace() * spaceWidth;
3392 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
3393 int xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
3394 if (ll->wrapIndent != 0) {
3395 int lineStart = ll->LineStart(subLine);
3396 if (lineStart != 0) // Wrapped
3397 xposCaret += ll->wrapIndent;
3399 bool caretBlinkState = (caret.active && caret.on) || (!additionalCaretsBlink && !mainCaret);
3400 bool caretVisibleState = additionalCaretsVisible || mainCaret;
3401 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
3402 ((posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
3403 bool caretAtEOF = false;
3404 bool caretAtEOL = false;
3405 bool drawBlockCaret = false;
3406 int widthOverstrikeCaret;
3407 int caretWidthOffset = 0;
3408 PRectangle rcCaret = rcLine;
3410 if (posCaret.Position() == pdoc->Length()) { // At end of document
3411 caretAtEOF = true;
3412 widthOverstrikeCaret = vsDraw.aveCharWidth;
3413 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
3414 caretAtEOL = true;
3415 widthOverstrikeCaret = vsDraw.aveCharWidth;
3416 } else {
3417 widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
3419 if (widthOverstrikeCaret < 3) // Make sure its visible
3420 widthOverstrikeCaret = 3;
3422 if (xposCaret > 0)
3423 caretWidthOffset = 1; // Move back so overlaps both character cells.
3424 xposCaret += xStart;
3425 if (posDrag.IsValid()) {
3426 /* Dragging text, use a line caret */
3427 rcCaret.left = xposCaret - caretWidthOffset;
3428 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3429 } else if (inOverstrike) {
3430 /* Overstrike (insert mode), use a modified bar caret */
3431 rcCaret.top = rcCaret.bottom - 2;
3432 rcCaret.left = xposCaret + 1;
3433 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
3434 } else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) {
3435 /* Block caret */
3436 rcCaret.left = xposCaret;
3437 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
3438 drawBlockCaret = true;
3439 rcCaret.right = xposCaret + widthOverstrikeCaret;
3440 } else {
3441 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
3443 } else {
3444 /* Line caret */
3445 rcCaret.left = xposCaret - caretWidthOffset;
3446 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3448 ColourAllocated caretColour = mainCaret ? vsDraw.caretcolour.allocated : vsDraw.additionalCaretColour.allocated;
3449 if (drawBlockCaret) {
3450 DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
3451 } else {
3452 surface->FillRectangle(rcCaret, caretColour);
3456 if (drawDrag)
3457 break;
3461 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
3462 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
3463 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
3465 StyleToPositionInView(PositionAfterArea(rcArea));
3467 pixmapLine->Release();
3468 RefreshStyleData();
3469 RefreshPixMaps(surfaceWindow);
3471 PRectangle rcClient = GetClientRectangle();
3472 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
3473 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3475 surfaceWindow->SetPalette(&palette, true);
3476 pixmapLine->SetPalette(&palette, !hasFocus);
3478 int screenLinePaintFirst = rcArea.top / vs.lineHeight;
3480 int xStart = vs.fixedColumnWidth - xOffset;
3481 int ypos = 0;
3482 if (!bufferedDraw)
3483 ypos += screenLinePaintFirst * vs.lineHeight;
3484 int yposScreen = screenLinePaintFirst * vs.lineHeight;
3486 bool paintAbandonedByStyling = paintState == paintAbandoned;
3487 if (needUpdateUI) {
3488 // Deselect palette by selecting a temporary palette
3489 Palette palTemp;
3490 surfaceWindow->SetPalette(&palTemp, true);
3492 NotifyUpdateUI();
3493 needUpdateUI = 0;
3495 RefreshStyleData();
3496 RefreshPixMaps(surfaceWindow);
3497 surfaceWindow->SetPalette(&palette, true);
3498 pixmapLine->SetPalette(&palette, !hasFocus);
3501 // Call priority lines wrap on a window of lines which are likely
3502 // to rendered with the following paint (that is wrap the visible
3503 // lines first).
3504 int startLineToWrap = cs.DocFromDisplay(topLine) - 5;
3505 if (startLineToWrap < 0)
3506 startLineToWrap = 0;
3507 if (WrapLines(false, startLineToWrap)) {
3508 // The wrapping process has changed the height of some lines so
3509 // abandon this paint for a complete repaint.
3510 if (AbandonPaint()) {
3511 return;
3513 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
3515 PLATFORM_ASSERT(pixmapSelPattern->Initialised());
3517 if (paintState != paintAbandoned) {
3518 PaintSelMargin(surfaceWindow, rcArea);
3520 PRectangle rcRightMargin = rcClient;
3521 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
3522 if (rcArea.Intersects(rcRightMargin)) {
3523 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back.allocated);
3527 if (paintState == paintAbandoned) {
3528 // Either styling or NotifyUpdateUI noticed that painting is needed
3529 // outside the current painting rectangle
3530 //Platform::DebugPrintf("Abandoning paint\n");
3531 if (wrapState != eWrapNone) {
3532 if (paintAbandonedByStyling) {
3533 // Styling has spilled over a line end, such as occurs by starting a multiline
3534 // comment. The width of subsequent text may have changed, so rewrap.
3535 NeedWrapping(cs.DocFromDisplay(topLine));
3538 return;
3540 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
3542 // Do the painting
3543 if (rcArea.right > vs.fixedColumnWidth) {
3545 Surface *surface = surfaceWindow;
3546 if (bufferedDraw) {
3547 surface = pixmapLine;
3548 PLATFORM_ASSERT(pixmapLine->Initialised());
3550 surface->SetUnicodeMode(IsUnicodeMode());
3551 surface->SetDBCSMode(CodePage());
3553 int visibleLine = topLine + screenLinePaintFirst;
3555 SelectionPosition posCaret = sel.RangeMain().caret;
3556 if (posDrag.IsValid())
3557 posCaret = posDrag;
3558 int lineCaret = pdoc->LineFromPosition(posCaret.Position());
3560 // Remove selection margin from drawing area so text will not be drawn
3561 // on it in unbuffered mode.
3562 PRectangle rcTextArea = rcClient;
3563 rcTextArea.left = vs.fixedColumnWidth;
3564 rcTextArea.right -= vs.rightMarginWidth;
3565 surfaceWindow->SetClip(rcTextArea);
3567 // Loop on visible lines
3568 //double durLayout = 0.0;
3569 //double durPaint = 0.0;
3570 //double durCopy = 0.0;
3571 //ElapsedTime etWhole;
3572 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
3573 AutoLineLayout ll(llc, 0);
3574 while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
3576 int lineDoc = cs.DocFromDisplay(visibleLine);
3577 // Only visible lines should be handled by the code within the loop
3578 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
3579 int lineStartSet = cs.DisplayFromDoc(lineDoc);
3580 int subLine = visibleLine - lineStartSet;
3582 // Copy this line and its styles from the document into local arrays
3583 // and determine the x position at which each character starts.
3584 //ElapsedTime et;
3585 if (lineDoc != lineDocPrevious) {
3586 ll.Set(0);
3587 ll.Set(RetrieveLineLayout(lineDoc));
3588 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
3589 lineDocPrevious = lineDoc;
3591 //durLayout += et.Duration(true);
3593 if (ll) {
3594 ll->containsCaret = lineDoc == lineCaret;
3595 if (hideSelection) {
3596 ll->containsCaret = false;
3599 GetHotSpotRange(ll->hsStart, ll->hsEnd);
3601 PRectangle rcLine = rcClient;
3602 rcLine.top = ypos;
3603 rcLine.bottom = ypos + vs.lineHeight;
3605 bool bracesIgnoreStyle = false;
3606 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
3607 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
3608 bracesIgnoreStyle = true;
3610 Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
3611 // Highlight the current braces if any
3612 ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
3613 highlightGuideColumn * vs.spaceWidth, bracesIgnoreStyle);
3615 // Draw the line
3616 DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
3617 //durPaint += et.Duration(true);
3619 // Restore the previous styles for the brace highlights in case layout is in cache.
3620 ll->RestoreBracesHighlight(rangeLine, braces, bracesIgnoreStyle);
3622 bool expanded = cs.GetExpanded(lineDoc);
3623 const int level = pdoc->GetLevel(lineDoc);
3624 const int levelNext = pdoc->GetLevel(lineDoc + 1);
3625 if ((level & SC_FOLDLEVELHEADERFLAG) &&
3626 ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
3627 // Paint the line above the fold
3628 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
3630 (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
3631 PRectangle rcFoldLine = rcLine;
3632 rcFoldLine.bottom = rcFoldLine.top + 1;
3633 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
3635 // Paint the line below the fold
3636 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
3638 (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
3639 PRectangle rcFoldLine = rcLine;
3640 rcFoldLine.top = rcFoldLine.bottom - 1;
3641 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
3645 DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine);
3647 if (bufferedDraw) {
3648 Point from(vs.fixedColumnWidth, 0);
3649 PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen,
3650 rcClient.right, yposScreen + vs.lineHeight);
3651 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
3654 lineWidthMaxSeen = Platform::Maximum(
3655 lineWidthMaxSeen, ll->positions[ll->numCharsInLine]);
3656 //durCopy += et.Duration(true);
3659 if (!bufferedDraw) {
3660 ypos += vs.lineHeight;
3663 yposScreen += vs.lineHeight;
3664 visibleLine++;
3666 //gdk_flush();
3668 ll.Set(0);
3669 //if (durPaint < 0.00000001)
3670 // durPaint = 0.00000001;
3672 // Right column limit indicator
3673 PRectangle rcBeyondEOF = rcClient;
3674 rcBeyondEOF.left = vs.fixedColumnWidth;
3675 rcBeyondEOF.right = rcBeyondEOF.right;
3676 rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight;
3677 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
3678 surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back.allocated);
3679 if (vs.edgeState == EDGE_LINE) {
3680 int edgeX = theEdge * vs.spaceWidth;
3681 rcBeyondEOF.left = edgeX + xStart;
3682 rcBeyondEOF.right = rcBeyondEOF.left + 1;
3683 surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour.allocated);
3686 //Platform::DebugPrintf(
3687 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
3688 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
3689 NotifyPainted();
3693 // Space (3 space characters) between line numbers and text when printing.
3694 #define lineNumberPrintSpace " "
3696 ColourDesired InvertedLight(ColourDesired orig) {
3697 unsigned int r = orig.GetRed();
3698 unsigned int g = orig.GetGreen();
3699 unsigned int b = orig.GetBlue();
3700 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
3701 unsigned int il = 0xff - l;
3702 if (l == 0)
3703 return ColourDesired(0xff, 0xff, 0xff);
3704 r = r * il / l;
3705 g = g * il / l;
3706 b = b * il / l;
3707 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
3710 // This is mostly copied from the Paint method but with some things omitted
3711 // such as the margin markers, line numbers, selection and caret
3712 // Should be merged back into a combined Draw method.
3713 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
3714 if (!pfr)
3715 return 0;
3717 AutoSurface surface(pfr->hdc, this);
3718 if (!surface)
3719 return 0;
3720 AutoSurface surfaceMeasure(pfr->hdcTarget, this);
3721 if (!surfaceMeasure) {
3722 return 0;
3725 // Can't use measurements cached for screen
3726 posCache.Clear();
3728 ViewStyle vsPrint(vs);
3730 // Modify the view style for printing as do not normally want any of the transient features to be printed
3731 // Printing supports only the line number margin.
3732 int lineNumberIndex = -1;
3733 for (int margin = 0; margin < ViewStyle::margins; margin++) {
3734 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
3735 lineNumberIndex = margin;
3736 } else {
3737 vsPrint.ms[margin].width = 0;
3740 vsPrint.showMarkedLines = false;
3741 vsPrint.fixedColumnWidth = 0;
3742 vsPrint.zoomLevel = printMagnification;
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.desired = InvertedLight(vsPrint.styles[sty].fore.desired);
3760 vsPrint.styles[sty].back.desired = InvertedLight(vsPrint.styles[sty].back.desired);
3761 } else if (printColourMode == SC_PRINT_BLACKONWHITE) {
3762 vsPrint.styles[sty].fore.desired = ColourDesired(0, 0, 0);
3763 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3764 } else if (printColourMode == SC_PRINT_COLOURONWHITE) {
3765 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3766 } else if (printColourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
3767 if (sty <= STYLE_DEFAULT) {
3768 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3772 // White background for the line numbers
3773 vsPrint.styles[STYLE_LINENUMBER].back.desired = ColourDesired(0xff, 0xff, 0xff);
3775 vsPrint.Refresh(*surfaceMeasure);
3776 // Determining width must hapen after fonts have been realised in Refresh
3777 int lineNumberWidth = 0;
3778 if (lineNumberIndex >= 0) {
3779 lineNumberWidth = surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
3780 "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace));
3781 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
3782 vsPrint.Refresh(*surfaceMeasure); // Recalculate fixedColumnWidth
3784 // Ensure colours are set up
3785 vsPrint.RefreshColourPalette(palette, true);
3786 vsPrint.RefreshColourPalette(palette, false);
3788 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
3789 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
3790 if (linePrintLast < linePrintStart)
3791 linePrintLast = linePrintStart;
3792 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
3793 if (linePrintLast > linePrintMax)
3794 linePrintLast = linePrintMax;
3795 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
3796 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
3797 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
3798 int endPosPrint = pdoc->Length();
3799 if (linePrintLast < pdoc->LinesTotal())
3800 endPosPrint = pdoc->LineStart(linePrintLast + 1);
3802 // Ensure we are styled to where we are formatting.
3803 pdoc->EnsureStyledTo(endPosPrint);
3805 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
3806 int ypos = pfr->rc.top;
3808 int lineDoc = linePrintStart;
3810 int nPrintPos = pfr->chrg.cpMin;
3811 int visibleLine = 0;
3812 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
3813 if (printWrapState == eWrapNone)
3814 widthPrint = LineLayout::wrapWidthInfinite;
3816 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
3818 // When printing, the hdc and hdcTarget may be the same, so
3819 // changing the state of surfaceMeasure may change the underlying
3820 // state of surface. Therefore, any cached state is discarded before
3821 // using each surface.
3822 surfaceMeasure->FlushCachedState();
3824 // Copy this line and its styles from the document into local arrays
3825 // and determine the x position at which each character starts.
3826 LineLayout ll(8000);
3827 LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
3829 ll.containsCaret = false;
3831 PRectangle rcLine;
3832 rcLine.left = pfr->rc.left;
3833 rcLine.top = ypos;
3834 rcLine.right = pfr->rc.right - 1;
3835 rcLine.bottom = ypos + vsPrint.lineHeight;
3837 // When document line is wrapped over multiple display lines, find where
3838 // to start printing from to ensure a particular position is on the first
3839 // line of the page.
3840 if (visibleLine == 0) {
3841 int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
3842 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
3843 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
3844 visibleLine = -iwl;
3848 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
3849 visibleLine = -(ll.lines - 1);
3853 if (draw && lineNumberWidth &&
3854 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
3855 (visibleLine >= 0)) {
3856 char number[100];
3857 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
3858 PRectangle rcNumber = rcLine;
3859 rcNumber.right = rcNumber.left + lineNumberWidth;
3860 // Right justify
3861 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
3862 vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
3863 surface->FlushCachedState();
3864 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
3865 ypos + vsPrint.maxAscent, number, istrlen(number),
3866 vsPrint.styles[STYLE_LINENUMBER].fore.allocated,
3867 vsPrint.styles[STYLE_LINENUMBER].back.allocated);
3870 // Draw the line
3871 surface->FlushCachedState();
3873 for (int iwl = 0; iwl < ll.lines; iwl++) {
3874 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
3875 if (visibleLine >= 0) {
3876 if (draw) {
3877 rcLine.top = ypos;
3878 rcLine.bottom = ypos + vsPrint.lineHeight;
3879 DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
3881 ypos += vsPrint.lineHeight;
3883 visibleLine++;
3884 if (iwl == ll.lines - 1)
3885 nPrintPos = pdoc->LineStart(lineDoc + 1);
3886 else
3887 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
3891 ++lineDoc;
3894 // Clear cache so measurements are not used for screen
3895 posCache.Clear();
3897 return nPrintPos;
3900 int Editor::TextWidth(int style, const char *text) {
3901 RefreshStyleData();
3902 AutoSurface surface(this);
3903 if (surface) {
3904 return surface->WidthText(vs.styles[style].font, text, istrlen(text));
3905 } else {
3906 return 1;
3910 // Empty method is overridden on GTK+ to show / hide scrollbars
3911 void Editor::ReconfigureScrollBars() {}
3913 void Editor::SetScrollBars() {
3914 RefreshStyleData();
3916 int nMax = MaxScrollPos();
3917 int nPage = LinesOnScreen();
3918 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
3919 if (modified) {
3920 DwellEnd(true);
3923 // TODO: ensure always showing as many lines as possible
3924 // May not be, if, for example, window made larger
3925 if (topLine > MaxScrollPos()) {
3926 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
3927 SetVerticalScrollPos();
3928 Redraw();
3930 if (modified) {
3931 if (!AbandonPaint())
3932 Redraw();
3934 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
3937 void Editor::ChangeSize() {
3938 DropGraphics();
3939 SetScrollBars();
3940 if (wrapState != eWrapNone) {
3941 PRectangle rcTextArea = GetClientRectangle();
3942 rcTextArea.left = vs.fixedColumnWidth;
3943 rcTextArea.right -= vs.rightMarginWidth;
3944 if (wrapWidth != rcTextArea.Width()) {
3945 NeedWrapping();
3946 Redraw();
3951 int Editor::InsertSpace(int position, unsigned int spaces) {
3952 if (spaces > 0) {
3953 std::string spaceText(spaces, ' ');
3954 pdoc->InsertString(position, spaceText.c_str(), spaces);
3955 position += spaces;
3957 return position;
3960 void Editor::AddChar(char ch) {
3961 char s[2];
3962 s[0] = ch;
3963 s[1] = '\0';
3964 AddCharUTF(s, 1);
3967 void Editor::FilterSelections() {
3968 if (!additionalSelectionTyping && (sel.Count() > 1)) {
3969 SelectionRange rangeOnly = sel.RangeMain();
3970 InvalidateSelection(rangeOnly, true);
3971 sel.SetSelection(rangeOnly);
3975 static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
3976 return *a < *b;
3979 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
3980 void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
3981 FilterSelections();
3983 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
3985 std::vector<SelectionRange *> selPtrs;
3986 for (size_t r = 0; r < sel.Count(); r++) {
3987 selPtrs.push_back(&sel.Range(r));
3989 std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
3991 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
3992 rit != selPtrs.rend(); ++rit) {
3993 SelectionRange *currentSel = *rit;
3994 if (!RangeContainsProtected(currentSel->Start().Position(),
3995 currentSel->End().Position())) {
3996 int positionInsert = currentSel->Start().Position();
3997 if (!currentSel->Empty()) {
3998 if (currentSel->Length()) {
3999 pdoc->DeleteChars(positionInsert, currentSel->Length());
4000 currentSel->ClearVirtualSpace();
4001 } else {
4002 // Range is all virtual so collapse to start of virtual space
4003 currentSel->MinimizeVirtualSpace();
4005 } else if (inOverstrike) {
4006 if (positionInsert < pdoc->Length()) {
4007 if (!IsEOLChar(pdoc->CharAt(positionInsert))) {
4008 pdoc->DelChar(positionInsert);
4009 currentSel->ClearVirtualSpace();
4013 positionInsert = InsertSpace(positionInsert, currentSel->caret.VirtualSpace());
4014 if (pdoc->InsertString(positionInsert, s, len)) {
4015 currentSel->caret.SetPosition(positionInsert + len);
4016 currentSel->anchor.SetPosition(positionInsert + len);
4018 currentSel->ClearVirtualSpace();
4019 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4020 if (wrapState != eWrapNone) {
4021 AutoSurface surface(this);
4022 if (surface) {
4023 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
4024 SetScrollBars();
4025 SetVerticalScrollPos();
4026 Redraw();
4033 if (wrapState != eWrapNone) {
4034 SetScrollBars();
4036 ThinRectangularRange();
4037 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4038 EnsureCaretVisible();
4039 // Avoid blinking during rapid typing:
4040 ShowCaretAtCurrentPosition();
4041 if ((caretSticky == SC_CARETSTICKY_OFF) ||
4042 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
4043 SetLastXChosen();
4046 if (treatAsDBCS) {
4047 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
4048 static_cast<unsigned char>(s[1]));
4049 } else {
4050 int byte = static_cast<unsigned char>(s[0]);
4051 if ((byte < 0xC0) || (1 == len)) {
4052 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
4053 // characters when not in UTF-8 mode.
4054 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
4055 // characters representing themselves.
4056 } else {
4057 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
4058 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
4059 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
4060 if (byte < 0xE0) {
4061 int byte2 = static_cast<unsigned char>(s[1]);
4062 if ((byte2 & 0xC0) == 0x80) {
4063 // Two-byte-character lead-byte followed by a trail-byte.
4064 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
4066 // A two-byte-character lead-byte not followed by trail-byte
4067 // represents itself.
4068 } else if (byte < 0xF0) {
4069 int byte2 = static_cast<unsigned char>(s[1]);
4070 int byte3 = static_cast<unsigned char>(s[2]);
4071 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
4072 // Three-byte-character lead byte followed by two trail bytes.
4073 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
4074 (byte3 & 0x3F));
4076 // A three-byte-character lead-byte not followed by two trail-bytes
4077 // represents itself.
4080 NotifyChar(byte);
4083 if (recordingMacro) {
4084 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
4088 void Editor::InsertPaste(SelectionPosition selStart, const char *text, int len) {
4089 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
4090 selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
4091 if (pdoc->InsertString(selStart.Position(), text, len)) {
4092 SetEmptySelection(selStart.Position() + len);
4094 } else {
4095 // SC_MULTIPASTE_EACH
4096 for (size_t r=0; r<sel.Count(); r++) {
4097 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4098 sel.Range(r).End().Position())) {
4099 int positionInsert = sel.Range(r).Start().Position();
4100 if (!sel.Range(r).Empty()) {
4101 if (sel.Range(r).Length()) {
4102 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
4103 sel.Range(r).ClearVirtualSpace();
4104 } else {
4105 // Range is all virtual so collapse to start of virtual space
4106 sel.Range(r).MinimizeVirtualSpace();
4109 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
4110 if (pdoc->InsertString(positionInsert, text, len)) {
4111 sel.Range(r).caret.SetPosition(positionInsert + len);
4112 sel.Range(r).anchor.SetPosition(positionInsert + len);
4114 sel.Range(r).ClearVirtualSpace();
4120 void Editor::ClearSelection(bool retainMultipleSelections) {
4121 if (!sel.IsRectangular() && !retainMultipleSelections)
4122 FilterSelections();
4123 UndoGroup ug(pdoc);
4124 for (size_t r=0; r<sel.Count(); r++) {
4125 if (!sel.Range(r).Empty()) {
4126 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4127 sel.Range(r).End().Position())) {
4128 pdoc->DeleteChars(sel.Range(r).Start().Position(),
4129 sel.Range(r).Length());
4130 sel.Range(r) = sel.Range(r).Start();
4134 ThinRectangularRange();
4135 sel.RemoveDuplicates();
4136 ClaimSelection();
4139 void Editor::ClearAll() {
4141 UndoGroup ug(pdoc);
4142 if (0 != pdoc->Length()) {
4143 pdoc->DeleteChars(0, pdoc->Length());
4145 if (!pdoc->IsReadOnly()) {
4146 cs.Clear();
4147 pdoc->AnnotationClearAll();
4148 pdoc->MarginClearAll();
4151 sel.Clear();
4152 SetTopLine(0);
4153 SetVerticalScrollPos();
4154 InvalidateStyleRedraw();
4157 void Editor::ClearDocumentStyle() {
4158 Decoration *deco = pdoc->decorations.root;
4159 while (deco) {
4160 // Save next in case deco deleted
4161 Decoration *decoNext = deco->next;
4162 if (deco->indicator < INDIC_CONTAINER) {
4163 pdoc->decorations.SetCurrentIndicator(deco->indicator);
4164 pdoc->DecorationFillRange(0, 0, pdoc->Length());
4166 deco = decoNext;
4168 pdoc->StartStyling(0, '\377');
4169 pdoc->SetStyleFor(pdoc->Length(), 0);
4170 cs.ShowAll();
4171 pdoc->ClearLevels();
4174 void Editor::CopyAllowLine() {
4175 SelectionText selectedText;
4176 CopySelectionRange(&selectedText, true);
4177 CopyToClipboard(selectedText);
4180 void Editor::Cut() {
4181 pdoc->CheckReadOnly();
4182 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
4183 Copy();
4184 ClearSelection();
4188 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
4189 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
4190 return;
4192 sel.Clear();
4193 sel.RangeMain() = SelectionRange(pos);
4194 int line = pdoc->LineFromPosition(sel.MainCaret());
4195 UndoGroup ug(pdoc);
4196 sel.RangeMain().caret = SelectionPosition(
4197 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
4198 int xInsert = XFromPosition(sel.RangeMain().caret);
4199 bool prevCr = false;
4200 while ((len > 0) && IsEOLChar(ptr[len-1]))
4201 len--;
4202 for (int i = 0; i < len; i++) {
4203 if (IsEOLChar(ptr[i])) {
4204 if ((ptr[i] == '\r') || (!prevCr))
4205 line++;
4206 if (line >= pdoc->LinesTotal()) {
4207 if (pdoc->eolMode != SC_EOL_LF)
4208 pdoc->InsertChar(pdoc->Length(), '\r');
4209 if (pdoc->eolMode != SC_EOL_CR)
4210 pdoc->InsertChar(pdoc->Length(), '\n');
4212 // Pad the end of lines with spaces if required
4213 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
4214 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
4215 while (XFromPosition(sel.MainCaret()) < xInsert) {
4216 pdoc->InsertChar(sel.MainCaret(), ' ');
4217 sel.RangeMain().caret.Add(1);
4220 prevCr = ptr[i] == '\r';
4221 } else {
4222 pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
4223 sel.RangeMain().caret.Add(1);
4224 prevCr = false;
4227 SetEmptySelection(pos);
4230 bool Editor::CanPaste() {
4231 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
4234 void Editor::Clear() {
4235 // If multiple selections, don't delete EOLS
4236 if (sel.Empty()) {
4237 bool singleVirtual = false;
4238 if ((sel.Count() == 1) &&
4239 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
4240 sel.RangeMain().Start().VirtualSpace()) {
4241 singleVirtual = true;
4243 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
4244 for (size_t r=0; r<sel.Count(); r++) {
4245 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
4246 if (sel.Range(r).Start().VirtualSpace()) {
4247 if (sel.Range(r).anchor < sel.Range(r).caret)
4248 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
4249 else
4250 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
4252 if ((sel.Count() == 1) || !IsEOLChar(pdoc->CharAt(sel.Range(r).caret.Position()))) {
4253 pdoc->DelChar(sel.Range(r).caret.Position());
4254 sel.Range(r).ClearVirtualSpace();
4255 } // else multiple selection so don't eat line ends
4256 } else {
4257 sel.Range(r).ClearVirtualSpace();
4260 } else {
4261 ClearSelection();
4263 sel.RemoveDuplicates();
4266 void Editor::SelectAll() {
4267 sel.Clear();
4268 SetSelection(0, pdoc->Length());
4269 Redraw();
4272 void Editor::Undo() {
4273 if (pdoc->CanUndo()) {
4274 InvalidateCaret();
4275 int newPos = pdoc->Undo();
4276 if (newPos >= 0)
4277 SetEmptySelection(newPos);
4278 EnsureCaretVisible();
4282 void Editor::Redo() {
4283 if (pdoc->CanRedo()) {
4284 int newPos = pdoc->Redo();
4285 if (newPos >= 0)
4286 SetEmptySelection(newPos);
4287 EnsureCaretVisible();
4291 void Editor::DelChar() {
4292 if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) {
4293 pdoc->DelChar(sel.MainCaret());
4295 // Avoid blinking during rapid typing:
4296 ShowCaretAtCurrentPosition();
4299 void Editor::DelCharBack(bool allowLineStartDeletion) {
4300 if (!sel.IsRectangular())
4301 FilterSelections();
4302 if (sel.IsRectangular())
4303 allowLineStartDeletion = false;
4304 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
4305 if (sel.Empty()) {
4306 for (size_t r=0; r<sel.Count(); r++) {
4307 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
4308 if (sel.Range(r).caret.VirtualSpace()) {
4309 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
4310 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
4311 } else {
4312 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4313 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
4314 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
4315 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
4316 UndoGroup ugInner(pdoc, !ug.Needed());
4317 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4318 int indentationStep = pdoc->IndentSize();
4319 if (indentation % indentationStep == 0) {
4320 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
4321 } else {
4322 pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
4324 // SetEmptySelection
4325 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos),
4326 pdoc->GetLineIndentPosition(lineCurrentPos));
4327 } else {
4328 pdoc->DelCharBack(sel.Range(r).caret.Position());
4332 } else {
4333 sel.Range(r).ClearVirtualSpace();
4336 } else {
4337 ClearSelection();
4339 sel.RemoveDuplicates();
4340 // Avoid blinking during rapid typing:
4341 ShowCaretAtCurrentPosition();
4344 void Editor::NotifyFocus(bool) {}
4346 void Editor::SetCtrlID(int identifier) {
4347 ctrlID = identifier;
4350 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
4351 SCNotification scn = {0};
4352 scn.nmhdr.code = SCN_STYLENEEDED;
4353 scn.position = endStyleNeeded;
4354 NotifyParent(scn);
4357 void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
4358 NotifyStyleToNeeded(endStyleNeeded);
4361 void Editor::NotifyLexerChanged(Document *, void *) {
4364 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
4365 errorStatus = status;
4368 void Editor::NotifyChar(int ch) {
4369 SCNotification scn = {0};
4370 scn.nmhdr.code = SCN_CHARADDED;
4371 scn.ch = ch;
4372 NotifyParent(scn);
4375 void Editor::NotifySavePoint(bool isSavePoint) {
4376 SCNotification scn = {0};
4377 if (isSavePoint) {
4378 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
4379 } else {
4380 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
4382 NotifyParent(scn);
4385 void Editor::NotifyModifyAttempt() {
4386 SCNotification scn = {0};
4387 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
4388 NotifyParent(scn);
4391 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
4392 SCNotification scn = {0};
4393 scn.nmhdr.code = SCN_DOUBLECLICK;
4394 scn.line = LineFromLocation(pt);
4395 scn.position = PositionFromLocation(pt, true);
4396 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4397 (alt ? SCI_ALT : 0);
4398 NotifyParent(scn);
4401 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
4402 SCNotification scn = {0};
4403 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
4404 scn.position = position;
4405 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4406 (alt ? SCI_ALT : 0);
4407 NotifyParent(scn);
4410 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
4411 SCNotification scn = {0};
4412 scn.nmhdr.code = SCN_HOTSPOTCLICK;
4413 scn.position = position;
4414 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4415 (alt ? SCI_ALT : 0);
4416 NotifyParent(scn);
4419 void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
4420 SCNotification scn = {0};
4421 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
4422 scn.position = position;
4423 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4424 (alt ? SCI_ALT : 0);
4425 NotifyParent(scn);
4428 void Editor::NotifyUpdateUI() {
4429 SCNotification scn = {0};
4430 scn.nmhdr.code = SCN_UPDATEUI;
4431 scn.updated = needUpdateUI;
4432 NotifyParent(scn);
4435 void Editor::NotifyPainted() {
4436 SCNotification scn = {0};
4437 scn.nmhdr.code = SCN_PAINTED;
4438 NotifyParent(scn);
4441 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
4442 int mask = pdoc->decorations.AllOnFor(position);
4443 if ((click && mask) || pdoc->decorations.clickNotified) {
4444 SCNotification scn = {0};
4445 pdoc->decorations.clickNotified = click;
4446 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
4447 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | (alt ? SCI_ALT : 0);
4448 scn.position = position;
4449 NotifyParent(scn);
4453 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
4454 int marginClicked = -1;
4455 int x = 0;
4456 for (int margin = 0; margin < ViewStyle::margins; margin++) {
4457 if ((pt.x > x) && (pt.x < x + vs.ms[margin].width))
4458 marginClicked = margin;
4459 x += vs.ms[margin].width;
4461 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
4462 SCNotification scn = {0};
4463 scn.nmhdr.code = SCN_MARGINCLICK;
4464 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4465 (alt ? SCI_ALT : 0);
4466 scn.position = pdoc->LineStart(LineFromLocation(pt));
4467 scn.margin = marginClicked;
4468 NotifyParent(scn);
4469 return true;
4470 } else {
4471 return false;
4475 void Editor::NotifyNeedShown(int pos, int len) {
4476 SCNotification scn = {0};
4477 scn.nmhdr.code = SCN_NEEDSHOWN;
4478 scn.position = pos;
4479 scn.length = len;
4480 NotifyParent(scn);
4483 void Editor::NotifyDwelling(Point pt, bool state) {
4484 SCNotification scn = {0};
4485 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
4486 scn.position = PositionFromLocation(pt, true);
4487 scn.x = pt.x;
4488 scn.y = pt.y;
4489 NotifyParent(scn);
4492 void Editor::NotifyZoom() {
4493 SCNotification scn = {0};
4494 scn.nmhdr.code = SCN_ZOOM;
4495 NotifyParent(scn);
4498 // Notifications from document
4499 void Editor::NotifyModifyAttempt(Document *, void *) {
4500 //Platform::DebugPrintf("** Modify Attempt\n");
4501 NotifyModifyAttempt();
4504 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
4505 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
4506 NotifySavePoint(atSavePoint);
4509 void Editor::CheckModificationForWrap(DocModification mh) {
4510 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
4511 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4512 if (wrapState != eWrapNone) {
4513 int lineDoc = pdoc->LineFromPosition(mh.position);
4514 int lines = Platform::Maximum(0, mh.linesAdded);
4515 NeedWrapping(lineDoc, lineDoc + lines + 1);
4517 // Fix up annotation heights
4518 int lineDoc = pdoc->LineFromPosition(mh.position);
4519 int lines = Platform::Maximum(0, mh.linesAdded);
4520 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
4524 // Move a position so it is still after the same character as before the insertion.
4525 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
4526 if (position > startInsertion) {
4527 return position + length;
4529 return position;
4532 // Move a position so it is still after the same character as before the deletion if that
4533 // character is still present else after the previous surviving character.
4534 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
4535 if (position > startDeletion) {
4536 int endDeletion = startDeletion + length;
4537 if (position > endDeletion) {
4538 return position - length;
4539 } else {
4540 return startDeletion;
4542 } else {
4543 return position;
4547 void Editor::NotifyModified(Document *, DocModification mh, void *) {
4548 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
4549 if (paintState == painting) {
4550 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
4552 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
4553 if (paintState == painting) {
4554 CheckForChangeOutsidePaint(
4555 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
4556 } else {
4557 // Could check that change is before last visible line.
4558 Redraw();
4561 if (mh.modificationType & SC_MOD_LEXERSTATE) {
4562 if (paintState == painting) {
4563 CheckForChangeOutsidePaint(
4564 Range(mh.position, mh.position + mh.length));
4565 } else {
4566 Redraw();
4569 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
4570 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4571 pdoc->IncrementStyleClock();
4573 if (paintState == notPainting) {
4574 if (mh.position < pdoc->LineStart(topLine)) {
4575 // Styling performed before this view
4576 Redraw();
4577 } else {
4578 InvalidateRange(mh.position, mh.position + mh.length);
4581 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4582 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4584 } else {
4585 // Move selection and brace highlights
4586 if (mh.modificationType & SC_MOD_INSERTTEXT) {
4587 sel.MovePositions(true, mh.position, mh.length);
4588 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
4589 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
4590 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
4591 sel.MovePositions(false, mh.position, mh.length);
4592 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
4593 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
4595 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && cs.HiddenLines()) {
4596 // Some lines are hidden so may need shown.
4597 // TODO: check if the modified area is hidden.
4598 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
4599 int lineOfPos = pdoc->LineFromPosition(mh.position);
4600 bool insertingNewLine = false;
4601 for (int i=0; i < mh.length; i++) {
4602 if ((mh.text[i] == '\n') || (mh.text[i] == '\r'))
4603 insertingNewLine = true;
4605 if (insertingNewLine && (mh.position != pdoc->LineStart(lineOfPos)))
4606 NotifyNeedShown(mh.position, pdoc->LineStart(lineOfPos+1) - mh.position);
4607 else
4608 NotifyNeedShown(mh.position, 0);
4609 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
4610 NotifyNeedShown(mh.position, mh.length);
4613 if (mh.linesAdded != 0) {
4614 // Update contraction state for inserted and removed lines
4615 // lineOfPos should be calculated in context of state before modification, shouldn't it
4616 int lineOfPos = pdoc->LineFromPosition(mh.position);
4617 if (mh.linesAdded > 0) {
4618 cs.InsertLines(lineOfPos, mh.linesAdded);
4619 } else {
4620 cs.DeleteLines(lineOfPos, -mh.linesAdded);
4623 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
4624 int lineDoc = pdoc->LineFromPosition(mh.position);
4625 if (vs.annotationVisible) {
4626 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
4627 Redraw();
4630 CheckModificationForWrap(mh);
4631 if (mh.linesAdded != 0) {
4632 // Avoid scrolling of display if change before current display
4633 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
4634 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
4635 if (newTop != topLine) {
4636 SetTopLine(newTop);
4637 SetVerticalScrollPos();
4641 //Platform::DebugPrintf("** %x Doc Changed\n", this);
4642 // TODO: could invalidate from mh.startModification to end of screen
4643 //InvalidateRange(mh.position, mh.position + mh.length);
4644 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
4645 QueueStyling(pdoc->Length());
4646 Redraw();
4648 } else {
4649 //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
4650 // mh.position, mh.position + mh.length);
4651 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
4652 QueueStyling(mh.position + mh.length);
4653 InvalidateRange(mh.position, mh.position + mh.length);
4658 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
4659 SetScrollBars();
4662 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
4663 if ((paintState == notPainting) || !PaintContainsMargin()) {
4664 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
4665 // Fold changes can affect the drawing of following lines so redraw whole margin
4666 RedrawSelMargin(mh.line-1, true);
4667 } else {
4668 RedrawSelMargin(mh.line);
4673 // NOW pay the piper WRT "deferred" visual updates
4674 if (IsLastStep(mh)) {
4675 SetScrollBars();
4676 Redraw();
4679 // If client wants to see this modification
4680 if (mh.modificationType & modEventMask) {
4681 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
4682 // Real modification made to text of document.
4683 NotifyChange(); // Send EN_CHANGE
4686 SCNotification scn = {0};
4687 scn.nmhdr.code = SCN_MODIFIED;
4688 scn.position = mh.position;
4689 scn.modificationType = mh.modificationType;
4690 scn.text = mh.text;
4691 scn.length = mh.length;
4692 scn.linesAdded = mh.linesAdded;
4693 scn.line = mh.line;
4694 scn.foldLevelNow = mh.foldLevelNow;
4695 scn.foldLevelPrev = mh.foldLevelPrev;
4696 scn.token = mh.token;
4697 scn.annotationLinesAdded = mh.annotationLinesAdded;
4698 NotifyParent(scn);
4702 void Editor::NotifyDeleted(Document *, void *) {
4703 /* Do nothing */
4706 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
4708 // Enumerates all macroable messages
4709 switch (iMessage) {
4710 case SCI_CUT:
4711 case SCI_COPY:
4712 case SCI_PASTE:
4713 case SCI_CLEAR:
4714 case SCI_REPLACESEL:
4715 case SCI_ADDTEXT:
4716 case SCI_INSERTTEXT:
4717 case SCI_APPENDTEXT:
4718 case SCI_CLEARALL:
4719 case SCI_SELECTALL:
4720 case SCI_GOTOLINE:
4721 case SCI_GOTOPOS:
4722 case SCI_SEARCHANCHOR:
4723 case SCI_SEARCHNEXT:
4724 case SCI_SEARCHPREV:
4725 case SCI_LINEDOWN:
4726 case SCI_LINEDOWNEXTEND:
4727 case SCI_PARADOWN:
4728 case SCI_PARADOWNEXTEND:
4729 case SCI_LINEUP:
4730 case SCI_LINEUPEXTEND:
4731 case SCI_PARAUP:
4732 case SCI_PARAUPEXTEND:
4733 case SCI_CHARLEFT:
4734 case SCI_CHARLEFTEXTEND:
4735 case SCI_CHARRIGHT:
4736 case SCI_CHARRIGHTEXTEND:
4737 case SCI_WORDLEFT:
4738 case SCI_WORDLEFTEXTEND:
4739 case SCI_WORDRIGHT:
4740 case SCI_WORDRIGHTEXTEND:
4741 case SCI_WORDPARTLEFT:
4742 case SCI_WORDPARTLEFTEXTEND:
4743 case SCI_WORDPARTRIGHT:
4744 case SCI_WORDPARTRIGHTEXTEND:
4745 case SCI_WORDLEFTEND:
4746 case SCI_WORDLEFTENDEXTEND:
4747 case SCI_WORDRIGHTEND:
4748 case SCI_WORDRIGHTENDEXTEND:
4749 case SCI_HOME:
4750 case SCI_HOMEEXTEND:
4751 case SCI_LINEEND:
4752 case SCI_LINEENDEXTEND:
4753 case SCI_HOMEWRAP:
4754 case SCI_HOMEWRAPEXTEND:
4755 case SCI_LINEENDWRAP:
4756 case SCI_LINEENDWRAPEXTEND:
4757 case SCI_DOCUMENTSTART:
4758 case SCI_DOCUMENTSTARTEXTEND:
4759 case SCI_DOCUMENTEND:
4760 case SCI_DOCUMENTENDEXTEND:
4761 case SCI_STUTTEREDPAGEUP:
4762 case SCI_STUTTEREDPAGEUPEXTEND:
4763 case SCI_STUTTEREDPAGEDOWN:
4764 case SCI_STUTTEREDPAGEDOWNEXTEND:
4765 case SCI_PAGEUP:
4766 case SCI_PAGEUPEXTEND:
4767 case SCI_PAGEDOWN:
4768 case SCI_PAGEDOWNEXTEND:
4769 case SCI_EDITTOGGLEOVERTYPE:
4770 case SCI_CANCEL:
4771 case SCI_DELETEBACK:
4772 case SCI_TAB:
4773 case SCI_BACKTAB:
4774 case SCI_FORMFEED:
4775 case SCI_VCHOME:
4776 case SCI_VCHOMEEXTEND:
4777 case SCI_VCHOMEWRAP:
4778 case SCI_VCHOMEWRAPEXTEND:
4779 case SCI_DELWORDLEFT:
4780 case SCI_DELWORDRIGHT:
4781 case SCI_DELWORDRIGHTEND:
4782 case SCI_DELLINELEFT:
4783 case SCI_DELLINERIGHT:
4784 case SCI_LINECOPY:
4785 case SCI_LINECUT:
4786 case SCI_LINEDELETE:
4787 case SCI_LINETRANSPOSE:
4788 case SCI_LINEDUPLICATE:
4789 case SCI_LOWERCASE:
4790 case SCI_UPPERCASE:
4791 case SCI_LINESCROLLDOWN:
4792 case SCI_LINESCROLLUP:
4793 case SCI_DELETEBACKNOTLINE:
4794 case SCI_HOMEDISPLAY:
4795 case SCI_HOMEDISPLAYEXTEND:
4796 case SCI_LINEENDDISPLAY:
4797 case SCI_LINEENDDISPLAYEXTEND:
4798 case SCI_SETSELECTIONMODE:
4799 case SCI_LINEDOWNRECTEXTEND:
4800 case SCI_LINEUPRECTEXTEND:
4801 case SCI_CHARLEFTRECTEXTEND:
4802 case SCI_CHARRIGHTRECTEXTEND:
4803 case SCI_HOMERECTEXTEND:
4804 case SCI_VCHOMERECTEXTEND:
4805 case SCI_LINEENDRECTEXTEND:
4806 case SCI_PAGEUPRECTEXTEND:
4807 case SCI_PAGEDOWNRECTEXTEND:
4808 case SCI_SELECTIONDUPLICATE:
4809 case SCI_COPYALLOWLINE:
4810 case SCI_VERTICALCENTRECARET:
4811 case SCI_MOVESELECTEDLINESUP:
4812 case SCI_MOVESELECTEDLINESDOWN:
4813 case SCI_SCROLLTOSTART:
4814 case SCI_SCROLLTOEND:
4815 break;
4817 // Filter out all others like display changes. Also, newlines are redundant
4818 // with char insert messages.
4819 case SCI_NEWLINE:
4820 default:
4821 // printf("Filtered out %ld of macro recording\n", iMessage);
4822 return ;
4825 // Send notification
4826 SCNotification scn = {0};
4827 scn.nmhdr.code = SCN_MACRORECORD;
4828 scn.message = iMessage;
4829 scn.wParam = wParam;
4830 scn.lParam = lParam;
4831 NotifyParent(scn);
4834 // Something has changed that the container should know about
4835 void Editor::ContainerNeedsUpdate(int flags) {
4836 needUpdateUI |= flags;
4840 * Force scroll and keep position relative to top of window.
4842 * If stuttered = true and not already at first/last row, move to first/last row of window.
4843 * If stuttered = true and already at first/last row, scroll as normal.
4845 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
4846 int topLineNew;
4847 SelectionPosition newPos;
4849 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
4850 int topStutterLine = topLine + caretYSlop;
4851 int bottomStutterLine =
4852 pdoc->LineFromPosition(PositionFromLocation(
4853 Point(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
4854 - caretYSlop - 1;
4856 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
4857 topLineNew = topLine;
4858 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
4859 false, false, UserVirtualSpace());
4861 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
4862 topLineNew = topLine;
4863 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
4864 false, false, UserVirtualSpace());
4866 } else {
4867 Point pt = LocationFromPosition(sel.MainCaret());
4869 topLineNew = Platform::Clamp(
4870 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
4871 newPos = SPositionFromLocation(
4872 Point(lastXChosen - xOffset, pt.y + direction * (vs.lineHeight * LinesToScroll())),
4873 false, false, UserVirtualSpace());
4876 if (topLineNew != topLine) {
4877 SetTopLine(topLineNew);
4878 MovePositionTo(newPos, selt);
4879 Redraw();
4880 SetVerticalScrollPos();
4881 } else {
4882 MovePositionTo(newPos, selt);
4886 void Editor::ChangeCaseOfSelection(int caseMapping) {
4887 UndoGroup ug(pdoc);
4888 for (size_t r=0; r<sel.Count(); r++) {
4889 SelectionRange current = sel.Range(r);
4890 SelectionRange currentNoVS = current;
4891 currentNoVS.ClearVirtualSpace();
4892 char *text = CopyRange(currentNoVS.Start().Position(), currentNoVS.End().Position());
4893 size_t rangeBytes = currentNoVS.Length();
4894 if (rangeBytes > 0) {
4895 std::string sText(text, rangeBytes);
4897 std::string sMapped = CaseMapString(sText, caseMapping);
4899 if (sMapped != sText) {
4900 size_t firstDifference = 0;
4901 while (sMapped[firstDifference] == sText[firstDifference])
4902 firstDifference++;
4903 size_t lastDifference = sMapped.size() - 1;
4904 while (sMapped[lastDifference] == sText[lastDifference])
4905 lastDifference--;
4906 size_t endSame = sMapped.size() - 1 - lastDifference;
4907 pdoc->DeleteChars(
4908 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
4909 static_cast<int>(rangeBytes - firstDifference - endSame));
4910 pdoc->InsertString(
4911 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
4912 sMapped.c_str() + firstDifference,
4913 static_cast<int>(lastDifference - firstDifference + 1));
4914 // Automatic movement changes selection so reset to exactly the same as it was.
4915 sel.Range(r) = current;
4918 delete []text;
4922 void Editor::LineTranspose() {
4923 int line = pdoc->LineFromPosition(sel.MainCaret());
4924 if (line > 0) {
4925 UndoGroup ug(pdoc);
4926 int startPrev = pdoc->LineStart(line - 1);
4927 int endPrev = pdoc->LineEnd(line - 1);
4928 int start = pdoc->LineStart(line);
4929 int end = pdoc->LineEnd(line);
4930 char *line1 = CopyRange(startPrev, endPrev);
4931 int len1 = endPrev - startPrev;
4932 char *line2 = CopyRange(start, end);
4933 int len2 = end - start;
4934 pdoc->DeleteChars(start, len2);
4935 pdoc->DeleteChars(startPrev, len1);
4936 pdoc->InsertString(startPrev, line2, len2);
4937 pdoc->InsertString(start - len1 + len2, line1, len1);
4938 MovePositionTo(SelectionPosition(start - len1 + len2));
4939 delete []line1;
4940 delete []line2;
4944 void Editor::Duplicate(bool forLine) {
4945 if (sel.Empty()) {
4946 forLine = true;
4948 UndoGroup ug(pdoc, sel.Count() > 1);
4949 const char *eol = "";
4950 int eolLen = 0;
4951 if (forLine) {
4952 eol = StringFromEOLMode(pdoc->eolMode);
4953 eolLen = istrlen(eol);
4955 for (size_t r=0; r<sel.Count(); r++) {
4956 SelectionPosition start = sel.Range(r).Start();
4957 SelectionPosition end = sel.Range(r).End();
4958 if (forLine) {
4959 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4960 start = SelectionPosition(pdoc->LineStart(line));
4961 end = SelectionPosition(pdoc->LineEnd(line));
4963 char *text = CopyRange(start.Position(), end.Position());
4964 if (forLine)
4965 pdoc->InsertString(end.Position(), eol, eolLen);
4966 pdoc->InsertString(end.Position() + eolLen, text, SelectionRange(end, start).Length());
4967 delete []text;
4969 if (sel.Count() && sel.IsRectangular()) {
4970 SelectionPosition last = sel.Last();
4971 if (forLine) {
4972 int line = pdoc->LineFromPosition(last.Position());
4973 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
4975 if (sel.Rectangular().anchor > sel.Rectangular().caret)
4976 sel.Rectangular().anchor = last;
4977 else
4978 sel.Rectangular().caret = last;
4979 SetRectangularRange();
4983 void Editor::CancelModes() {
4984 sel.SetMoveExtends(false);
4987 void Editor::NewLine() {
4988 ClearSelection();
4989 const char *eol = "\n";
4990 if (pdoc->eolMode == SC_EOL_CRLF) {
4991 eol = "\r\n";
4992 } else if (pdoc->eolMode == SC_EOL_CR) {
4993 eol = "\r";
4994 } // else SC_EOL_LF -> "\n" already set
4995 if (pdoc->InsertCString(sel.MainCaret(), eol)) {
4996 SetEmptySelection(sel.MainCaret() + istrlen(eol));
4997 while (*eol) {
4998 NotifyChar(*eol);
4999 if (recordingMacro) {
5000 char txt[2];
5001 txt[0] = *eol;
5002 txt[1] = '\0';
5003 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
5005 eol++;
5008 SetLastXChosen();
5009 SetScrollBars();
5010 EnsureCaretVisible();
5011 // Avoid blinking during rapid typing:
5012 ShowCaretAtCurrentPosition();
5015 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
5016 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
5017 if (sel.IsRectangular()) {
5018 if (selt == Selection::noSel) {
5019 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
5020 } else {
5021 caretToUse = sel.Rectangular().caret;
5024 Point pt = LocationFromPosition(caretToUse);
5025 int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
5026 Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
5027 int subLine = (pt.y - ptStartLine.y) / vs.lineHeight;
5028 int commentLines = vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0;
5029 SelectionPosition posNew = SPositionFromLocation(
5030 Point(lastXChosen - xOffset, pt.y + direction * vs.lineHeight), false, false, UserVirtualSpace());
5031 if ((direction > 0) && (subLine >= (cs.GetHeight(lineDoc) - 1 - commentLines))) {
5032 posNew = SPositionFromLocation(
5033 Point(lastXChosen - xOffset, pt.y + (commentLines + 1) * vs.lineHeight), false, false, UserVirtualSpace());
5035 if (direction < 0) {
5036 // Line wrapping may lead to a location on the same line, so
5037 // seek back if that is the case.
5038 // There is an equivalent case when moving down which skips
5039 // over a line but as that does not trap the user it is fine.
5040 Point ptNew = LocationFromPosition(posNew.Position());
5041 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
5042 posNew.Add(- 1);
5043 posNew.SetVirtualSpace(0);
5044 ptNew = LocationFromPosition(posNew.Position());
5047 MovePositionTo(posNew, selt);
5050 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
5051 int lineDoc, savedPos = sel.MainCaret();
5052 do {
5053 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
5054 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
5055 if (direction > 0) {
5056 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
5057 if (selt == Selection::noSel) {
5058 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
5060 break;
5063 } while (!cs.GetVisible(lineDoc));
5066 int Editor::StartEndDisplayLine(int pos, bool start) {
5067 RefreshStyleData();
5068 int line = pdoc->LineFromPosition(pos);
5069 AutoSurface surface(this);
5070 AutoLineLayout ll(llc, RetrieveLineLayout(line));
5071 int posRet = INVALID_POSITION;
5072 if (surface && ll) {
5073 unsigned int posLineStart = pdoc->LineStart(line);
5074 LayoutLine(line, surface, vs, ll, wrapWidth);
5075 int posInLine = pos - posLineStart;
5076 if (posInLine <= ll->maxLineLength) {
5077 for (int subLine = 0; subLine < ll->lines; subLine++) {
5078 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
5079 if (start) {
5080 posRet = ll->LineStart(subLine) + posLineStart;
5081 } else {
5082 if (subLine == ll->lines - 1)
5083 posRet = ll->LineStart(subLine + 1) + posLineStart;
5084 else
5085 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
5091 if (posRet == INVALID_POSITION) {
5092 return pos;
5093 } else {
5094 return posRet;
5098 int Editor::KeyCommand(unsigned int iMessage) {
5099 switch (iMessage) {
5100 case SCI_LINEDOWN:
5101 CursorUpOrDown(1);
5102 break;
5103 case SCI_LINEDOWNEXTEND:
5104 CursorUpOrDown(1, Selection::selStream);
5105 break;
5106 case SCI_LINEDOWNRECTEXTEND:
5107 CursorUpOrDown(1, Selection::selRectangle);
5108 break;
5109 case SCI_PARADOWN:
5110 ParaUpOrDown(1);
5111 break;
5112 case SCI_PARADOWNEXTEND:
5113 ParaUpOrDown(1, Selection::selStream);
5114 break;
5115 case SCI_LINESCROLLDOWN:
5116 ScrollTo(topLine + 1);
5117 MoveCaretInsideView(false);
5118 break;
5119 case SCI_LINEUP:
5120 CursorUpOrDown(-1);
5121 break;
5122 case SCI_LINEUPEXTEND:
5123 CursorUpOrDown(-1, Selection::selStream);
5124 break;
5125 case SCI_LINEUPRECTEXTEND:
5126 CursorUpOrDown(-1, Selection::selRectangle);
5127 break;
5128 case SCI_PARAUP:
5129 ParaUpOrDown(-1);
5130 break;
5131 case SCI_PARAUPEXTEND:
5132 ParaUpOrDown(-1, Selection::selStream);
5133 break;
5134 case SCI_LINESCROLLUP:
5135 ScrollTo(topLine - 1);
5136 MoveCaretInsideView(false);
5137 break;
5138 case SCI_CHARLEFT:
5139 if (SelectionEmpty() || sel.MoveExtends()) {
5140 if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5141 SelectionPosition spCaret = sel.RangeMain().caret;
5142 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5143 MovePositionTo(spCaret);
5144 } else {
5145 MovePositionTo(MovePositionSoVisible(
5146 SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
5148 } else {
5149 MovePositionTo(sel.LimitsForRectangularElseMain().start);
5151 SetLastXChosen();
5152 break;
5153 case SCI_CHARLEFTEXTEND:
5154 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5155 SelectionPosition spCaret = sel.RangeMain().caret;
5156 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5157 MovePositionTo(spCaret, Selection::selStream);
5158 } else {
5159 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
5161 SetLastXChosen();
5162 break;
5163 case SCI_CHARLEFTRECTEXTEND:
5164 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5165 SelectionPosition spCaret = sel.RangeMain().caret;
5166 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5167 MovePositionTo(spCaret, Selection::selRectangle);
5168 } else {
5169 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
5171 SetLastXChosen();
5172 break;
5173 case SCI_CHARRIGHT:
5174 if (SelectionEmpty() || sel.MoveExtends()) {
5175 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5176 SelectionPosition spCaret = sel.RangeMain().caret;
5177 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5178 MovePositionTo(spCaret);
5179 } else {
5180 MovePositionTo(MovePositionSoVisible(
5181 SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
5183 } else {
5184 MovePositionTo(sel.LimitsForRectangularElseMain().end);
5186 SetLastXChosen();
5187 break;
5188 case SCI_CHARRIGHTEXTEND:
5189 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5190 SelectionPosition spCaret = sel.RangeMain().caret;
5191 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5192 MovePositionTo(spCaret, Selection::selStream);
5193 } else {
5194 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
5196 SetLastXChosen();
5197 break;
5198 case SCI_CHARRIGHTRECTEXTEND:
5199 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5200 SelectionPosition spCaret = sel.RangeMain().caret;
5201 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5202 MovePositionTo(spCaret, Selection::selRectangle);
5203 } else {
5204 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
5206 SetLastXChosen();
5207 break;
5208 case SCI_WORDLEFT:
5209 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
5210 SetLastXChosen();
5211 break;
5212 case SCI_WORDLEFTEXTEND:
5213 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
5214 SetLastXChosen();
5215 break;
5216 case SCI_WORDRIGHT:
5217 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
5218 SetLastXChosen();
5219 break;
5220 case SCI_WORDRIGHTEXTEND:
5221 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
5222 SetLastXChosen();
5223 break;
5225 case SCI_WORDLEFTEND:
5226 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
5227 SetLastXChosen();
5228 break;
5229 case SCI_WORDLEFTENDEXTEND:
5230 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
5231 SetLastXChosen();
5232 break;
5233 case SCI_WORDRIGHTEND:
5234 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
5235 SetLastXChosen();
5236 break;
5237 case SCI_WORDRIGHTENDEXTEND:
5238 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
5239 SetLastXChosen();
5240 break;
5242 case SCI_HOME:
5243 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5244 SetLastXChosen();
5245 break;
5246 case SCI_HOMEEXTEND:
5247 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
5248 SetLastXChosen();
5249 break;
5250 case SCI_HOMERECTEXTEND:
5251 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
5252 SetLastXChosen();
5253 break;
5254 case SCI_LINEEND:
5255 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
5256 SetLastXChosen();
5257 break;
5258 case SCI_LINEENDEXTEND:
5259 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
5260 SetLastXChosen();
5261 break;
5262 case SCI_LINEENDRECTEXTEND:
5263 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
5264 SetLastXChosen();
5265 break;
5266 case SCI_HOMEWRAP: {
5267 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5268 if (sel.RangeMain().caret <= homePos)
5269 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5270 MovePositionTo(homePos);
5271 SetLastXChosen();
5273 break;
5274 case SCI_HOMEWRAPEXTEND: {
5275 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5276 if (sel.RangeMain().caret <= homePos)
5277 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5278 MovePositionTo(homePos, Selection::selStream);
5279 SetLastXChosen();
5281 break;
5282 case SCI_LINEENDWRAP: {
5283 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5284 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5285 if (endPos > realEndPos // if moved past visible EOLs
5286 || sel.RangeMain().caret >= endPos) // if at end of display line already
5287 endPos = realEndPos;
5288 MovePositionTo(endPos);
5289 SetLastXChosen();
5291 break;
5292 case SCI_LINEENDWRAPEXTEND: {
5293 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5294 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5295 if (endPos > realEndPos // if moved past visible EOLs
5296 || sel.RangeMain().caret >= endPos) // if at end of display line already
5297 endPos = realEndPos;
5298 MovePositionTo(endPos, Selection::selStream);
5299 SetLastXChosen();
5301 break;
5302 case SCI_DOCUMENTSTART:
5303 MovePositionTo(0);
5304 SetLastXChosen();
5305 break;
5306 case SCI_DOCUMENTSTARTEXTEND:
5307 MovePositionTo(0, Selection::selStream);
5308 SetLastXChosen();
5309 break;
5310 case SCI_DOCUMENTEND:
5311 MovePositionTo(pdoc->Length());
5312 SetLastXChosen();
5313 break;
5314 case SCI_DOCUMENTENDEXTEND:
5315 MovePositionTo(pdoc->Length(), Selection::selStream);
5316 SetLastXChosen();
5317 break;
5318 case SCI_STUTTEREDPAGEUP:
5319 PageMove(-1, Selection::noSel, true);
5320 break;
5321 case SCI_STUTTEREDPAGEUPEXTEND:
5322 PageMove(-1, Selection::selStream, true);
5323 break;
5324 case SCI_STUTTEREDPAGEDOWN:
5325 PageMove(1, Selection::noSel, true);
5326 break;
5327 case SCI_STUTTEREDPAGEDOWNEXTEND:
5328 PageMove(1, Selection::selStream, true);
5329 break;
5330 case SCI_PAGEUP:
5331 PageMove(-1);
5332 break;
5333 case SCI_PAGEUPEXTEND:
5334 PageMove(-1, Selection::selStream);
5335 break;
5336 case SCI_PAGEUPRECTEXTEND:
5337 PageMove(-1, Selection::selRectangle);
5338 break;
5339 case SCI_PAGEDOWN:
5340 PageMove(1);
5341 break;
5342 case SCI_PAGEDOWNEXTEND:
5343 PageMove(1, Selection::selStream);
5344 break;
5345 case SCI_PAGEDOWNRECTEXTEND:
5346 PageMove(1, Selection::selRectangle);
5347 break;
5348 case SCI_EDITTOGGLEOVERTYPE:
5349 inOverstrike = !inOverstrike;
5350 DropCaret();
5351 ShowCaretAtCurrentPosition();
5352 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
5353 NotifyUpdateUI();
5354 break;
5355 case SCI_CANCEL: // Cancel any modes - handled in subclass
5356 // Also unselect text
5357 CancelModes();
5358 break;
5359 case SCI_DELETEBACK:
5360 DelCharBack(true);
5361 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5362 SetLastXChosen();
5364 EnsureCaretVisible();
5365 break;
5366 case SCI_DELETEBACKNOTLINE:
5367 DelCharBack(false);
5368 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5369 SetLastXChosen();
5371 EnsureCaretVisible();
5372 break;
5373 case SCI_TAB:
5374 Indent(true);
5375 if (caretSticky == SC_CARETSTICKY_OFF) {
5376 SetLastXChosen();
5378 EnsureCaretVisible();
5379 ShowCaretAtCurrentPosition(); // Avoid blinking
5380 break;
5381 case SCI_BACKTAB:
5382 Indent(false);
5383 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5384 SetLastXChosen();
5386 EnsureCaretVisible();
5387 ShowCaretAtCurrentPosition(); // Avoid blinking
5388 break;
5389 case SCI_NEWLINE:
5390 NewLine();
5391 break;
5392 case SCI_FORMFEED:
5393 AddChar('\f');
5394 break;
5395 case SCI_VCHOME:
5396 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
5397 SetLastXChosen();
5398 break;
5399 case SCI_VCHOMEEXTEND:
5400 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
5401 SetLastXChosen();
5402 break;
5403 case SCI_VCHOMERECTEXTEND:
5404 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
5405 SetLastXChosen();
5406 break;
5407 case SCI_VCHOMEWRAP: {
5408 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5409 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5410 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5411 homePos = viewLineStart;
5413 MovePositionTo(homePos);
5414 SetLastXChosen();
5416 break;
5417 case SCI_VCHOMEWRAPEXTEND: {
5418 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5419 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5420 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5421 homePos = viewLineStart;
5423 MovePositionTo(homePos, Selection::selStream);
5424 SetLastXChosen();
5426 break;
5427 case SCI_ZOOMIN:
5428 if (vs.zoomLevel < 20) {
5429 vs.zoomLevel++;
5430 InvalidateStyleRedraw();
5431 NotifyZoom();
5433 break;
5434 case SCI_ZOOMOUT:
5435 if (vs.zoomLevel > -10) {
5436 vs.zoomLevel--;
5437 InvalidateStyleRedraw();
5438 NotifyZoom();
5440 break;
5441 case SCI_DELWORDLEFT: {
5442 int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
5443 pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
5444 sel.RangeMain().ClearVirtualSpace();
5445 SetLastXChosen();
5447 break;
5448 case SCI_DELWORDRIGHT: {
5449 UndoGroup ug(pdoc);
5450 sel.RangeMain().caret = SelectionPosition(
5451 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5452 sel.RangeMain().anchor = sel.RangeMain().caret;
5453 int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
5454 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5456 break;
5457 case SCI_DELWORDRIGHTEND: {
5458 UndoGroup ug(pdoc);
5459 sel.RangeMain().caret = SelectionPosition(
5460 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5461 int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
5462 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5464 break;
5465 case SCI_DELLINELEFT: {
5466 int line = pdoc->LineFromPosition(sel.MainCaret());
5467 int start = pdoc->LineStart(line);
5468 pdoc->DeleteChars(start, sel.MainCaret() - start);
5469 sel.RangeMain().ClearVirtualSpace();
5470 SetLastXChosen();
5472 break;
5473 case SCI_DELLINERIGHT: {
5474 int line = pdoc->LineFromPosition(sel.MainCaret());
5475 int end = pdoc->LineEnd(line);
5476 pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
5478 break;
5479 case SCI_LINECOPY: {
5480 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5481 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5482 CopyRangeToClipboard(pdoc->LineStart(lineStart),
5483 pdoc->LineStart(lineEnd + 1));
5485 break;
5486 case SCI_LINECUT: {
5487 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5488 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5489 int start = pdoc->LineStart(lineStart);
5490 int end = pdoc->LineStart(lineEnd + 1);
5491 SetSelection(start, end);
5492 Cut();
5493 SetLastXChosen();
5495 break;
5496 case SCI_LINEDELETE: {
5497 int line = pdoc->LineFromPosition(sel.MainCaret());
5498 int start = pdoc->LineStart(line);
5499 int end = pdoc->LineStart(line + 1);
5500 pdoc->DeleteChars(start, end - start);
5502 break;
5503 case SCI_LINETRANSPOSE:
5504 LineTranspose();
5505 break;
5506 case SCI_LINEDUPLICATE:
5507 Duplicate(true);
5508 break;
5509 case SCI_SELECTIONDUPLICATE:
5510 Duplicate(false);
5511 break;
5512 case SCI_LOWERCASE:
5513 ChangeCaseOfSelection(cmLower);
5514 break;
5515 case SCI_UPPERCASE:
5516 ChangeCaseOfSelection(cmUpper);
5517 break;
5518 case SCI_WORDPARTLEFT:
5519 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
5520 SetLastXChosen();
5521 break;
5522 case SCI_WORDPARTLEFTEXTEND:
5523 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
5524 SetLastXChosen();
5525 break;
5526 case SCI_WORDPARTRIGHT:
5527 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
5528 SetLastXChosen();
5529 break;
5530 case SCI_WORDPARTRIGHTEXTEND:
5531 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
5532 SetLastXChosen();
5533 break;
5534 case SCI_HOMEDISPLAY:
5535 MovePositionTo(MovePositionSoVisible(
5536 StartEndDisplayLine(sel.MainCaret(), true), -1));
5537 SetLastXChosen();
5538 break;
5539 case SCI_HOMEDISPLAYEXTEND:
5540 MovePositionTo(MovePositionSoVisible(
5541 StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
5542 SetLastXChosen();
5543 break;
5544 case SCI_LINEENDDISPLAY:
5545 MovePositionTo(MovePositionSoVisible(
5546 StartEndDisplayLine(sel.MainCaret(), false), 1));
5547 SetLastXChosen();
5548 break;
5549 case SCI_LINEENDDISPLAYEXTEND:
5550 MovePositionTo(MovePositionSoVisible(
5551 StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
5552 SetLastXChosen();
5553 break;
5554 case SCI_SCROLLTOSTART:
5555 ScrollTo(0);
5556 break;
5557 case SCI_SCROLLTOEND:
5558 ScrollTo(MaxScrollPos());
5559 break;
5561 return 0;
5564 int Editor::KeyDefault(int, int) {
5565 return 0;
5568 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
5569 DwellEnd(false);
5570 int msg = kmap.Find(key, modifiers);
5571 if (msg) {
5572 if (consumed)
5573 *consumed = true;
5574 return WndProc(msg, 0, 0);
5575 } else {
5576 if (consumed)
5577 *consumed = false;
5578 return KeyDefault(key, modifiers);
5582 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
5583 int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
5584 (alt ? SCI_ALT : 0);
5585 return KeyDownWithModifiers(key, modifiers, consumed);
5588 void Editor::SetWhitespaceVisible(int view) {
5589 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(view);
5592 int Editor::GetWhitespaceVisible() {
5593 return vs.viewWhitespace;
5596 void Editor::Indent(bool forwards) {
5597 for (size_t r=0; r<sel.Count(); r++) {
5598 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
5599 int caretPosition = sel.Range(r).caret.Position();
5600 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
5601 if (lineOfAnchor == lineCurrentPos) {
5602 if (forwards) {
5603 UndoGroup ug(pdoc);
5604 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
5605 caretPosition = sel.Range(r).caret.Position();
5606 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
5607 pdoc->tabIndents) {
5608 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5609 int indentationStep = pdoc->IndentSize();
5610 pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
5611 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5612 } else {
5613 if (pdoc->useTabs) {
5614 pdoc->InsertChar(caretPosition, '\t');
5615 sel.Range(r) = SelectionRange(caretPosition+1);
5616 } else {
5617 int numSpaces = (pdoc->tabInChars) -
5618 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
5619 if (numSpaces < 1)
5620 numSpaces = pdoc->tabInChars;
5621 for (int i = 0; i < numSpaces; i++) {
5622 pdoc->InsertChar(caretPosition + i, ' ');
5624 sel.Range(r) = SelectionRange(caretPosition+numSpaces);
5627 } else {
5628 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
5629 pdoc->tabIndents) {
5630 UndoGroup ug(pdoc);
5631 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5632 int indentationStep = pdoc->IndentSize();
5633 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
5634 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5635 } else {
5636 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
5637 pdoc->tabInChars;
5638 if (newColumn < 0)
5639 newColumn = 0;
5640 int newPos = caretPosition;
5641 while (pdoc->GetColumn(newPos) > newColumn)
5642 newPos--;
5643 sel.Range(r) = SelectionRange(newPos);
5646 } else { // Multiline
5647 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
5648 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
5649 // Multiple lines selected so indent / dedent
5650 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
5651 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
5652 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
5653 lineBottomSel--; // If not selecting any characters on a line, do not indent
5655 UndoGroup ug(pdoc);
5656 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
5658 if (lineOfAnchor < lineCurrentPos) {
5659 if (currentPosPosOnLine == 0)
5660 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5661 else
5662 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
5663 } else {
5664 if (anchorPosOnLine == 0)
5665 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5666 else
5667 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
5673 class CaseFolderASCII : public CaseFolderTable {
5674 public:
5675 CaseFolderASCII() {
5676 StandardASCII();
5678 ~CaseFolderASCII() {
5680 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
5681 if (lenMixed > sizeFolded) {
5682 return 0;
5683 } else {
5684 for (size_t i=0; i<lenMixed; i++) {
5685 folded[i] = mapping[static_cast<unsigned char>(mixed[i])];
5687 return lenMixed;
5693 CaseFolder *Editor::CaseFolderForEncoding() {
5694 // Simple default that only maps ASCII upper case to lower case.
5695 return new CaseFolderASCII();
5699 * Search of a text in the document, in the given range.
5700 * @return The position of the found text, -1 if not found.
5702 long Editor::FindText(
5703 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5704 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5705 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
5707 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
5708 int lengthFound = istrlen(ft->lpstrText);
5709 std::auto_ptr<CaseFolder> pcf(CaseFolderForEncoding());
5710 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
5711 (wParam & SCFIND_MATCHCASE) != 0,
5712 (wParam & SCFIND_WHOLEWORD) != 0,
5713 (wParam & SCFIND_WORDSTART) != 0,
5714 (wParam & SCFIND_REGEXP) != 0,
5715 wParam,
5716 &lengthFound,
5717 pcf.get());
5718 if (pos != -1) {
5719 ft->chrgText.cpMin = pos;
5720 ft->chrgText.cpMax = pos + lengthFound;
5722 return pos;
5726 * Relocatable search support : Searches relative to current selection
5727 * point and sets the selection to the found text range with
5728 * each search.
5731 * Anchor following searches at current selection start: This allows
5732 * multiple incremental interactive searches to be macro recorded
5733 * while still setting the selection to found text so the find/select
5734 * operation is self-contained.
5736 void Editor::SearchAnchor() {
5737 searchAnchor = SelectionStart().Position();
5741 * Find text from current search anchor: Must call @c SearchAnchor first.
5742 * Used for next text and previous text requests.
5743 * @return The position of the found text, -1 if not found.
5745 long Editor::SearchText(
5746 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
5747 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5748 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5749 sptr_t lParam) { ///< The text to search for.
5751 const char *txt = reinterpret_cast<char *>(lParam);
5752 int pos;
5753 int lengthFound = istrlen(txt);
5754 std::auto_ptr<CaseFolder> pcf(CaseFolderForEncoding());
5755 if (iMessage == SCI_SEARCHNEXT) {
5756 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
5757 (wParam & SCFIND_MATCHCASE) != 0,
5758 (wParam & SCFIND_WHOLEWORD) != 0,
5759 (wParam & SCFIND_WORDSTART) != 0,
5760 (wParam & SCFIND_REGEXP) != 0,
5761 wParam,
5762 &lengthFound,
5763 pcf.get());
5764 } else {
5765 pos = pdoc->FindText(searchAnchor, 0, txt,
5766 (wParam & SCFIND_MATCHCASE) != 0,
5767 (wParam & SCFIND_WHOLEWORD) != 0,
5768 (wParam & SCFIND_WORDSTART) != 0,
5769 (wParam & SCFIND_REGEXP) != 0,
5770 wParam,
5771 &lengthFound,
5772 pcf.get());
5774 if (pos != -1) {
5775 SetSelection(pos, pos + lengthFound);
5778 return pos;
5781 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
5782 std::string ret(s);
5783 for (size_t i=0; i<ret.size(); i++) {
5784 switch (caseMapping) {
5785 case cmUpper:
5786 if (ret[i] >= 'a' && ret[i] <= 'z')
5787 ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
5788 break;
5789 case cmLower:
5790 if (ret[i] >= 'A' && ret[i] <= 'Z')
5791 ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
5792 break;
5795 return ret;
5799 * Search for text in the target range of the document.
5800 * @return The position of the found text, -1 if not found.
5802 long Editor::SearchInTarget(const char *text, int length) {
5803 int lengthFound = length;
5805 std::auto_ptr<CaseFolder> pcf(CaseFolderForEncoding());
5806 int pos = pdoc->FindText(targetStart, targetEnd, text,
5807 (searchFlags & SCFIND_MATCHCASE) != 0,
5808 (searchFlags & SCFIND_WHOLEWORD) != 0,
5809 (searchFlags & SCFIND_WORDSTART) != 0,
5810 (searchFlags & SCFIND_REGEXP) != 0,
5811 searchFlags,
5812 &lengthFound,
5813 pcf.get());
5814 if (pos != -1) {
5815 targetStart = pos;
5816 targetEnd = pos + lengthFound;
5818 return pos;
5821 void Editor::GoToLine(int lineNo) {
5822 if (lineNo > pdoc->LinesTotal())
5823 lineNo = pdoc->LinesTotal();
5824 if (lineNo < 0)
5825 lineNo = 0;
5826 SetEmptySelection(pdoc->LineStart(lineNo));
5827 ShowCaretAtCurrentPosition();
5828 EnsureCaretVisible();
5831 static bool Close(Point pt1, Point pt2) {
5832 if (abs(pt1.x - pt2.x) > 3)
5833 return false;
5834 if (abs(pt1.y - pt2.y) > 3)
5835 return false;
5836 return true;
5839 char *Editor::CopyRange(int start, int end) {
5840 char *text = 0;
5841 if (start < end) {
5842 int len = end - start;
5843 text = new char[len + 1];
5844 for (int i = 0; i < len; i++) {
5845 text[i] = pdoc->CharAt(start + i);
5847 text[len] = '\0';
5849 return text;
5852 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
5853 if (sel.Empty()) {
5854 if (allowLineCopy) {
5855 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
5856 int start = pdoc->LineStart(currentLine);
5857 int end = pdoc->LineEnd(currentLine);
5859 char *text = CopyRange(start, end);
5860 size_t textLen = text ? strlen(text) : 0;
5861 // include room for \r\n\0
5862 textLen += 3;
5863 char *textWithEndl = new char[textLen];
5864 textWithEndl[0] = '\0';
5865 if (text)
5866 strncat(textWithEndl, text, textLen);
5867 if (pdoc->eolMode != SC_EOL_LF)
5868 strncat(textWithEndl, "\r", textLen);
5869 if (pdoc->eolMode != SC_EOL_CR)
5870 strncat(textWithEndl, "\n", textLen);
5871 ss->Set(textWithEndl, static_cast<int>(strlen(textWithEndl) + 1),
5872 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, true);
5873 delete []text;
5875 } else {
5876 int delimiterLength = 0;
5877 if (sel.selType == Selection::selRectangle) {
5878 if (pdoc->eolMode == SC_EOL_CRLF) {
5879 delimiterLength = 2;
5880 } else {
5881 delimiterLength = 1;
5884 size_t size = sel.Length() + delimiterLength * sel.Count();
5885 char *text = new char[size + 1];
5886 int j = 0;
5887 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
5888 if (sel.selType == Selection::selRectangle)
5889 std::sort(rangesInOrder.begin(), rangesInOrder.end());
5890 for (size_t r=0; r<rangesInOrder.size(); r++) {
5891 SelectionRange current = rangesInOrder[r];
5892 for (int i = current.Start().Position();
5893 i < current.End().Position();
5894 i++) {
5895 text[j++] = pdoc->CharAt(i);
5897 if (sel.selType == Selection::selRectangle) {
5898 if (pdoc->eolMode != SC_EOL_LF) {
5899 text[j++] = '\r';
5901 if (pdoc->eolMode != SC_EOL_CR) {
5902 text[j++] = '\n';
5906 text[size] = '\0';
5907 ss->Set(text, static_cast<int>(size + 1), pdoc->dbcsCodePage,
5908 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
5912 void Editor::CopyRangeToClipboard(int start, int end) {
5913 start = pdoc->ClampPositionIntoDocument(start);
5914 end = pdoc->ClampPositionIntoDocument(end);
5915 SelectionText selectedText;
5916 selectedText.Set(CopyRange(start, end), end - start + 1,
5917 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5918 CopyToClipboard(selectedText);
5921 void Editor::CopyText(int length, const char *text) {
5922 SelectionText selectedText;
5923 selectedText.Copy(text, length + 1,
5924 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5925 CopyToClipboard(selectedText);
5928 void Editor::SetDragPosition(SelectionPosition newPos) {
5929 if (newPos.Position() >= 0) {
5930 newPos = MovePositionOutsideChar(newPos, 1);
5931 posDrop = newPos;
5933 if (!(posDrag == newPos)) {
5934 caret.on = true;
5935 SetTicking(true);
5936 InvalidateCaret();
5937 posDrag = newPos;
5938 InvalidateCaret();
5942 void Editor::DisplayCursor(Window::Cursor c) {
5943 if (cursorMode == SC_CURSORNORMAL)
5944 wMain.SetCursor(c);
5945 else
5946 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
5949 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
5950 int xMove = ptStart.x - ptNow.x;
5951 int yMove = ptStart.y - ptNow.y;
5952 int distanceSquared = xMove * xMove + yMove * yMove;
5953 return distanceSquared > 16;
5956 void Editor::StartDrag() {
5957 // Always handled by subclasses
5958 //SetMouseCapture(true);
5959 //DisplayCursor(Window::cursorArrow);
5962 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
5963 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
5964 if (inDragDrop == ddDragging)
5965 dropWentOutside = false;
5967 bool positionWasInSelection = PositionInSelection(position.Position());
5969 bool positionOnEdgeOfSelection =
5970 (position == SelectionStart()) || (position == SelectionEnd());
5972 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
5973 (positionOnEdgeOfSelection && !moving)) {
5975 SelectionPosition selStart = SelectionStart();
5976 SelectionPosition selEnd = SelectionEnd();
5978 UndoGroup ug(pdoc);
5980 SelectionPosition positionAfterDeletion = position;
5981 if ((inDragDrop == ddDragging) && moving) {
5982 // Remove dragged out text
5983 if (rectangular || sel.selType == Selection::selLines) {
5984 for (size_t r=0; r<sel.Count(); r++) {
5985 if (position >= sel.Range(r).Start()) {
5986 if (position > sel.Range(r).End()) {
5987 positionAfterDeletion.Add(-sel.Range(r).Length());
5988 } else {
5989 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
5993 } else {
5994 if (position > selStart) {
5995 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
5998 ClearSelection();
6000 position = positionAfterDeletion;
6002 if (rectangular) {
6003 PasteRectangular(position, value, istrlen(value));
6004 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
6005 SetEmptySelection(position);
6006 } else {
6007 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
6008 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
6009 if (pdoc->InsertCString(position.Position(), value)) {
6010 SelectionPosition posAfterInsertion = position;
6011 posAfterInsertion.Add(istrlen(value));
6012 SetSelection(posAfterInsertion, position);
6015 } else if (inDragDrop == ddDragging) {
6016 SetEmptySelection(position);
6021 * @return true if given position is inside the selection,
6023 bool Editor::PositionInSelection(int pos) {
6024 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
6025 for (size_t r=0; r<sel.Count(); r++) {
6026 if (sel.Range(r).Contains(pos))
6027 return true;
6029 return false;
6032 bool Editor::PointInSelection(Point pt) {
6033 SelectionPosition pos = SPositionFromLocation(pt, false, true);
6034 Point ptPos = LocationFromPosition(pos);
6035 for (size_t r=0; r<sel.Count(); r++) {
6036 SelectionRange range = sel.Range(r);
6037 if (range.Contains(pos)) {
6038 bool hit = true;
6039 if (pos == range.Start()) {
6040 // see if just before selection
6041 if (pt.x < ptPos.x) {
6042 hit = false;
6045 if (pos == range.End()) {
6046 // see if just after selection
6047 if (pt.x > ptPos.x) {
6048 hit = false;
6051 if (hit)
6052 return true;
6055 return false;
6058 bool Editor::PointInSelMargin(Point pt) {
6059 // Really means: "Point in a margin"
6060 if (vs.fixedColumnWidth > 0) { // There is a margin
6061 PRectangle rcSelMargin = GetClientRectangle();
6062 rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth;
6063 return rcSelMargin.Contains(pt);
6064 } else {
6065 return false;
6069 Window::Cursor Editor::GetMarginCursor(Point pt) {
6070 int x = 0;
6071 for (int margin = 0; margin < ViewStyle::margins; margin++) {
6072 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
6073 return static_cast<Window::Cursor>(vs.ms[margin].cursor);
6074 x += vs.ms[margin].width;
6076 return Window::cursorReverseArrow;
6079 void Editor::LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine) {
6080 int selCurrentPos, selAnchorPos;
6081 if (wholeLine) {
6082 int lineCurrent_ = pdoc->LineFromPosition(lineCurrentPos_);
6083 int lineAnchor_ = pdoc->LineFromPosition(lineAnchorPos_);
6084 if (lineAnchorPos_ < lineCurrentPos_) {
6085 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
6086 selAnchorPos = pdoc->LineStart(lineAnchor_);
6087 } else if (lineAnchorPos_ > lineCurrentPos_) {
6088 selCurrentPos = pdoc->LineStart(lineCurrent_);
6089 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
6090 } else { // Same line, select it
6091 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
6092 selAnchorPos = pdoc->LineStart(lineAnchor_);
6094 } else {
6095 if (lineAnchorPos_ < lineCurrentPos_) {
6096 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
6097 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6098 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6099 } else if (lineAnchorPos_ > lineCurrentPos_) {
6100 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
6101 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6102 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
6103 } else { // Same line, select it
6104 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6105 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6106 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6109 SetSelection(selCurrentPos, selAnchorPos);
6112 void Editor::WordSelection(int pos) {
6113 if (pos < wordSelectAnchorStartPos) {
6114 // Extend backward to the word containing pos.
6115 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
6116 // This ensures that a series of empty lines isn't counted as a single "word".
6117 if (!pdoc->IsLineEndPosition(pos))
6118 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
6119 SetSelection(pos, wordSelectAnchorEndPos);
6120 } else if (pos > wordSelectAnchorEndPos) {
6121 // Extend forward to the word containing the character to the left of pos.
6122 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
6123 // This ensures that a series of empty lines isn't counted as a single "word".
6124 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
6125 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
6126 SetSelection(pos, wordSelectAnchorStartPos);
6127 } else {
6128 // Select only the anchored word
6129 if (pos >= originalAnchorPos)
6130 SetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
6131 else
6132 SetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
6136 void Editor::DwellEnd(bool mouseMoved) {
6137 if (mouseMoved)
6138 ticksToDwell = dwellDelay;
6139 else
6140 ticksToDwell = SC_TIME_FOREVER;
6141 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
6142 dwelling = false;
6143 NotifyDwelling(ptMouseLast, dwelling);
6147 void Editor::MouseLeave() {
6148 SetHotSpotRange(NULL);
6149 if (!HaveMouseCapture()) {
6150 ptMouseLast = Point(-1,-1);
6151 DwellEnd(true);
6155 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
6156 return ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0)
6157 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
6160 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
6161 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
6162 ptMouseLast = pt;
6163 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
6164 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6165 inDragDrop = ddNone;
6166 sel.SetMoveExtends(false);
6168 bool processed = NotifyMarginClick(pt, shift, ctrl, alt);
6169 if (processed)
6170 return;
6172 NotifyIndicatorClick(true, newPos.Position(), shift, ctrl, alt);
6174 bool inSelMargin = PointInSelMargin(pt);
6175 if (shift & !inSelMargin) {
6176 SetSelection(newPos.Position());
6178 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
6179 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
6180 SetMouseCapture(true);
6181 SetEmptySelection(newPos.Position());
6182 bool doubleClick = false;
6183 // Stop mouse button bounce changing selection type
6184 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
6185 if (selectionType == selChar) {
6186 selectionType = selWord;
6187 doubleClick = true;
6188 } else if (selectionType == selWord) {
6189 // Since we ended up here, we're inside a *triple* click, which should always select
6190 // whole line irregardless of word wrap being enabled or not.
6191 selectionType = selWholeLine;
6192 } else {
6193 if (inSelMargin) {
6194 // Selection type is either selSubLine or selWholeLine here and we're inside margin.
6195 // If it is selSubLine, we're inside a *double* click and word wrap is enabled,
6196 // so we switch to selWholeLine in order to select whole line.
6197 if (selectionType == selSubLine)
6198 selectionType = selWholeLine;
6199 } else {
6200 selectionType = selChar;
6201 originalAnchorPos = sel.MainCaret();
6206 if (selectionType == selWord) {
6207 int charPos = originalAnchorPos;
6208 if (sel.MainCaret() == originalAnchorPos) {
6209 charPos = PositionFromLocation(pt, false, true);
6210 charPos = MovePositionOutsideChar(charPos, -1);
6213 int startWord, endWord;
6214 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
6215 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
6216 endWord = pdoc->ExtendWordSelect(charPos, 1);
6217 } else {
6218 // Selecting backwards, or anchor beyond last character on line. In these cases,
6219 // we select the word containing the character to the *left* of the anchor.
6220 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
6221 startWord = pdoc->ExtendWordSelect(charPos, -1);
6222 endWord = pdoc->ExtendWordSelect(startWord, 1);
6223 } else {
6224 // Anchor at start of line; select nothing to begin with.
6225 startWord = charPos;
6226 endWord = charPos;
6230 wordSelectAnchorStartPos = startWord;
6231 wordSelectAnchorEndPos = endWord;
6232 wordSelectInitialCaretPos = sel.MainCaret();
6233 WordSelection(wordSelectInitialCaretPos);
6234 } else if (selectionType == selSubLine || selectionType == selWholeLine) {
6235 lineAnchorPos = newPos.Position();
6236 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6237 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
6238 } else {
6239 SetEmptySelection(sel.MainCaret());
6241 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
6242 if (doubleClick) {
6243 NotifyDoubleClick(pt, shift, ctrl, alt);
6244 if (PositionIsHotspot(newPos.Position()))
6245 NotifyHotSpotDoubleClicked(newPos.Position(), shift, ctrl, alt);
6247 } else { // Single click
6248 if (inSelMargin) {
6249 sel.selType = Selection::selStream;
6250 if (ctrl) {
6251 SelectAll();
6252 lastClickTime = curTime;
6253 return;
6255 if (!shift) {
6256 // Single click in margin: select whole line or only subline if word wrap is enabled
6257 lineAnchorPos = newPos.Position();
6258 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6259 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6260 } else {
6261 // Single shift+click in margin: select from line anchor to clicked line
6262 if (sel.MainAnchor() > sel.MainCaret())
6263 lineAnchorPos = sel.MainAnchor() - 1;
6264 else
6265 lineAnchorPos = sel.MainAnchor();
6266 // Reset selection type if there is an empty selection.
6267 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
6268 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
6269 // This ensures that we continue selecting in the same selection mode.
6270 if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
6271 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6272 LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
6275 SetDragPosition(SelectionPosition(invalidPosition));
6276 SetMouseCapture(true);
6277 } else {
6278 if (PointIsHotspot(pt)) {
6279 NotifyHotSpotClicked(newPos.Position(), shift, ctrl, alt);
6280 hotSpotClickPos = PositionFromLocation(pt,true,false);
6282 if (!shift) {
6283 if (PointInSelection(pt) && !SelectionEmpty())
6284 inDragDrop = ddInitial;
6285 else
6286 inDragDrop = ddNone;
6288 SetMouseCapture(true);
6289 if (inDragDrop != ddInitial) {
6290 SetDragPosition(SelectionPosition(invalidPosition));
6291 if (!shift) {
6292 if (ctrl && multipleSelection) {
6293 SelectionRange range(newPos);
6294 sel.TentativeSelection(range);
6295 InvalidateSelection(range, true);
6296 } else {
6297 InvalidateSelection(SelectionRange(newPos), true);
6298 if (sel.Count() > 1)
6299 Redraw();
6300 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
6301 sel.Clear();
6302 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6303 SetSelection(newPos, newPos);
6306 SelectionPosition anchorCurrent = newPos;
6307 if (shift)
6308 anchorCurrent = sel.IsRectangular() ?
6309 sel.Rectangular().anchor : sel.RangeMain().anchor;
6310 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6311 selectionType = selChar;
6312 originalAnchorPos = sel.MainCaret();
6313 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
6314 SetRectangularRange();
6318 lastClickTime = curTime;
6319 lastXChosen = pt.x + xOffset;
6320 ShowCaretAtCurrentPosition();
6323 bool Editor::PositionIsHotspot(int position) {
6324 return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
6327 bool Editor::PointIsHotspot(Point pt) {
6328 int pos = PositionFromLocation(pt, true);
6329 if (pos == INVALID_POSITION)
6330 return false;
6331 return PositionIsHotspot(pos);
6334 void Editor::SetHotSpotRange(Point *pt) {
6335 if (pt) {
6336 int pos = PositionFromLocation(*pt);
6338 // If we don't limit this to word characters then the
6339 // range can encompass more than the run range and then
6340 // the underline will not be drawn properly.
6341 int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
6342 int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
6344 // Only invalidate the range if the hotspot range has changed...
6345 if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
6346 if (hsStart != -1) {
6347 InvalidateRange(hsStart, hsEnd);
6349 hsStart = hsStart_;
6350 hsEnd = hsEnd_;
6351 InvalidateRange(hsStart, hsEnd);
6353 } else {
6354 if (hsStart != -1) {
6355 int hsStart_ = hsStart;
6356 int hsEnd_ = hsEnd;
6357 hsStart = -1;
6358 hsEnd = -1;
6359 InvalidateRange(hsStart_, hsEnd_);
6360 } else {
6361 hsStart = -1;
6362 hsEnd = -1;
6367 void Editor::GetHotSpotRange(int &hsStart_, int &hsEnd_) {
6368 hsStart_ = hsStart;
6369 hsEnd_ = hsEnd;
6372 void Editor::ButtonMove(Point pt) {
6373 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
6374 DwellEnd(true);
6377 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
6378 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6379 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
6381 if (inDragDrop == ddInitial) {
6382 if (DragThreshold(ptMouseLast, pt)) {
6383 SetMouseCapture(false);
6384 SetDragPosition(movePos);
6385 CopySelectionRange(&drag);
6386 StartDrag();
6388 return;
6391 ptMouseLast = pt;
6392 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
6393 if (HaveMouseCapture()) {
6395 // Slow down autoscrolling/selection
6396 autoScrollTimer.ticksToWait -= timer.tickSize;
6397 if (autoScrollTimer.ticksToWait > 0)
6398 return;
6399 autoScrollTimer.ticksToWait = autoScrollDelay;
6401 // Adjust selection
6402 if (posDrag.IsValid()) {
6403 SetDragPosition(movePos);
6404 } else {
6405 if (selectionType == selChar) {
6406 if (sel.IsRectangular()) {
6407 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
6408 SetSelection(movePos, sel.RangeMain().anchor);
6409 } else if (sel.Count() > 1) {
6410 SelectionRange range(movePos, sel.RangeMain().anchor);
6411 sel.TentativeSelection(range);
6412 InvalidateSelection(range, true);
6413 } else {
6414 SetSelection(movePos, sel.RangeMain().anchor);
6416 } else if (selectionType == selWord) {
6417 // Continue selecting by word
6418 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
6419 // No need to do anything. Previously this case was lumped
6420 // in with "Moved forward", but that can be harmful in this
6421 // case: a handler for the NotifyDoubleClick re-adjusts
6422 // the selection for a fancier definition of "word" (for
6423 // example, in Perl it is useful to include the leading
6424 // '$', '%' or '@' on variables for word selection). In this
6425 // the ButtonMove() called via Tick() for auto-scrolling
6426 // could result in the fancier word selection adjustment
6427 // being unmade.
6428 } else {
6429 wordSelectInitialCaretPos = -1;
6430 WordSelection(movePos.Position());
6432 } else {
6433 // Continue selecting by line
6434 LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
6438 // Autoscroll
6439 PRectangle rcClient = GetClientRectangle();
6440 int lineMove = DisplayFromPosition(movePos.Position());
6441 if (pt.y > rcClient.bottom) {
6442 ScrollTo(lineMove - LinesOnScreen() + 1);
6443 Redraw();
6444 } else if (pt.y < rcClient.top) {
6445 ScrollTo(lineMove);
6446 Redraw();
6448 EnsureCaretVisible(false, false, true);
6450 if (hsStart != -1 && !PositionIsHotspot(movePos.Position()))
6451 SetHotSpotRange(NULL);
6453 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,false) != hotSpotClickPos) {
6454 if (inDragDrop == ddNone) {
6455 DisplayCursor(Window::cursorText);
6457 hotSpotClickPos = INVALID_POSITION;
6460 } else {
6461 if (vs.fixedColumnWidth > 0) { // There is a margin
6462 if (PointInSelMargin(pt)) {
6463 DisplayCursor(GetMarginCursor(pt));
6464 SetHotSpotRange(NULL);
6465 return; // No need to test for selection
6468 // Display regular (drag) cursor over selection
6469 if (PointInSelection(pt) && !SelectionEmpty()) {
6470 DisplayCursor(Window::cursorArrow);
6471 } else if (PointIsHotspot(pt)) {
6472 DisplayCursor(Window::cursorHand);
6473 SetHotSpotRange(&pt);
6474 } else {
6475 DisplayCursor(Window::cursorText);
6476 SetHotSpotRange(NULL);
6481 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
6482 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
6483 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
6484 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6485 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6486 if (inDragDrop == ddInitial) {
6487 inDragDrop = ddNone;
6488 SetEmptySelection(newPos.Position());
6489 selectionType = selChar;
6490 originalAnchorPos = sel.MainCaret();
6492 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
6493 hotSpotClickPos = INVALID_POSITION;
6494 NotifyHotSpotReleaseClick(newPos.Position(), false, ctrl, false);
6496 if (HaveMouseCapture()) {
6497 if (PointInSelMargin(pt)) {
6498 DisplayCursor(GetMarginCursor(pt));
6499 } else {
6500 DisplayCursor(Window::cursorText);
6501 SetHotSpotRange(NULL);
6503 ptMouseLast = pt;
6504 SetMouseCapture(false);
6505 NotifyIndicatorClick(false, newPos.Position(), false, false, false);
6506 if (inDragDrop == ddDragging) {
6507 SelectionPosition selStart = SelectionStart();
6508 SelectionPosition selEnd = SelectionEnd();
6509 if (selStart < selEnd) {
6510 if (drag.len) {
6511 if (ctrl) {
6512 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6513 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6515 } else if (newPos < selStart) {
6516 pdoc->DeleteChars(selStart.Position(), drag.len);
6517 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6518 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6520 } else if (newPos > selEnd) {
6521 pdoc->DeleteChars(selStart.Position(), drag.len);
6522 newPos.Add(-drag.len);
6523 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6524 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6526 } else {
6527 SetEmptySelection(newPos.Position());
6529 drag.Free();
6531 selectionType = selChar;
6533 } else {
6534 if (selectionType == selChar) {
6535 if (sel.Count() > 1) {
6536 sel.RangeMain() =
6537 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
6538 InvalidateSelection(sel.RangeMain(), true);
6539 } else {
6540 SetSelection(newPos, sel.RangeMain().anchor);
6543 sel.CommitTentative();
6545 SetRectangularRange();
6546 lastClickTime = curTime;
6547 lastClick = pt;
6548 lastXChosen = pt.x + xOffset;
6549 if (sel.selType == Selection::selStream) {
6550 SetLastXChosen();
6552 inDragDrop = ddNone;
6553 EnsureCaretVisible(false);
6557 // Called frequently to perform background UI including
6558 // caret blinking and automatic scrolling.
6559 void Editor::Tick() {
6560 if (HaveMouseCapture()) {
6561 // Auto scroll
6562 ButtonMove(ptMouseLast);
6564 if (caret.period > 0) {
6565 timer.ticksToWait -= timer.tickSize;
6566 if (timer.ticksToWait <= 0) {
6567 caret.on = !caret.on;
6568 timer.ticksToWait = caret.period;
6569 if (caret.active) {
6570 InvalidateCaret();
6574 if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) {
6575 scrollWidth = lineWidthMaxSeen;
6576 SetScrollBars();
6578 if ((dwellDelay < SC_TIME_FOREVER) &&
6579 (ticksToDwell > 0) &&
6580 (!HaveMouseCapture()) &&
6581 (ptMouseLast.y >= 0)) {
6582 ticksToDwell -= timer.tickSize;
6583 if (ticksToDwell <= 0) {
6584 dwelling = true;
6585 NotifyDwelling(ptMouseLast, dwelling);
6590 bool Editor::Idle() {
6592 bool idleDone;
6594 bool wrappingDone = wrapState == eWrapNone;
6596 if (!wrappingDone) {
6597 // Wrap lines during idle.
6598 WrapLines(false, -1);
6599 // No more wrapping
6600 if (wrapStart == wrapEnd)
6601 wrappingDone = true;
6604 // Add more idle things to do here, but make sure idleDone is
6605 // set correctly before the function returns. returning
6606 // false will stop calling this idle funtion until SetIdle() is
6607 // called again.
6609 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
6611 return !idleDone;
6614 void Editor::SetFocusState(bool focusState) {
6615 hasFocus = focusState;
6616 NotifyFocus(hasFocus);
6617 if (hasFocus) {
6618 ShowCaretAtCurrentPosition();
6619 } else {
6620 CancelModes();
6621 DropCaret();
6625 int Editor::PositionAfterArea(PRectangle rcArea) {
6626 // The start of the document line after the display line after the area
6627 // This often means that the line after a modification is restyled which helps
6628 // detect multiline comment additions and heals single line comments
6629 int lineAfter = topLine + (rcArea.bottom - 1) / vs.lineHeight + 1;
6630 if (lineAfter < cs.LinesDisplayed())
6631 return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
6632 else
6633 return pdoc->Length();
6636 // Style to a position within the view. If this causes a change at end of last line then
6637 // affects later lines so style all the viewed text.
6638 void Editor::StyleToPositionInView(Position pos) {
6639 int endWindow = PositionAfterArea(GetClientRectangle());
6640 if (pos > endWindow)
6641 pos = endWindow;
6642 int styleAtEnd = pdoc->StyleAt(pos-1);
6643 pdoc->EnsureStyledTo(pos);
6644 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleAt(pos-1))) {
6645 // Style at end of line changed so is multi-line change like starting a comment
6646 // so require rest of window to be styled.
6647 pdoc->EnsureStyledTo(endWindow);
6651 void Editor::IdleStyling() {
6652 // Style the line after the modification as this allows modifications that change just the
6653 // line of the modification to heal instead of propagating to the rest of the window.
6654 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(styleNeeded.upTo) + 2));
6656 if (needUpdateUI) {
6657 NotifyUpdateUI();
6658 needUpdateUI = 0;
6660 styleNeeded.Reset();
6663 void Editor::QueueStyling(int upTo) {
6664 styleNeeded.NeedUpTo(upTo);
6667 bool Editor::PaintContains(PRectangle rc) {
6668 if (rc.Empty()) {
6669 return true;
6670 } else {
6671 return rcPaint.Contains(rc);
6675 bool Editor::PaintContainsMargin() {
6676 PRectangle rcSelMargin = GetClientRectangle();
6677 rcSelMargin.right = vs.fixedColumnWidth;
6678 return PaintContains(rcSelMargin);
6681 void Editor::CheckForChangeOutsidePaint(Range r) {
6682 if (paintState == painting && !paintingAllText) {
6683 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
6684 if (!r.Valid())
6685 return;
6687 PRectangle rcRange = RectangleFromRange(r.start, r.end);
6688 PRectangle rcText = GetTextRectangle();
6689 if (rcRange.top < rcText.top) {
6690 rcRange.top = rcText.top;
6692 if (rcRange.bottom > rcText.bottom) {
6693 rcRange.bottom = rcText.bottom;
6696 if (!PaintContains(rcRange)) {
6697 AbandonPaint();
6702 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
6703 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
6704 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
6705 CheckForChangeOutsidePaint(Range(braces[0]));
6706 CheckForChangeOutsidePaint(Range(pos0));
6707 braces[0] = pos0;
6709 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
6710 CheckForChangeOutsidePaint(Range(braces[1]));
6711 CheckForChangeOutsidePaint(Range(pos1));
6712 braces[1] = pos1;
6714 bracesMatchStyle = matchStyle;
6715 if (paintState == notPainting) {
6716 Redraw();
6721 void Editor::SetAnnotationHeights(int start, int end) {
6722 if (vs.annotationVisible) {
6723 for (int line=start; line<end; line++) {
6724 cs.SetHeight(line, pdoc->AnnotationLines(line) + 1);
6729 void Editor::SetDocPointer(Document *document) {
6730 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
6731 pdoc->RemoveWatcher(this, 0);
6732 pdoc->Release();
6733 if (document == NULL) {
6734 pdoc = new Document();
6735 } else {
6736 pdoc = document;
6738 pdoc->AddRef();
6740 // Ensure all positions within document
6741 sel.Clear();
6742 targetStart = 0;
6743 targetEnd = 0;
6745 braces[0] = invalidPosition;
6746 braces[1] = invalidPosition;
6748 // Reset the contraction state to fully shown.
6749 cs.Clear();
6750 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6751 SetAnnotationHeights(0, pdoc->LinesTotal());
6752 llc.Deallocate();
6753 NeedWrapping();
6755 pdoc->AddWatcher(this, 0);
6756 SetScrollBars();
6757 Redraw();
6760 void Editor::SetAnnotationVisible(int visible) {
6761 if (vs.annotationVisible != visible) {
6762 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
6763 vs.annotationVisible = visible;
6764 if (changedFromOrToHidden) {
6765 int dir = vs.annotationVisible ? 1 : -1;
6766 for (int line=0; line<pdoc->LinesTotal(); line++) {
6767 int annotationLines = pdoc->AnnotationLines(line);
6768 if (annotationLines > 0) {
6769 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
6773 Redraw();
6778 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
6780 void Editor::Expand(int &line, bool doExpand) {
6781 int lineMaxSubord = pdoc->GetLastChild(line);
6782 line++;
6783 while (line <= lineMaxSubord) {
6784 if (doExpand)
6785 cs.SetVisible(line, line, true);
6786 int level = pdoc->GetLevel(line);
6787 if (level & SC_FOLDLEVELHEADERFLAG) {
6788 if (doExpand && cs.GetExpanded(line)) {
6789 Expand(line, true);
6790 } else {
6791 Expand(line, false);
6793 } else {
6794 line++;
6799 void Editor::ToggleContraction(int line) {
6800 if (line >= 0) {
6801 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
6802 line = pdoc->GetFoldParent(line);
6803 if (line < 0)
6804 return;
6807 if (cs.GetExpanded(line)) {
6808 int lineMaxSubord = pdoc->GetLastChild(line);
6809 if (lineMaxSubord > line) {
6810 cs.SetExpanded(line, 0);
6811 cs.SetVisible(line + 1, lineMaxSubord, false);
6813 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
6814 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
6815 // This does not re-expand the fold
6816 EnsureCaretVisible();
6819 SetScrollBars();
6820 Redraw();
6823 } else {
6824 if (!(cs.GetVisible(line))) {
6825 EnsureLineVisible(line, false);
6826 GoToLine(line);
6828 cs.SetExpanded(line, 1);
6829 Expand(line, true);
6830 SetScrollBars();
6831 Redraw();
6836 int Editor::ContractedFoldNext(int lineStart) {
6837 for (int line = lineStart; line<pdoc->LinesTotal();) {
6838 if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
6839 return line;
6840 line = cs.ContractedNext(line+1);
6841 if (line < 0)
6842 return -1;
6845 return -1;
6849 * Recurse up from this line to find any folds that prevent this line from being visible
6850 * and unfold them all.
6852 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
6854 // In case in need of wrapping to ensure DisplayFromDoc works.
6855 WrapLines(true, -1);
6857 if (!cs.GetVisible(lineDoc)) {
6858 int lookLine = lineDoc;
6859 int lookLineLevel = pdoc->GetLevel(lookLine);
6860 while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
6861 lookLineLevel = pdoc->GetLevel(--lookLine);
6863 int lineParent = pdoc->GetFoldParent(lookLine);
6864 if (lineParent >= 0) {
6865 if (lineDoc != lineParent)
6866 EnsureLineVisible(lineParent, enforcePolicy);
6867 if (!cs.GetExpanded(lineParent)) {
6868 cs.SetExpanded(lineParent, 1);
6869 Expand(lineParent, true);
6872 SetScrollBars();
6873 Redraw();
6875 if (enforcePolicy) {
6876 int lineDisplay = cs.DisplayFromDoc(lineDoc);
6877 if (visiblePolicy & VISIBLE_SLOP) {
6878 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
6879 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
6880 SetVerticalScrollPos();
6881 Redraw();
6882 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
6883 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
6884 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
6885 SetVerticalScrollPos();
6886 Redraw();
6888 } else {
6889 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
6890 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
6891 SetVerticalScrollPos();
6892 Redraw();
6898 int Editor::GetTag(char *tagValue, int tagNumber) {
6899 char name[3] = "\\?";
6900 const char *text = 0;
6901 int length = 0;
6902 if ((tagNumber >= 1) && (tagNumber <= 9)) {
6903 name[1] = static_cast<char>(tagNumber + '0');
6904 length = 2;
6905 text = pdoc->SubstituteByPosition(name, &length);
6907 if (tagValue) {
6908 if (text)
6909 memcpy(tagValue, text, length + 1);
6910 else
6911 *tagValue = '\0';
6913 return length;
6916 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
6917 UndoGroup ug(pdoc);
6918 if (length == -1)
6919 length = istrlen(text);
6920 if (replacePatterns) {
6921 text = pdoc->SubstituteByPosition(text, &length);
6922 if (!text) {
6923 return 0;
6926 if (targetStart != targetEnd)
6927 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
6928 targetEnd = targetStart;
6929 pdoc->InsertString(targetStart, text, length);
6930 targetEnd = targetStart + length;
6931 return length;
6934 bool Editor::IsUnicodeMode() const {
6935 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
6938 int Editor::CodePage() const {
6939 if (pdoc)
6940 return pdoc->dbcsCodePage;
6941 else
6942 return 0;
6945 int Editor::WrapCount(int line) {
6946 AutoSurface surface(this);
6947 AutoLineLayout ll(llc, RetrieveLineLayout(line));
6949 if (surface && ll) {
6950 LayoutLine(line, surface, vs, ll, wrapWidth);
6951 return ll->lines;
6952 } else {
6953 return 1;
6957 void Editor::AddStyledText(char *buffer, int appendLength) {
6958 // The buffer consists of alternating character bytes and style bytes
6959 int textLength = appendLength / 2;
6960 char *text = new char[textLength];
6961 int i;
6962 for (i = 0; i < textLength; i++) {
6963 text[i] = buffer[i*2];
6965 pdoc->InsertString(CurrentPosition(), text, textLength);
6966 for (i = 0; i < textLength; i++) {
6967 text[i] = buffer[i*2+1];
6969 pdoc->StartStyling(CurrentPosition(), static_cast<char>(0xff));
6970 pdoc->SetStyles(textLength, text);
6971 delete []text;
6972 SetEmptySelection(sel.MainCaret() + textLength);
6975 static bool ValidMargin(unsigned long wParam) {
6976 return wParam < ViewStyle::margins;
6979 static char *CharPtrFromSPtr(sptr_t lParam) {
6980 return reinterpret_cast<char *>(lParam);
6983 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
6984 vs.EnsureStyle(wParam);
6985 switch (iMessage) {
6986 case SCI_STYLESETFORE:
6987 vs.styles[wParam].fore.desired = ColourDesired(lParam);
6988 break;
6989 case SCI_STYLESETBACK:
6990 vs.styles[wParam].back.desired = ColourDesired(lParam);
6991 break;
6992 case SCI_STYLESETBOLD:
6993 vs.styles[wParam].bold = lParam != 0;
6994 break;
6995 case SCI_STYLESETITALIC:
6996 vs.styles[wParam].italic = lParam != 0;
6997 break;
6998 case SCI_STYLESETEOLFILLED:
6999 vs.styles[wParam].eolFilled = lParam != 0;
7000 break;
7001 case SCI_STYLESETSIZE:
7002 vs.styles[wParam].size = lParam;
7003 break;
7004 case SCI_STYLESETFONT:
7005 if (lParam != 0) {
7006 vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam));
7008 break;
7009 case SCI_STYLESETUNDERLINE:
7010 vs.styles[wParam].underline = lParam != 0;
7011 break;
7012 case SCI_STYLESETCASE:
7013 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
7014 break;
7015 case SCI_STYLESETCHARACTERSET:
7016 vs.styles[wParam].characterSet = lParam;
7017 break;
7018 case SCI_STYLESETVISIBLE:
7019 vs.styles[wParam].visible = lParam != 0;
7020 break;
7021 case SCI_STYLESETCHANGEABLE:
7022 vs.styles[wParam].changeable = lParam != 0;
7023 break;
7024 case SCI_STYLESETHOTSPOT:
7025 vs.styles[wParam].hotspot = lParam != 0;
7026 break;
7028 InvalidateStyleRedraw();
7031 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7032 vs.EnsureStyle(wParam);
7033 switch (iMessage) {
7034 case SCI_STYLEGETFORE:
7035 return vs.styles[wParam].fore.desired.AsLong();
7036 case SCI_STYLEGETBACK:
7037 return vs.styles[wParam].back.desired.AsLong();
7038 case SCI_STYLEGETBOLD:
7039 return vs.styles[wParam].bold ? 1 : 0;
7040 case SCI_STYLEGETITALIC:
7041 return vs.styles[wParam].italic ? 1 : 0;
7042 case SCI_STYLEGETEOLFILLED:
7043 return vs.styles[wParam].eolFilled ? 1 : 0;
7044 case SCI_STYLEGETSIZE:
7045 return vs.styles[wParam].size;
7046 case SCI_STYLEGETFONT:
7047 if (!vs.styles[wParam].fontName)
7048 return 0;
7049 if (lParam != 0)
7050 strcpy(CharPtrFromSPtr(lParam), vs.styles[wParam].fontName);
7051 return strlen(vs.styles[wParam].fontName);
7052 case SCI_STYLEGETUNDERLINE:
7053 return vs.styles[wParam].underline ? 1 : 0;
7054 case SCI_STYLEGETCASE:
7055 return static_cast<int>(vs.styles[wParam].caseForce);
7056 case SCI_STYLEGETCHARACTERSET:
7057 return vs.styles[wParam].characterSet;
7058 case SCI_STYLEGETVISIBLE:
7059 return vs.styles[wParam].visible ? 1 : 0;
7060 case SCI_STYLEGETCHANGEABLE:
7061 return vs.styles[wParam].changeable ? 1 : 0;
7062 case SCI_STYLEGETHOTSPOT:
7063 return vs.styles[wParam].hotspot ? 1 : 0;
7065 return 0;
7068 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
7069 const size_t n = strlen(val);
7070 if (lParam != 0) {
7071 char *ptr = reinterpret_cast<char *>(lParam);
7072 strcpy(ptr, val);
7074 return n; // Not including NUL
7077 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7078 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
7080 // Optional macro recording hook
7081 if (recordingMacro)
7082 NotifyMacroRecord(iMessage, wParam, lParam);
7084 switch (iMessage) {
7086 case SCI_GETTEXT: {
7087 if (lParam == 0)
7088 return pdoc->Length() + 1;
7089 if (wParam == 0)
7090 return 0;
7091 char *ptr = CharPtrFromSPtr(lParam);
7092 unsigned int iChar = 0;
7093 for (; iChar < wParam - 1; iChar++)
7094 ptr[iChar] = pdoc->CharAt(iChar);
7095 ptr[iChar] = '\0';
7096 return iChar;
7099 case SCI_SETTEXT: {
7100 if (lParam == 0)
7101 return 0;
7102 UndoGroup ug(pdoc);
7103 pdoc->DeleteChars(0, pdoc->Length());
7104 SetEmptySelection(0);
7105 pdoc->InsertCString(0, CharPtrFromSPtr(lParam));
7106 return 1;
7109 case SCI_GETTEXTLENGTH:
7110 return pdoc->Length();
7112 case SCI_CUT:
7113 Cut();
7114 SetLastXChosen();
7115 break;
7117 case SCI_COPY:
7118 Copy();
7119 break;
7121 case SCI_COPYALLOWLINE:
7122 CopyAllowLine();
7123 break;
7125 case SCI_VERTICALCENTRECARET:
7126 VerticalCentreCaret();
7127 break;
7129 case SCI_MOVESELECTEDLINESUP:
7130 MoveSelectedLinesUp();
7131 break;
7133 case SCI_MOVESELECTEDLINESDOWN:
7134 MoveSelectedLinesDown();
7135 break;
7137 case SCI_COPYRANGE:
7138 CopyRangeToClipboard(wParam, lParam);
7139 break;
7141 case SCI_COPYTEXT:
7142 CopyText(wParam, CharPtrFromSPtr(lParam));
7143 break;
7145 case SCI_PASTE:
7146 Paste();
7147 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
7148 SetLastXChosen();
7150 EnsureCaretVisible();
7151 break;
7153 case SCI_CLEAR:
7154 Clear();
7155 SetLastXChosen();
7156 EnsureCaretVisible();
7157 break;
7159 case SCI_UNDO:
7160 Undo();
7161 SetLastXChosen();
7162 break;
7164 case SCI_CANUNDO:
7165 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
7167 case SCI_EMPTYUNDOBUFFER:
7168 pdoc->DeleteUndoHistory();
7169 return 0;
7171 case SCI_GETFIRSTVISIBLELINE:
7172 return topLine;
7174 case SCI_SETFIRSTVISIBLELINE:
7175 ScrollTo(wParam);
7176 break;
7178 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
7179 int lineStart = pdoc->LineStart(wParam);
7180 int lineEnd = pdoc->LineStart(wParam + 1);
7181 if (lParam == 0) {
7182 return lineEnd - lineStart;
7184 char *ptr = CharPtrFromSPtr(lParam);
7185 int iPlace = 0;
7186 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
7187 ptr[iPlace++] = pdoc->CharAt(iChar);
7189 return iPlace;
7192 case SCI_GETLINECOUNT:
7193 if (pdoc->LinesTotal() == 0)
7194 return 1;
7195 else
7196 return pdoc->LinesTotal();
7198 case SCI_GETMODIFY:
7199 return !pdoc->IsSavePoint();
7201 case SCI_SETSEL: {
7202 int nStart = static_cast<int>(wParam);
7203 int nEnd = static_cast<int>(lParam);
7204 if (nEnd < 0)
7205 nEnd = pdoc->Length();
7206 if (nStart < 0)
7207 nStart = nEnd; // Remove selection
7208 InvalidateSelection(SelectionRange(nStart, nEnd));
7209 sel.Clear();
7210 sel.selType = Selection::selStream;
7211 SetSelection(nEnd, nStart);
7212 EnsureCaretVisible();
7214 break;
7216 case SCI_GETSELTEXT: {
7217 SelectionText selectedText;
7218 CopySelectionRange(&selectedText);
7219 if (lParam == 0) {
7220 return selectedText.len ? selectedText.len : 1;
7221 } else {
7222 char *ptr = CharPtrFromSPtr(lParam);
7223 int iChar = 0;
7224 if (selectedText.len) {
7225 for (; iChar < selectedText.len; iChar++)
7226 ptr[iChar] = selectedText.s[iChar];
7227 } else {
7228 ptr[0] = '\0';
7230 return iChar;
7234 case SCI_LINEFROMPOSITION:
7235 if (static_cast<int>(wParam) < 0)
7236 return 0;
7237 return pdoc->LineFromPosition(wParam);
7239 case SCI_POSITIONFROMLINE:
7240 if (static_cast<int>(wParam) < 0)
7241 wParam = pdoc->LineFromPosition(SelectionStart().Position());
7242 if (wParam == 0)
7243 return 0; // Even if there is no text, there is a first line that starts at 0
7244 if (static_cast<int>(wParam) > pdoc->LinesTotal())
7245 return -1;
7246 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
7247 // return -1;
7248 return pdoc->LineStart(wParam);
7250 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
7251 case SCI_LINELENGTH:
7252 if ((static_cast<int>(wParam) < 0) ||
7253 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
7254 return 0;
7255 return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
7257 case SCI_REPLACESEL: {
7258 if (lParam == 0)
7259 return 0;
7260 UndoGroup ug(pdoc);
7261 ClearSelection();
7262 char *replacement = CharPtrFromSPtr(lParam);
7263 pdoc->InsertCString(sel.MainCaret(), replacement);
7264 SetEmptySelection(sel.MainCaret() + istrlen(replacement));
7265 EnsureCaretVisible();
7267 break;
7269 case SCI_SETTARGETSTART:
7270 targetStart = wParam;
7271 break;
7273 case SCI_GETTARGETSTART:
7274 return targetStart;
7276 case SCI_SETTARGETEND:
7277 targetEnd = wParam;
7278 break;
7280 case SCI_GETTARGETEND:
7281 return targetEnd;
7283 case SCI_TARGETFROMSELECTION:
7284 if (sel.MainCaret() < sel.MainAnchor()) {
7285 targetStart = sel.MainCaret();
7286 targetEnd = sel.MainAnchor();
7287 } else {
7288 targetStart = sel.MainAnchor();
7289 targetEnd = sel.MainCaret();
7291 break;
7293 case SCI_REPLACETARGET:
7294 PLATFORM_ASSERT(lParam);
7295 return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam);
7297 case SCI_REPLACETARGETRE:
7298 PLATFORM_ASSERT(lParam);
7299 return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam);
7301 case SCI_SEARCHINTARGET:
7302 PLATFORM_ASSERT(lParam);
7303 return SearchInTarget(CharPtrFromSPtr(lParam), wParam);
7305 case SCI_SETSEARCHFLAGS:
7306 searchFlags = wParam;
7307 break;
7309 case SCI_GETSEARCHFLAGS:
7310 return searchFlags;
7312 case SCI_GETTAG:
7313 return GetTag(CharPtrFromSPtr(lParam), wParam);
7315 case SCI_POSITIONBEFORE:
7316 return pdoc->MovePositionOutsideChar(wParam - 1, -1, true);
7318 case SCI_POSITIONAFTER:
7319 return pdoc->MovePositionOutsideChar(wParam + 1, 1, true);
7321 case SCI_LINESCROLL:
7322 ScrollTo(topLine + lParam);
7323 HorizontalScrollTo(xOffset + wParam * vs.spaceWidth);
7324 return 1;
7326 case SCI_SETXOFFSET:
7327 xOffset = wParam;
7328 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7329 SetHorizontalScrollPos();
7330 Redraw();
7331 break;
7333 case SCI_GETXOFFSET:
7334 return xOffset;
7336 case SCI_CHOOSECARETX:
7337 SetLastXChosen();
7338 break;
7340 case SCI_SCROLLCARET:
7341 EnsureCaretVisible();
7342 break;
7344 case SCI_SETREADONLY:
7345 pdoc->SetReadOnly(wParam != 0);
7346 return 1;
7348 case SCI_GETREADONLY:
7349 return pdoc->IsReadOnly();
7351 case SCI_CANPASTE:
7352 return CanPaste();
7354 case SCI_POINTXFROMPOSITION:
7355 if (lParam < 0) {
7356 return 0;
7357 } else {
7358 Point pt = LocationFromPosition(lParam);
7359 return pt.x;
7362 case SCI_POINTYFROMPOSITION:
7363 if (lParam < 0) {
7364 return 0;
7365 } else {
7366 Point pt = LocationFromPosition(lParam);
7367 return pt.y;
7370 case SCI_FINDTEXT:
7371 return FindText(wParam, lParam);
7373 case SCI_GETTEXTRANGE: {
7374 if (lParam == 0)
7375 return 0;
7376 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7377 int cpMax = tr->chrg.cpMax;
7378 if (cpMax == -1)
7379 cpMax = pdoc->Length();
7380 PLATFORM_ASSERT(cpMax <= pdoc->Length());
7381 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
7382 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
7383 // Spec says copied text is terminated with a NUL
7384 tr->lpstrText[len] = '\0';
7385 return len; // Not including NUL
7388 case SCI_HIDESELECTION:
7389 hideSelection = wParam != 0;
7390 Redraw();
7391 break;
7393 case SCI_FORMATRANGE:
7394 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
7396 case SCI_GETMARGINLEFT:
7397 return vs.leftMarginWidth;
7399 case SCI_GETMARGINRIGHT:
7400 return vs.rightMarginWidth;
7402 case SCI_SETMARGINLEFT:
7403 vs.leftMarginWidth = lParam;
7404 InvalidateStyleRedraw();
7405 break;
7407 case SCI_SETMARGINRIGHT:
7408 vs.rightMarginWidth = lParam;
7409 InvalidateStyleRedraw();
7410 break;
7412 // Control specific mesages
7414 case SCI_ADDTEXT: {
7415 if (lParam == 0)
7416 return 0;
7417 pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam);
7418 SetEmptySelection(sel.MainCaret() + wParam);
7419 return 0;
7422 case SCI_ADDSTYLEDTEXT:
7423 if (lParam)
7424 AddStyledText(CharPtrFromSPtr(lParam), wParam);
7425 return 0;
7427 case SCI_INSERTTEXT: {
7428 if (lParam == 0)
7429 return 0;
7430 int insertPos = wParam;
7431 if (static_cast<int>(wParam) == -1)
7432 insertPos = CurrentPosition();
7433 int newCurrent = CurrentPosition();
7434 char *sz = CharPtrFromSPtr(lParam);
7435 pdoc->InsertCString(insertPos, sz);
7436 if (newCurrent > insertPos)
7437 newCurrent += istrlen(sz);
7438 SetEmptySelection(newCurrent);
7439 return 0;
7442 case SCI_APPENDTEXT:
7443 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam);
7444 return 0;
7446 case SCI_CLEARALL:
7447 ClearAll();
7448 return 0;
7450 case SCI_CLEARDOCUMENTSTYLE:
7451 ClearDocumentStyle();
7452 return 0;
7454 case SCI_SETUNDOCOLLECTION:
7455 pdoc->SetUndoCollection(wParam != 0);
7456 return 0;
7458 case SCI_GETUNDOCOLLECTION:
7459 return pdoc->IsCollectingUndo();
7461 case SCI_BEGINUNDOACTION:
7462 pdoc->BeginUndoAction();
7463 return 0;
7465 case SCI_ENDUNDOACTION:
7466 pdoc->EndUndoAction();
7467 return 0;
7469 case SCI_GETCARETPERIOD:
7470 return caret.period;
7472 case SCI_SETCARETPERIOD:
7473 caret.period = wParam;
7474 break;
7476 case SCI_SETWORDCHARS: {
7477 pdoc->SetDefaultCharClasses(false);
7478 if (lParam == 0)
7479 return 0;
7480 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
7482 break;
7484 case SCI_SETWHITESPACECHARS: {
7485 if (lParam == 0)
7486 return 0;
7487 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
7489 break;
7491 case SCI_SETCHARSDEFAULT:
7492 pdoc->SetDefaultCharClasses(true);
7493 break;
7495 case SCI_GETLENGTH:
7496 return pdoc->Length();
7498 case SCI_ALLOCATE:
7499 pdoc->Allocate(wParam);
7500 break;
7502 case SCI_GETCHARAT:
7503 return pdoc->CharAt(wParam);
7505 case SCI_SETCURRENTPOS:
7506 if (sel.IsRectangular()) {
7507 sel.Rectangular().caret.SetPosition(wParam);
7508 SetRectangularRange();
7509 Redraw();
7510 } else {
7511 SetSelection(wParam, sel.MainAnchor());
7513 break;
7515 case SCI_GETCURRENTPOS:
7516 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
7518 case SCI_SETANCHOR:
7519 if (sel.IsRectangular()) {
7520 sel.Rectangular().anchor.SetPosition(wParam);
7521 SetRectangularRange();
7522 Redraw();
7523 } else {
7524 SetSelection(sel.MainCaret(), wParam);
7526 break;
7528 case SCI_GETANCHOR:
7529 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
7531 case SCI_SETSELECTIONSTART:
7532 SetSelection(Platform::Maximum(sel.MainCaret(), wParam), wParam);
7533 break;
7535 case SCI_GETSELECTIONSTART:
7536 return sel.LimitsForRectangularElseMain().start.Position();
7538 case SCI_SETSELECTIONEND:
7539 SetSelection(wParam, Platform::Minimum(sel.MainAnchor(), wParam));
7540 break;
7542 case SCI_GETSELECTIONEND:
7543 return sel.LimitsForRectangularElseMain().end.Position();
7545 case SCI_SETEMPTYSELECTION:
7546 SetEmptySelection(wParam);
7547 break;
7549 case SCI_SETPRINTMAGNIFICATION:
7550 printMagnification = wParam;
7551 break;
7553 case SCI_GETPRINTMAGNIFICATION:
7554 return printMagnification;
7556 case SCI_SETPRINTCOLOURMODE:
7557 printColourMode = wParam;
7558 break;
7560 case SCI_GETPRINTCOLOURMODE:
7561 return printColourMode;
7563 case SCI_SETPRINTWRAPMODE:
7564 printWrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
7565 break;
7567 case SCI_GETPRINTWRAPMODE:
7568 return printWrapState;
7570 case SCI_GETSTYLEAT:
7571 if (static_cast<int>(wParam) >= pdoc->Length())
7572 return 0;
7573 else
7574 return pdoc->StyleAt(wParam);
7576 case SCI_REDO:
7577 Redo();
7578 break;
7580 case SCI_SELECTALL:
7581 SelectAll();
7582 break;
7584 case SCI_SETSAVEPOINT:
7585 pdoc->SetSavePoint();
7586 break;
7588 case SCI_GETSTYLEDTEXT: {
7589 if (lParam == 0)
7590 return 0;
7591 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7592 int iPlace = 0;
7593 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
7594 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
7595 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
7597 tr->lpstrText[iPlace] = '\0';
7598 tr->lpstrText[iPlace + 1] = '\0';
7599 return iPlace;
7602 case SCI_CANREDO:
7603 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
7605 case SCI_MARKERLINEFROMHANDLE:
7606 return pdoc->LineFromHandle(wParam);
7608 case SCI_MARKERDELETEHANDLE:
7609 pdoc->DeleteMarkFromHandle(wParam);
7610 break;
7612 case SCI_GETVIEWWS:
7613 return vs.viewWhitespace;
7615 case SCI_SETVIEWWS:
7616 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
7617 Redraw();
7618 break;
7620 case SCI_GETWHITESPACESIZE:
7621 return vs.whitespaceSize;
7623 case SCI_SETWHITESPACESIZE:
7624 vs.whitespaceSize = static_cast<int>(wParam);
7625 Redraw();
7626 break;
7628 case SCI_POSITIONFROMPOINT:
7629 return PositionFromLocation(Point(wParam, lParam), false, false);
7631 case SCI_POSITIONFROMPOINTCLOSE:
7632 return PositionFromLocation(Point(wParam, lParam), true, false);
7634 case SCI_CHARPOSITIONFROMPOINT:
7635 return PositionFromLocation(Point(wParam, lParam), false, true);
7637 case SCI_CHARPOSITIONFROMPOINTCLOSE:
7638 return PositionFromLocation(Point(wParam, lParam), true, true);
7640 case SCI_GOTOLINE:
7641 GoToLine(wParam);
7642 break;
7644 case SCI_GOTOPOS:
7645 SetEmptySelection(wParam);
7646 EnsureCaretVisible();
7647 break;
7649 case SCI_GETCURLINE: {
7650 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
7651 int lineStart = pdoc->LineStart(lineCurrentPos);
7652 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
7653 if (lParam == 0) {
7654 return 1 + lineEnd - lineStart;
7656 PLATFORM_ASSERT(wParam > 0);
7657 char *ptr = CharPtrFromSPtr(lParam);
7658 unsigned int iPlace = 0;
7659 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
7660 ptr[iPlace++] = pdoc->CharAt(iChar);
7662 ptr[iPlace] = '\0';
7663 return sel.MainCaret() - lineStart;
7666 case SCI_GETENDSTYLED:
7667 return pdoc->GetEndStyled();
7669 case SCI_GETEOLMODE:
7670 return pdoc->eolMode;
7672 case SCI_SETEOLMODE:
7673 pdoc->eolMode = wParam;
7674 break;
7676 case SCI_STARTSTYLING:
7677 pdoc->StartStyling(wParam, static_cast<char>(lParam));
7678 break;
7680 case SCI_SETSTYLING:
7681 pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
7682 break;
7684 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
7685 if (lParam == 0)
7686 return 0;
7687 pdoc->SetStyles(wParam, CharPtrFromSPtr(lParam));
7688 break;
7690 case SCI_SETBUFFEREDDRAW:
7691 bufferedDraw = wParam != 0;
7692 break;
7694 case SCI_GETBUFFEREDDRAW:
7695 return bufferedDraw;
7697 case SCI_GETTWOPHASEDRAW:
7698 return twoPhaseDraw;
7700 case SCI_SETTWOPHASEDRAW:
7701 twoPhaseDraw = wParam != 0;
7702 InvalidateStyleRedraw();
7703 break;
7705 case SCI_SETFONTQUALITY:
7706 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
7707 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
7708 InvalidateStyleRedraw();
7709 break;
7711 case SCI_GETFONTQUALITY:
7712 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
7714 case SCI_SETTABWIDTH:
7715 if (wParam > 0) {
7716 pdoc->tabInChars = wParam;
7717 if (pdoc->indentInChars == 0)
7718 pdoc->actualIndentInChars = pdoc->tabInChars;
7720 InvalidateStyleRedraw();
7721 break;
7723 case SCI_GETTABWIDTH:
7724 return pdoc->tabInChars;
7726 case SCI_SETINDENT:
7727 pdoc->indentInChars = wParam;
7728 if (pdoc->indentInChars != 0)
7729 pdoc->actualIndentInChars = pdoc->indentInChars;
7730 else
7731 pdoc->actualIndentInChars = pdoc->tabInChars;
7732 InvalidateStyleRedraw();
7733 break;
7735 case SCI_GETINDENT:
7736 return pdoc->indentInChars;
7738 case SCI_SETUSETABS:
7739 pdoc->useTabs = wParam != 0;
7740 InvalidateStyleRedraw();
7741 break;
7743 case SCI_GETUSETABS:
7744 return pdoc->useTabs;
7746 case SCI_SETLINEINDENTATION:
7747 pdoc->SetLineIndentation(wParam, lParam);
7748 break;
7750 case SCI_GETLINEINDENTATION:
7751 return pdoc->GetLineIndentation(wParam);
7753 case SCI_GETLINEINDENTPOSITION:
7754 return pdoc->GetLineIndentPosition(wParam);
7756 case SCI_SETTABINDENTS:
7757 pdoc->tabIndents = wParam != 0;
7758 break;
7760 case SCI_GETTABINDENTS:
7761 return pdoc->tabIndents;
7763 case SCI_SETBACKSPACEUNINDENTS:
7764 pdoc->backspaceUnindents = wParam != 0;
7765 break;
7767 case SCI_GETBACKSPACEUNINDENTS:
7768 return pdoc->backspaceUnindents;
7770 case SCI_SETMOUSEDWELLTIME:
7771 dwellDelay = wParam;
7772 ticksToDwell = dwellDelay;
7773 break;
7775 case SCI_GETMOUSEDWELLTIME:
7776 return dwellDelay;
7778 case SCI_WORDSTARTPOSITION:
7779 return pdoc->ExtendWordSelect(wParam, -1, lParam != 0);
7781 case SCI_WORDENDPOSITION:
7782 return pdoc->ExtendWordSelect(wParam, 1, lParam != 0);
7784 case SCI_SETWRAPMODE:
7785 switch (wParam) {
7786 case SC_WRAP_WORD:
7787 wrapState = eWrapWord;
7788 break;
7789 case SC_WRAP_CHAR:
7790 wrapState = eWrapChar;
7791 break;
7792 default:
7793 wrapState = eWrapNone;
7794 break;
7796 xOffset = 0;
7797 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7798 InvalidateStyleRedraw();
7799 ReconfigureScrollBars();
7800 break;
7802 case SCI_GETWRAPMODE:
7803 return wrapState;
7805 case SCI_SETWRAPVISUALFLAGS:
7806 if (wrapVisualFlags != static_cast<int>(wParam)) {
7807 wrapVisualFlags = wParam;
7808 InvalidateStyleRedraw();
7809 ReconfigureScrollBars();
7811 break;
7813 case SCI_GETWRAPVISUALFLAGS:
7814 return wrapVisualFlags;
7816 case SCI_SETWRAPVISUALFLAGSLOCATION:
7817 wrapVisualFlagsLocation = wParam;
7818 InvalidateStyleRedraw();
7819 break;
7821 case SCI_GETWRAPVISUALFLAGSLOCATION:
7822 return wrapVisualFlagsLocation;
7824 case SCI_SETWRAPSTARTINDENT:
7825 if (wrapVisualStartIndent != static_cast<int>(wParam)) {
7826 wrapVisualStartIndent = wParam;
7827 InvalidateStyleRedraw();
7828 ReconfigureScrollBars();
7830 break;
7832 case SCI_GETWRAPSTARTINDENT:
7833 return wrapVisualStartIndent;
7835 case SCI_SETWRAPINDENTMODE:
7836 if (wrapIndentMode != static_cast<int>(wParam)) {
7837 wrapIndentMode = wParam;
7838 InvalidateStyleRedraw();
7839 ReconfigureScrollBars();
7841 break;
7843 case SCI_GETWRAPINDENTMODE:
7844 return wrapIndentMode;
7846 case SCI_SETLAYOUTCACHE:
7847 llc.SetLevel(wParam);
7848 break;
7850 case SCI_GETLAYOUTCACHE:
7851 return llc.GetLevel();
7853 case SCI_SETPOSITIONCACHE:
7854 posCache.SetSize(wParam);
7855 break;
7857 case SCI_GETPOSITIONCACHE:
7858 return posCache.GetSize();
7860 case SCI_SETSCROLLWIDTH:
7861 PLATFORM_ASSERT(wParam > 0);
7862 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
7863 lineWidthMaxSeen = 0;
7864 scrollWidth = wParam;
7865 SetScrollBars();
7867 break;
7869 case SCI_GETSCROLLWIDTH:
7870 return scrollWidth;
7872 case SCI_SETSCROLLWIDTHTRACKING:
7873 trackLineWidth = wParam != 0;
7874 break;
7876 case SCI_GETSCROLLWIDTHTRACKING:
7877 return trackLineWidth;
7879 case SCI_LINESJOIN:
7880 LinesJoin();
7881 break;
7883 case SCI_LINESSPLIT:
7884 LinesSplit(wParam);
7885 break;
7887 case SCI_TEXTWIDTH:
7888 PLATFORM_ASSERT(wParam < vs.stylesSize);
7889 PLATFORM_ASSERT(lParam);
7890 return TextWidth(wParam, CharPtrFromSPtr(lParam));
7892 case SCI_TEXTHEIGHT:
7893 return vs.lineHeight;
7895 case SCI_SETENDATLASTLINE:
7896 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
7897 if (endAtLastLine != (wParam != 0)) {
7898 endAtLastLine = wParam != 0;
7899 SetScrollBars();
7901 break;
7903 case SCI_GETENDATLASTLINE:
7904 return endAtLastLine;
7906 case SCI_SETCARETSTICKY:
7907 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
7908 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
7909 caretSticky = wParam;
7911 break;
7913 case SCI_GETCARETSTICKY:
7914 return caretSticky;
7916 case SCI_TOGGLECARETSTICKY:
7917 caretSticky = !caretSticky;
7918 break;
7920 case SCI_GETCOLUMN:
7921 return pdoc->GetColumn(wParam);
7923 case SCI_FINDCOLUMN:
7924 return pdoc->FindColumn(wParam, lParam);
7926 case SCI_SETHSCROLLBAR :
7927 if (horizontalScrollBarVisible != (wParam != 0)) {
7928 horizontalScrollBarVisible = wParam != 0;
7929 SetScrollBars();
7930 ReconfigureScrollBars();
7932 break;
7934 case SCI_GETHSCROLLBAR:
7935 return horizontalScrollBarVisible;
7937 case SCI_SETVSCROLLBAR:
7938 if (verticalScrollBarVisible != (wParam != 0)) {
7939 verticalScrollBarVisible = wParam != 0;
7940 SetScrollBars();
7941 ReconfigureScrollBars();
7943 break;
7945 case SCI_GETVSCROLLBAR:
7946 return verticalScrollBarVisible;
7948 case SCI_SETINDENTATIONGUIDES:
7949 vs.viewIndentationGuides = IndentView(wParam);
7950 Redraw();
7951 break;
7953 case SCI_GETINDENTATIONGUIDES:
7954 return vs.viewIndentationGuides;
7956 case SCI_SETHIGHLIGHTGUIDE:
7957 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
7958 highlightGuideColumn = wParam;
7959 Redraw();
7961 break;
7963 case SCI_GETHIGHLIGHTGUIDE:
7964 return highlightGuideColumn;
7966 case SCI_GETLINEENDPOSITION:
7967 return pdoc->LineEnd(wParam);
7969 case SCI_SETCODEPAGE:
7970 if (ValidCodePage(wParam)) {
7971 pdoc->dbcsCodePage = wParam;
7972 InvalidateStyleRedraw();
7974 break;
7976 case SCI_GETCODEPAGE:
7977 return pdoc->dbcsCodePage;
7979 case SCI_SETUSEPALETTE:
7980 palette.allowRealization = wParam != 0;
7981 InvalidateStyleRedraw();
7982 break;
7984 case SCI_GETUSEPALETTE:
7985 return palette.allowRealization;
7987 // Marker definition and setting
7988 case SCI_MARKERDEFINE:
7989 if (wParam <= MARKER_MAX)
7990 vs.markers[wParam].markType = lParam;
7991 InvalidateStyleData();
7992 RedrawSelMargin();
7993 break;
7995 case SCI_MARKERSYMBOLDEFINED:
7996 if (wParam <= MARKER_MAX)
7997 return vs.markers[wParam].markType;
7998 else
7999 return 0;
8001 case SCI_MARKERSETFORE:
8002 if (wParam <= MARKER_MAX)
8003 vs.markers[wParam].fore.desired = ColourDesired(lParam);
8004 InvalidateStyleData();
8005 RedrawSelMargin();
8006 break;
8007 case SCI_MARKERSETBACKSELECTED:
8008 if (wParam <= MARKER_MAX)
8009 vs.markers[wParam].backSelected.desired = ColourDesired(lParam);
8010 InvalidateStyleRedraw();
8011 break;
8012 case SCI_MARKERENABLEHIGHLIGHT:
8013 highlightDelimiter.isEnabled = wParam == 1;
8014 InvalidateStyleRedraw();
8015 break;
8016 case SCI_MARKERSETBACK:
8017 if (wParam <= MARKER_MAX)
8018 vs.markers[wParam].back.desired = ColourDesired(lParam);
8019 InvalidateStyleData();
8020 RedrawSelMargin();
8021 break;
8022 case SCI_MARKERSETALPHA:
8023 if (wParam <= MARKER_MAX)
8024 vs.markers[wParam].alpha = lParam;
8025 InvalidateStyleRedraw();
8026 break;
8027 case SCI_MARKERADD: {
8028 int markerID = pdoc->AddMark(wParam, lParam);
8029 return markerID;
8031 case SCI_MARKERADDSET:
8032 if (lParam != 0)
8033 pdoc->AddMarkSet(wParam, lParam);
8034 break;
8036 case SCI_MARKERDELETE:
8037 pdoc->DeleteMark(wParam, lParam);
8038 break;
8040 case SCI_MARKERDELETEALL:
8041 pdoc->DeleteAllMarks(static_cast<int>(wParam));
8042 break;
8044 case SCI_MARKERGET:
8045 return pdoc->GetMark(wParam);
8047 case SCI_MARKERNEXT: {
8048 int lt = pdoc->LinesTotal();
8049 for (int iLine = wParam; iLine < lt; iLine++) {
8050 if ((pdoc->GetMark(iLine) & lParam) != 0)
8051 return iLine;
8054 return -1;
8056 case SCI_MARKERPREVIOUS: {
8057 for (int iLine = wParam; iLine >= 0; iLine--) {
8058 if ((pdoc->GetMark(iLine) & lParam) != 0)
8059 return iLine;
8062 return -1;
8064 case SCI_MARKERDEFINEPIXMAP:
8065 if (wParam <= MARKER_MAX) {
8066 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
8068 InvalidateStyleData();
8069 RedrawSelMargin();
8070 break;
8072 case SCI_RGBAIMAGESETWIDTH:
8073 sizeRGBAImage.x = wParam;
8074 break;
8076 case SCI_RGBAIMAGESETHEIGHT:
8077 sizeRGBAImage.y = wParam;
8078 break;
8080 case SCI_MARKERDEFINERGBAIMAGE:
8081 if (wParam <= MARKER_MAX) {
8082 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, reinterpret_cast<unsigned char *>(lParam));
8084 InvalidateStyleData();
8085 RedrawSelMargin();
8086 break;
8088 case SCI_SETMARGINTYPEN:
8089 if (ValidMargin(wParam)) {
8090 vs.ms[wParam].style = lParam;
8091 InvalidateStyleRedraw();
8093 break;
8095 case SCI_GETMARGINTYPEN:
8096 if (ValidMargin(wParam))
8097 return vs.ms[wParam].style;
8098 else
8099 return 0;
8101 case SCI_SETMARGINWIDTHN:
8102 if (ValidMargin(wParam)) {
8103 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
8104 if (vs.ms[wParam].width != lParam) {
8105 vs.ms[wParam].width = lParam;
8106 InvalidateStyleRedraw();
8109 break;
8111 case SCI_GETMARGINWIDTHN:
8112 if (ValidMargin(wParam))
8113 return vs.ms[wParam].width;
8114 else
8115 return 0;
8117 case SCI_SETMARGINMASKN:
8118 if (ValidMargin(wParam)) {
8119 vs.ms[wParam].mask = lParam;
8120 InvalidateStyleRedraw();
8122 break;
8124 case SCI_GETMARGINMASKN:
8125 if (ValidMargin(wParam))
8126 return vs.ms[wParam].mask;
8127 else
8128 return 0;
8130 case SCI_SETMARGINSENSITIVEN:
8131 if (ValidMargin(wParam)) {
8132 vs.ms[wParam].sensitive = lParam != 0;
8133 InvalidateStyleRedraw();
8135 break;
8137 case SCI_GETMARGINSENSITIVEN:
8138 if (ValidMargin(wParam))
8139 return vs.ms[wParam].sensitive ? 1 : 0;
8140 else
8141 return 0;
8143 case SCI_SETMARGINCURSORN:
8144 if (ValidMargin(wParam))
8145 vs.ms[wParam].cursor = lParam;
8146 break;
8148 case SCI_GETMARGINCURSORN:
8149 if (ValidMargin(wParam))
8150 return vs.ms[wParam].cursor;
8151 else
8152 return 0;
8154 case SCI_STYLECLEARALL:
8155 vs.ClearStyles();
8156 InvalidateStyleRedraw();
8157 break;
8159 case SCI_STYLESETFORE:
8160 case SCI_STYLESETBACK:
8161 case SCI_STYLESETBOLD:
8162 case SCI_STYLESETITALIC:
8163 case SCI_STYLESETEOLFILLED:
8164 case SCI_STYLESETSIZE:
8165 case SCI_STYLESETFONT:
8166 case SCI_STYLESETUNDERLINE:
8167 case SCI_STYLESETCASE:
8168 case SCI_STYLESETCHARACTERSET:
8169 case SCI_STYLESETVISIBLE:
8170 case SCI_STYLESETCHANGEABLE:
8171 case SCI_STYLESETHOTSPOT:
8172 StyleSetMessage(iMessage, wParam, lParam);
8173 break;
8175 case SCI_STYLEGETFORE:
8176 case SCI_STYLEGETBACK:
8177 case SCI_STYLEGETBOLD:
8178 case SCI_STYLEGETITALIC:
8179 case SCI_STYLEGETEOLFILLED:
8180 case SCI_STYLEGETSIZE:
8181 case SCI_STYLEGETFONT:
8182 case SCI_STYLEGETUNDERLINE:
8183 case SCI_STYLEGETCASE:
8184 case SCI_STYLEGETCHARACTERSET:
8185 case SCI_STYLEGETVISIBLE:
8186 case SCI_STYLEGETCHANGEABLE:
8187 case SCI_STYLEGETHOTSPOT:
8188 return StyleGetMessage(iMessage, wParam, lParam);
8190 case SCI_STYLERESETDEFAULT:
8191 vs.ResetDefaultStyle();
8192 InvalidateStyleRedraw();
8193 break;
8194 case SCI_SETSTYLEBITS:
8195 vs.EnsureStyle((1 << wParam) - 1);
8196 pdoc->SetStylingBits(wParam);
8197 break;
8199 case SCI_GETSTYLEBITS:
8200 return pdoc->stylingBits;
8202 case SCI_SETLINESTATE:
8203 return pdoc->SetLineState(wParam, lParam);
8205 case SCI_GETLINESTATE:
8206 return pdoc->GetLineState(wParam);
8208 case SCI_GETMAXLINESTATE:
8209 return pdoc->GetMaxLineState();
8211 case SCI_GETCARETLINEVISIBLE:
8212 return vs.showCaretLineBackground;
8213 case SCI_SETCARETLINEVISIBLE:
8214 vs.showCaretLineBackground = wParam != 0;
8215 InvalidateStyleRedraw();
8216 break;
8217 case SCI_GETCARETLINEBACK:
8218 return vs.caretLineBackground.desired.AsLong();
8219 case SCI_SETCARETLINEBACK:
8220 vs.caretLineBackground.desired = wParam;
8221 InvalidateStyleRedraw();
8222 break;
8223 case SCI_GETCARETLINEBACKALPHA:
8224 return vs.caretLineAlpha;
8225 case SCI_SETCARETLINEBACKALPHA:
8226 vs.caretLineAlpha = wParam;
8227 InvalidateStyleRedraw();
8228 break;
8230 // Folding messages
8232 case SCI_VISIBLEFROMDOCLINE:
8233 return cs.DisplayFromDoc(wParam);
8235 case SCI_DOCLINEFROMVISIBLE:
8236 return cs.DocFromDisplay(wParam);
8238 case SCI_WRAPCOUNT:
8239 return WrapCount(wParam);
8241 case SCI_SETFOLDLEVEL: {
8242 int prev = pdoc->SetLevel(wParam, lParam);
8243 if (prev != lParam)
8244 RedrawSelMargin();
8245 return prev;
8248 case SCI_GETFOLDLEVEL:
8249 return pdoc->GetLevel(wParam);
8251 case SCI_GETLASTCHILD:
8252 return pdoc->GetLastChild(wParam, lParam);
8254 case SCI_GETFOLDPARENT:
8255 return pdoc->GetFoldParent(wParam);
8257 case SCI_SHOWLINES:
8258 cs.SetVisible(wParam, lParam, true);
8259 SetScrollBars();
8260 Redraw();
8261 break;
8263 case SCI_HIDELINES:
8264 if (wParam > 0)
8265 cs.SetVisible(wParam, lParam, false);
8266 SetScrollBars();
8267 Redraw();
8268 break;
8270 case SCI_GETLINEVISIBLE:
8271 return cs.GetVisible(wParam);
8273 case SCI_SETFOLDEXPANDED:
8274 if (cs.SetExpanded(wParam, lParam != 0)) {
8275 RedrawSelMargin();
8277 break;
8279 case SCI_GETFOLDEXPANDED:
8280 return cs.GetExpanded(wParam);
8282 case SCI_SETFOLDFLAGS:
8283 foldFlags = wParam;
8284 Redraw();
8285 break;
8287 case SCI_TOGGLEFOLD:
8288 ToggleContraction(wParam);
8289 break;
8291 case SCI_CONTRACTEDFOLDNEXT:
8292 return ContractedFoldNext(wParam);
8294 case SCI_ENSUREVISIBLE:
8295 EnsureLineVisible(wParam, false);
8296 break;
8298 case SCI_ENSUREVISIBLEENFORCEPOLICY:
8299 EnsureLineVisible(wParam, true);
8300 break;
8302 case SCI_SEARCHANCHOR:
8303 SearchAnchor();
8304 break;
8306 case SCI_SEARCHNEXT:
8307 case SCI_SEARCHPREV:
8308 return SearchText(iMessage, wParam, lParam);
8310 case SCI_SETXCARETPOLICY:
8311 caretXPolicy = wParam;
8312 caretXSlop = lParam;
8313 break;
8315 case SCI_SETYCARETPOLICY:
8316 caretYPolicy = wParam;
8317 caretYSlop = lParam;
8318 break;
8320 case SCI_SETVISIBLEPOLICY:
8321 visiblePolicy = wParam;
8322 visibleSlop = lParam;
8323 break;
8325 case SCI_LINESONSCREEN:
8326 return LinesOnScreen();
8328 case SCI_SETSELFORE:
8329 vs.selforeset = wParam != 0;
8330 vs.selforeground.desired = ColourDesired(lParam);
8331 vs.selAdditionalForeground.desired = ColourDesired(lParam);
8332 InvalidateStyleRedraw();
8333 break;
8335 case SCI_SETSELBACK:
8336 vs.selbackset = wParam != 0;
8337 vs.selbackground.desired = ColourDesired(lParam);
8338 vs.selAdditionalBackground.desired = ColourDesired(lParam);
8339 InvalidateStyleRedraw();
8340 break;
8342 case SCI_SETSELALPHA:
8343 vs.selAlpha = wParam;
8344 vs.selAdditionalAlpha = wParam;
8345 InvalidateStyleRedraw();
8346 break;
8348 case SCI_GETSELALPHA:
8349 return vs.selAlpha;
8351 case SCI_GETSELEOLFILLED:
8352 return vs.selEOLFilled;
8354 case SCI_SETSELEOLFILLED:
8355 vs.selEOLFilled = wParam != 0;
8356 InvalidateStyleRedraw();
8357 break;
8359 case SCI_SETWHITESPACEFORE:
8360 vs.whitespaceForegroundSet = wParam != 0;
8361 vs.whitespaceForeground.desired = ColourDesired(lParam);
8362 InvalidateStyleRedraw();
8363 break;
8365 case SCI_SETWHITESPACEBACK:
8366 vs.whitespaceBackgroundSet = wParam != 0;
8367 vs.whitespaceBackground.desired = ColourDesired(lParam);
8368 InvalidateStyleRedraw();
8369 break;
8371 case SCI_SETCARETFORE:
8372 vs.caretcolour.desired = ColourDesired(wParam);
8373 InvalidateStyleRedraw();
8374 break;
8376 case SCI_GETCARETFORE:
8377 return vs.caretcolour.desired.AsLong();
8379 case SCI_SETCARETSTYLE:
8380 if (wParam <= CARETSTYLE_BLOCK)
8381 vs.caretStyle = wParam;
8382 else
8383 /* Default to the line caret */
8384 vs.caretStyle = CARETSTYLE_LINE;
8385 InvalidateStyleRedraw();
8386 break;
8388 case SCI_GETCARETSTYLE:
8389 return vs.caretStyle;
8391 case SCI_SETCARETWIDTH:
8392 if (static_cast<int>(wParam) <= 0)
8393 vs.caretWidth = 0;
8394 else if (wParam >= 3)
8395 vs.caretWidth = 3;
8396 else
8397 vs.caretWidth = wParam;
8398 InvalidateStyleRedraw();
8399 break;
8401 case SCI_GETCARETWIDTH:
8402 return vs.caretWidth;
8404 case SCI_ASSIGNCMDKEY:
8405 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8406 Platform::HighShortFromLong(wParam), lParam);
8407 break;
8409 case SCI_CLEARCMDKEY:
8410 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8411 Platform::HighShortFromLong(wParam), SCI_NULL);
8412 break;
8414 case SCI_CLEARALLCMDKEYS:
8415 kmap.Clear();
8416 break;
8418 case SCI_INDICSETSTYLE:
8419 if (wParam <= INDIC_MAX) {
8420 vs.indicators[wParam].style = lParam;
8421 InvalidateStyleRedraw();
8423 break;
8425 case SCI_INDICGETSTYLE:
8426 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
8428 case SCI_INDICSETFORE:
8429 if (wParam <= INDIC_MAX) {
8430 vs.indicators[wParam].fore.desired = ColourDesired(lParam);
8431 InvalidateStyleRedraw();
8433 break;
8435 case SCI_INDICGETFORE:
8436 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.desired.AsLong() : 0;
8438 case SCI_INDICSETUNDER:
8439 if (wParam <= INDIC_MAX) {
8440 vs.indicators[wParam].under = lParam != 0;
8441 InvalidateStyleRedraw();
8443 break;
8445 case SCI_INDICGETUNDER:
8446 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
8448 case SCI_INDICSETALPHA:
8449 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8450 vs.indicators[wParam].fillAlpha = lParam;
8451 InvalidateStyleRedraw();
8453 break;
8455 case SCI_INDICGETALPHA:
8456 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
8458 case SCI_INDICSETOUTLINEALPHA:
8459 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8460 vs.indicators[wParam].outlineAlpha = lParam;
8461 InvalidateStyleRedraw();
8463 break;
8465 case SCI_INDICGETOUTLINEALPHA:
8466 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
8468 case SCI_SETINDICATORCURRENT:
8469 pdoc->decorations.SetCurrentIndicator(wParam);
8470 break;
8471 case SCI_GETINDICATORCURRENT:
8472 return pdoc->decorations.GetCurrentIndicator();
8473 case SCI_SETINDICATORVALUE:
8474 pdoc->decorations.SetCurrentValue(wParam);
8475 break;
8476 case SCI_GETINDICATORVALUE:
8477 return pdoc->decorations.GetCurrentValue();
8479 case SCI_INDICATORFILLRANGE:
8480 pdoc->DecorationFillRange(wParam, pdoc->decorations.GetCurrentValue(), lParam);
8481 break;
8483 case SCI_INDICATORCLEARRANGE:
8484 pdoc->DecorationFillRange(wParam, 0, lParam);
8485 break;
8487 case SCI_INDICATORALLONFOR:
8488 return pdoc->decorations.AllOnFor(wParam);
8490 case SCI_INDICATORVALUEAT:
8491 return pdoc->decorations.ValueAt(wParam, lParam);
8493 case SCI_INDICATORSTART:
8494 return pdoc->decorations.Start(wParam, lParam);
8496 case SCI_INDICATOREND:
8497 return pdoc->decorations.End(wParam, lParam);
8499 case SCI_LINEDOWN:
8500 case SCI_LINEDOWNEXTEND:
8501 case SCI_PARADOWN:
8502 case SCI_PARADOWNEXTEND:
8503 case SCI_LINEUP:
8504 case SCI_LINEUPEXTEND:
8505 case SCI_PARAUP:
8506 case SCI_PARAUPEXTEND:
8507 case SCI_CHARLEFT:
8508 case SCI_CHARLEFTEXTEND:
8509 case SCI_CHARRIGHT:
8510 case SCI_CHARRIGHTEXTEND:
8511 case SCI_WORDLEFT:
8512 case SCI_WORDLEFTEXTEND:
8513 case SCI_WORDRIGHT:
8514 case SCI_WORDRIGHTEXTEND:
8515 case SCI_WORDLEFTEND:
8516 case SCI_WORDLEFTENDEXTEND:
8517 case SCI_WORDRIGHTEND:
8518 case SCI_WORDRIGHTENDEXTEND:
8519 case SCI_HOME:
8520 case SCI_HOMEEXTEND:
8521 case SCI_LINEEND:
8522 case SCI_LINEENDEXTEND:
8523 case SCI_HOMEWRAP:
8524 case SCI_HOMEWRAPEXTEND:
8525 case SCI_LINEENDWRAP:
8526 case SCI_LINEENDWRAPEXTEND:
8527 case SCI_DOCUMENTSTART:
8528 case SCI_DOCUMENTSTARTEXTEND:
8529 case SCI_DOCUMENTEND:
8530 case SCI_DOCUMENTENDEXTEND:
8531 case SCI_SCROLLTOSTART:
8532 case SCI_SCROLLTOEND:
8534 case SCI_STUTTEREDPAGEUP:
8535 case SCI_STUTTEREDPAGEUPEXTEND:
8536 case SCI_STUTTEREDPAGEDOWN:
8537 case SCI_STUTTEREDPAGEDOWNEXTEND:
8539 case SCI_PAGEUP:
8540 case SCI_PAGEUPEXTEND:
8541 case SCI_PAGEDOWN:
8542 case SCI_PAGEDOWNEXTEND:
8543 case SCI_EDITTOGGLEOVERTYPE:
8544 case SCI_CANCEL:
8545 case SCI_DELETEBACK:
8546 case SCI_TAB:
8547 case SCI_BACKTAB:
8548 case SCI_NEWLINE:
8549 case SCI_FORMFEED:
8550 case SCI_VCHOME:
8551 case SCI_VCHOMEEXTEND:
8552 case SCI_VCHOMEWRAP:
8553 case SCI_VCHOMEWRAPEXTEND:
8554 case SCI_ZOOMIN:
8555 case SCI_ZOOMOUT:
8556 case SCI_DELWORDLEFT:
8557 case SCI_DELWORDRIGHT:
8558 case SCI_DELWORDRIGHTEND:
8559 case SCI_DELLINELEFT:
8560 case SCI_DELLINERIGHT:
8561 case SCI_LINECOPY:
8562 case SCI_LINECUT:
8563 case SCI_LINEDELETE:
8564 case SCI_LINETRANSPOSE:
8565 case SCI_LINEDUPLICATE:
8566 case SCI_LOWERCASE:
8567 case SCI_UPPERCASE:
8568 case SCI_LINESCROLLDOWN:
8569 case SCI_LINESCROLLUP:
8570 case SCI_WORDPARTLEFT:
8571 case SCI_WORDPARTLEFTEXTEND:
8572 case SCI_WORDPARTRIGHT:
8573 case SCI_WORDPARTRIGHTEXTEND:
8574 case SCI_DELETEBACKNOTLINE:
8575 case SCI_HOMEDISPLAY:
8576 case SCI_HOMEDISPLAYEXTEND:
8577 case SCI_LINEENDDISPLAY:
8578 case SCI_LINEENDDISPLAYEXTEND:
8579 case SCI_LINEDOWNRECTEXTEND:
8580 case SCI_LINEUPRECTEXTEND:
8581 case SCI_CHARLEFTRECTEXTEND:
8582 case SCI_CHARRIGHTRECTEXTEND:
8583 case SCI_HOMERECTEXTEND:
8584 case SCI_VCHOMERECTEXTEND:
8585 case SCI_LINEENDRECTEXTEND:
8586 case SCI_PAGEUPRECTEXTEND:
8587 case SCI_PAGEDOWNRECTEXTEND:
8588 case SCI_SELECTIONDUPLICATE:
8589 return KeyCommand(iMessage);
8591 case SCI_BRACEHIGHLIGHT:
8592 SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
8593 break;
8595 case SCI_BRACEHIGHLIGHTINDICATOR:
8596 if (lParam >= 0 && lParam <= INDIC_MAX) {
8597 vs.braceHighlightIndicatorSet = wParam != 0;
8598 vs.braceHighlightIndicator = lParam;
8600 break;
8602 case SCI_BRACEBADLIGHT:
8603 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
8604 break;
8606 case SCI_BRACEBADLIGHTINDICATOR:
8607 if (lParam >= 0 && lParam <= INDIC_MAX) {
8608 vs.braceBadLightIndicatorSet = wParam != 0;
8609 vs.braceBadLightIndicator = lParam;
8611 break;
8613 case SCI_BRACEMATCH:
8614 // wParam is position of char to find brace for,
8615 // lParam is maximum amount of text to restyle to find it
8616 return pdoc->BraceMatch(wParam, lParam);
8618 case SCI_GETVIEWEOL:
8619 return vs.viewEOL;
8621 case SCI_SETVIEWEOL:
8622 vs.viewEOL = wParam != 0;
8623 InvalidateStyleRedraw();
8624 break;
8626 case SCI_SETZOOM:
8627 vs.zoomLevel = wParam;
8628 InvalidateStyleRedraw();
8629 NotifyZoom();
8630 break;
8632 case SCI_GETZOOM:
8633 return vs.zoomLevel;
8635 case SCI_GETEDGECOLUMN:
8636 return theEdge;
8638 case SCI_SETEDGECOLUMN:
8639 theEdge = wParam;
8640 InvalidateStyleRedraw();
8641 break;
8643 case SCI_GETEDGEMODE:
8644 return vs.edgeState;
8646 case SCI_SETEDGEMODE:
8647 vs.edgeState = wParam;
8648 InvalidateStyleRedraw();
8649 break;
8651 case SCI_GETEDGECOLOUR:
8652 return vs.edgecolour.desired.AsLong();
8654 case SCI_SETEDGECOLOUR:
8655 vs.edgecolour.desired = ColourDesired(wParam);
8656 InvalidateStyleRedraw();
8657 break;
8659 case SCI_GETDOCPOINTER:
8660 return reinterpret_cast<sptr_t>(pdoc);
8662 case SCI_SETDOCPOINTER:
8663 CancelModes();
8664 SetDocPointer(reinterpret_cast<Document *>(lParam));
8665 return 0;
8667 case SCI_CREATEDOCUMENT: {
8668 Document *doc = new Document();
8669 if (doc) {
8670 doc->AddRef();
8672 return reinterpret_cast<sptr_t>(doc);
8675 case SCI_ADDREFDOCUMENT:
8676 (reinterpret_cast<Document *>(lParam))->AddRef();
8677 break;
8679 case SCI_RELEASEDOCUMENT:
8680 (reinterpret_cast<Document *>(lParam))->Release();
8681 break;
8683 case SCI_SETMODEVENTMASK:
8684 modEventMask = wParam;
8685 return 0;
8687 case SCI_GETMODEVENTMASK:
8688 return modEventMask;
8690 case SCI_CONVERTEOLS:
8691 pdoc->ConvertLineEnds(wParam);
8692 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
8693 return 0;
8695 case SCI_SETLENGTHFORENCODE:
8696 lengthForEncode = wParam;
8697 return 0;
8699 case SCI_SELECTIONISRECTANGLE:
8700 return sel.selType == Selection::selRectangle ? 1 : 0;
8702 case SCI_SETSELECTIONMODE: {
8703 switch (wParam) {
8704 case SC_SEL_STREAM:
8705 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
8706 sel.selType = Selection::selStream;
8707 break;
8708 case SC_SEL_RECTANGLE:
8709 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
8710 sel.selType = Selection::selRectangle;
8711 break;
8712 case SC_SEL_LINES:
8713 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
8714 sel.selType = Selection::selLines;
8715 break;
8716 case SC_SEL_THIN:
8717 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
8718 sel.selType = Selection::selThin;
8719 break;
8720 default:
8721 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
8722 sel.selType = Selection::selStream;
8724 InvalidateSelection(sel.RangeMain(), true);
8726 case SCI_GETSELECTIONMODE:
8727 switch (sel.selType) {
8728 case Selection::selStream:
8729 return SC_SEL_STREAM;
8730 case Selection::selRectangle:
8731 return SC_SEL_RECTANGLE;
8732 case Selection::selLines:
8733 return SC_SEL_LINES;
8734 case Selection::selThin:
8735 return SC_SEL_THIN;
8736 default: // ?!
8737 return SC_SEL_STREAM;
8739 case SCI_GETLINESELSTARTPOSITION:
8740 case SCI_GETLINESELENDPOSITION: {
8741 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(wParam)),
8742 SelectionPosition(pdoc->LineEnd(wParam)));
8743 for (size_t r=0; r<sel.Count(); r++) {
8744 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
8745 if (portion.start.IsValid()) {
8746 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
8749 return INVALID_POSITION;
8752 case SCI_SETOVERTYPE:
8753 inOverstrike = wParam != 0;
8754 break;
8756 case SCI_GETOVERTYPE:
8757 return inOverstrike ? 1 : 0;
8759 case SCI_SETFOCUS:
8760 SetFocusState(wParam != 0);
8761 break;
8763 case SCI_GETFOCUS:
8764 return hasFocus;
8766 case SCI_SETSTATUS:
8767 errorStatus = wParam;
8768 break;
8770 case SCI_GETSTATUS:
8771 return errorStatus;
8773 case SCI_SETMOUSEDOWNCAPTURES:
8774 mouseDownCaptures = wParam != 0;
8775 break;
8777 case SCI_GETMOUSEDOWNCAPTURES:
8778 return mouseDownCaptures;
8780 case SCI_SETCURSOR:
8781 cursorMode = wParam;
8782 DisplayCursor(Window::cursorText);
8783 break;
8785 case SCI_GETCURSOR:
8786 return cursorMode;
8788 case SCI_SETCONTROLCHARSYMBOL:
8789 controlCharSymbol = wParam;
8790 break;
8792 case SCI_GETCONTROLCHARSYMBOL:
8793 return controlCharSymbol;
8795 case SCI_STARTRECORD:
8796 recordingMacro = true;
8797 return 0;
8799 case SCI_STOPRECORD:
8800 recordingMacro = false;
8801 return 0;
8803 case SCI_MOVECARETINSIDEVIEW:
8804 MoveCaretInsideView();
8805 break;
8807 case SCI_SETFOLDMARGINCOLOUR:
8808 vs.foldmarginColourSet = wParam != 0;
8809 vs.foldmarginColour.desired = ColourDesired(lParam);
8810 InvalidateStyleRedraw();
8811 break;
8813 case SCI_SETFOLDMARGINHICOLOUR:
8814 vs.foldmarginHighlightColourSet = wParam != 0;
8815 vs.foldmarginHighlightColour.desired = ColourDesired(lParam);
8816 InvalidateStyleRedraw();
8817 break;
8819 case SCI_SETHOTSPOTACTIVEFORE:
8820 vs.hotspotForegroundSet = wParam != 0;
8821 vs.hotspotForeground.desired = ColourDesired(lParam);
8822 InvalidateStyleRedraw();
8823 break;
8825 case SCI_GETHOTSPOTACTIVEFORE:
8826 return vs.hotspotForeground.desired.AsLong();
8828 case SCI_SETHOTSPOTACTIVEBACK:
8829 vs.hotspotBackgroundSet = wParam != 0;
8830 vs.hotspotBackground.desired = ColourDesired(lParam);
8831 InvalidateStyleRedraw();
8832 break;
8834 case SCI_GETHOTSPOTACTIVEBACK:
8835 return vs.hotspotBackground.desired.AsLong();
8837 case SCI_SETHOTSPOTACTIVEUNDERLINE:
8838 vs.hotspotUnderline = wParam != 0;
8839 InvalidateStyleRedraw();
8840 break;
8842 case SCI_GETHOTSPOTACTIVEUNDERLINE:
8843 return vs.hotspotUnderline ? 1 : 0;
8845 case SCI_SETHOTSPOTSINGLELINE:
8846 vs.hotspotSingleLine = wParam != 0;
8847 InvalidateStyleRedraw();
8848 break;
8850 case SCI_GETHOTSPOTSINGLELINE:
8851 return vs.hotspotSingleLine ? 1 : 0;
8853 case SCI_SETPASTECONVERTENDINGS:
8854 convertPastes = wParam != 0;
8855 break;
8857 case SCI_GETPASTECONVERTENDINGS:
8858 return convertPastes ? 1 : 0;
8860 case SCI_GETCHARACTERPOINTER:
8861 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
8863 case SCI_SETEXTRAASCENT:
8864 vs.extraAscent = wParam;
8865 InvalidateStyleRedraw();
8866 break;
8868 case SCI_GETEXTRAASCENT:
8869 return vs.extraAscent;
8871 case SCI_SETEXTRADESCENT:
8872 vs.extraDescent = wParam;
8873 InvalidateStyleRedraw();
8874 break;
8876 case SCI_GETEXTRADESCENT:
8877 return vs.extraDescent;
8879 case SCI_MARGINSETSTYLEOFFSET:
8880 vs.marginStyleOffset = wParam;
8881 InvalidateStyleRedraw();
8882 break;
8884 case SCI_MARGINGETSTYLEOFFSET:
8885 return vs.marginStyleOffset;
8887 case SCI_SETMARGINOPTIONS:
8888 marginOptions = wParam;
8889 break;
8891 case SCI_GETMARGINOPTIONS:
8892 return marginOptions;
8894 case SCI_MARGINSETTEXT:
8895 pdoc->MarginSetText(wParam, CharPtrFromSPtr(lParam));
8896 break;
8898 case SCI_MARGINGETTEXT: {
8899 const StyledText st = pdoc->MarginStyledText(wParam);
8900 if (lParam) {
8901 if (st.text)
8902 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
8903 else
8904 strcpy(CharPtrFromSPtr(lParam), "");
8906 return st.length;
8909 case SCI_MARGINSETSTYLE:
8910 pdoc->MarginSetStyle(wParam, lParam);
8911 break;
8913 case SCI_MARGINGETSTYLE: {
8914 const StyledText st = pdoc->MarginStyledText(wParam);
8915 return st.style;
8918 case SCI_MARGINSETSTYLES:
8919 pdoc->MarginSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
8920 break;
8922 case SCI_MARGINGETSTYLES: {
8923 const StyledText st = pdoc->MarginStyledText(wParam);
8924 if (lParam) {
8925 if (st.styles)
8926 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
8927 else
8928 strcpy(CharPtrFromSPtr(lParam), "");
8930 return st.styles ? st.length : 0;
8933 case SCI_MARGINTEXTCLEARALL:
8934 pdoc->MarginClearAll();
8935 break;
8937 case SCI_ANNOTATIONSETTEXT:
8938 pdoc->AnnotationSetText(wParam, CharPtrFromSPtr(lParam));
8939 break;
8941 case SCI_ANNOTATIONGETTEXT: {
8942 const StyledText st = pdoc->AnnotationStyledText(wParam);
8943 if (lParam) {
8944 if (st.text)
8945 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
8946 else
8947 strcpy(CharPtrFromSPtr(lParam), "");
8949 return st.length;
8952 case SCI_ANNOTATIONGETSTYLE: {
8953 const StyledText st = pdoc->AnnotationStyledText(wParam);
8954 return st.style;
8957 case SCI_ANNOTATIONSETSTYLE:
8958 pdoc->AnnotationSetStyle(wParam, lParam);
8959 break;
8961 case SCI_ANNOTATIONSETSTYLES:
8962 pdoc->AnnotationSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
8963 break;
8965 case SCI_ANNOTATIONGETSTYLES: {
8966 const StyledText st = pdoc->AnnotationStyledText(wParam);
8967 if (lParam) {
8968 if (st.styles)
8969 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
8970 else
8971 strcpy(CharPtrFromSPtr(lParam), "");
8973 return st.styles ? st.length : 0;
8976 case SCI_ANNOTATIONGETLINES:
8977 return pdoc->AnnotationLines(wParam);
8979 case SCI_ANNOTATIONCLEARALL:
8980 pdoc->AnnotationClearAll();
8981 break;
8983 case SCI_ANNOTATIONSETVISIBLE:
8984 SetAnnotationVisible(wParam);
8985 break;
8987 case SCI_ANNOTATIONGETVISIBLE:
8988 return vs.annotationVisible;
8990 case SCI_ANNOTATIONSETSTYLEOFFSET:
8991 vs.annotationStyleOffset = wParam;
8992 InvalidateStyleRedraw();
8993 break;
8995 case SCI_ANNOTATIONGETSTYLEOFFSET:
8996 return vs.annotationStyleOffset;
8998 case SCI_ADDUNDOACTION:
8999 pdoc->AddUndoAction(wParam, lParam & UNDO_MAY_COALESCE);
9000 break;
9002 case SCI_SETMULTIPLESELECTION:
9003 multipleSelection = wParam != 0;
9004 InvalidateCaret();
9005 break;
9007 case SCI_GETMULTIPLESELECTION:
9008 return multipleSelection;
9010 case SCI_SETADDITIONALSELECTIONTYPING:
9011 additionalSelectionTyping = wParam != 0;
9012 InvalidateCaret();
9013 break;
9015 case SCI_GETADDITIONALSELECTIONTYPING:
9016 return additionalSelectionTyping;
9018 case SCI_SETMULTIPASTE:
9019 multiPasteMode = wParam;
9020 break;
9022 case SCI_GETMULTIPASTE:
9023 return multiPasteMode;
9025 case SCI_SETADDITIONALCARETSBLINK:
9026 additionalCaretsBlink = wParam != 0;
9027 InvalidateCaret();
9028 break;
9030 case SCI_GETADDITIONALCARETSBLINK:
9031 return additionalCaretsBlink;
9033 case SCI_SETADDITIONALCARETSVISIBLE:
9034 additionalCaretsVisible = wParam != 0;
9035 InvalidateCaret();
9036 break;
9038 case SCI_GETADDITIONALCARETSVISIBLE:
9039 return additionalCaretsVisible;
9041 case SCI_GETSELECTIONS:
9042 return sel.Count();
9044 case SCI_CLEARSELECTIONS:
9045 sel.Clear();
9046 Redraw();
9047 break;
9049 case SCI_SETSELECTION:
9050 sel.SetSelection(SelectionRange(wParam, lParam));
9051 Redraw();
9052 break;
9054 case SCI_ADDSELECTION:
9055 sel.AddSelection(SelectionRange(wParam, lParam));
9056 Redraw();
9057 break;
9059 case SCI_SETMAINSELECTION:
9060 sel.SetMain(wParam);
9061 Redraw();
9062 break;
9064 case SCI_GETMAINSELECTION:
9065 return sel.Main();
9067 case SCI_SETSELECTIONNCARET:
9068 sel.Range(wParam).caret.SetPosition(lParam);
9069 Redraw();
9070 break;
9072 case SCI_GETSELECTIONNCARET:
9073 return sel.Range(wParam).caret.Position();
9075 case SCI_SETSELECTIONNANCHOR:
9076 sel.Range(wParam).anchor.SetPosition(lParam);
9077 Redraw();
9078 break;
9079 case SCI_GETSELECTIONNANCHOR:
9080 return sel.Range(wParam).anchor.Position();
9082 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
9083 sel.Range(wParam).caret.SetVirtualSpace(lParam);
9084 Redraw();
9085 break;
9087 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
9088 return sel.Range(wParam).caret.VirtualSpace();
9090 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
9091 sel.Range(wParam).anchor.SetVirtualSpace(lParam);
9092 Redraw();
9093 break;
9095 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
9096 return sel.Range(wParam).anchor.VirtualSpace();
9098 case SCI_SETSELECTIONNSTART:
9099 sel.Range(wParam).anchor.SetPosition(lParam);
9100 Redraw();
9101 break;
9103 case SCI_GETSELECTIONNSTART:
9104 return sel.Range(wParam).Start().Position();
9106 case SCI_SETSELECTIONNEND:
9107 sel.Range(wParam).caret.SetPosition(lParam);
9108 Redraw();
9109 break;
9111 case SCI_GETSELECTIONNEND:
9112 return sel.Range(wParam).End().Position();
9114 case SCI_SETRECTANGULARSELECTIONCARET:
9115 if (!sel.IsRectangular())
9116 sel.Clear();
9117 sel.selType = Selection::selRectangle;
9118 sel.Rectangular().caret.SetPosition(wParam);
9119 SetRectangularRange();
9120 Redraw();
9121 break;
9123 case SCI_GETRECTANGULARSELECTIONCARET:
9124 return sel.Rectangular().caret.Position();
9126 case SCI_SETRECTANGULARSELECTIONANCHOR:
9127 if (!sel.IsRectangular())
9128 sel.Clear();
9129 sel.selType = Selection::selRectangle;
9130 sel.Rectangular().anchor.SetPosition(wParam);
9131 SetRectangularRange();
9132 Redraw();
9133 break;
9135 case SCI_GETRECTANGULARSELECTIONANCHOR:
9136 return sel.Rectangular().anchor.Position();
9138 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9139 if (!sel.IsRectangular())
9140 sel.Clear();
9141 sel.selType = Selection::selRectangle;
9142 sel.Rectangular().caret.SetVirtualSpace(wParam);
9143 SetRectangularRange();
9144 Redraw();
9145 break;
9147 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9148 return sel.Rectangular().caret.VirtualSpace();
9150 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9151 if (!sel.IsRectangular())
9152 sel.Clear();
9153 sel.selType = Selection::selRectangle;
9154 sel.Rectangular().anchor.SetVirtualSpace(wParam);
9155 SetRectangularRange();
9156 Redraw();
9157 break;
9159 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9160 return sel.Rectangular().anchor.VirtualSpace();
9162 case SCI_SETVIRTUALSPACEOPTIONS:
9163 virtualSpaceOptions = wParam;
9164 break;
9166 case SCI_GETVIRTUALSPACEOPTIONS:
9167 return virtualSpaceOptions;
9169 case SCI_SETADDITIONALSELFORE:
9170 vs.selAdditionalForeground.desired = ColourDesired(wParam);
9171 InvalidateStyleRedraw();
9172 break;
9174 case SCI_SETADDITIONALSELBACK:
9175 vs.selAdditionalBackground.desired = ColourDesired(wParam);
9176 InvalidateStyleRedraw();
9177 break;
9179 case SCI_SETADDITIONALSELALPHA:
9180 vs.selAdditionalAlpha = wParam;
9181 InvalidateStyleRedraw();
9182 break;
9184 case SCI_GETADDITIONALSELALPHA:
9185 return vs.selAdditionalAlpha;
9187 case SCI_SETADDITIONALCARETFORE:
9188 vs.additionalCaretColour.desired = ColourDesired(wParam);
9189 InvalidateStyleRedraw();
9190 break;
9192 case SCI_GETADDITIONALCARETFORE:
9193 return vs.additionalCaretColour.desired.AsLong();
9195 case SCI_ROTATESELECTION:
9196 sel.RotateMain();
9197 InvalidateSelection(sel.RangeMain(), true);
9198 break;
9200 case SCI_SWAPMAINANCHORCARET:
9201 InvalidateSelection(sel.RangeMain());
9202 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
9203 break;
9205 case SCI_CHANGELEXERSTATE:
9206 pdoc->ChangeLexerState(wParam, lParam);
9207 break;
9209 case SCI_SETIDENTIFIER:
9210 SetCtrlID(wParam);
9211 break;
9213 case SCI_GETIDENTIFIER:
9214 return GetCtrlID();
9216 default:
9217 return DefWndProc(iMessage, wParam, lParam);
9219 //Platform::DebugPrintf("end wnd proc\n");
9220 return 0l;