updated Scintilla to 2.29
[TortoiseGit.git] / ext / scintilla / src / Editor.cxx
blobb19481abac9f2c5a1082cb9c4c0c5505a8d2570d
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 bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
2808 (!overrideBackground) && (vsDraw.whitespaceBackgroundSet);
2810 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2811 int indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
2813 int posLineStart = pdoc->LineStart(line);
2815 int startseg = ll->LineStart(subLine);
2816 int subLineStart = ll->positions[startseg];
2817 if (subLine >= ll->lines) {
2818 DrawAnnotation(surface, vsDraw, line, xStart, rcLine, ll, subLine);
2819 return; // No further drawing
2821 int lineStart = 0;
2822 int lineEnd = 0;
2823 if (subLine < ll->lines) {
2824 lineStart = ll->LineStart(subLine);
2825 lineEnd = ll->LineStart(subLine + 1);
2826 if (subLine == ll->lines - 1) {
2827 lineEnd = ll->numCharsBeforeEOL;
2831 ColourAllocated wrapColour = vsDraw.styles[STYLE_DEFAULT].fore.allocated;
2832 if (vsDraw.whitespaceForegroundSet)
2833 wrapColour = vsDraw.whitespaceForeground.allocated;
2835 bool drawWrapMarkEnd = false;
2837 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2838 if (subLine + 1 < ll->lines) {
2839 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
2843 if (ll->wrapIndent != 0) {
2845 bool continuedWrapLine = false;
2846 if (subLine < ll->lines) {
2847 continuedWrapLine = ll->LineStart(subLine) != 0;
2850 if (continuedWrapLine) {
2851 // draw continuation rect
2852 PRectangle rcPlace = rcSegment;
2854 rcPlace.left = ll->positions[startseg] + xStart - subLineStart;
2855 rcPlace.right = rcPlace.left + ll->wrapIndent;
2857 // default bgnd here..
2858 surface->FillRectangle(rcSegment, overrideBackground ? background :
2859 vsDraw.styles[STYLE_DEFAULT].back.allocated);
2861 // main line style would be below but this would be inconsistent with end markers
2862 // also would possibly not be the style at wrap point
2863 //int styleMain = ll->styles[lineStart];
2864 //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back.allocated);
2866 if (wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
2868 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
2869 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2870 else
2871 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2873 DrawWrapMarker(surface, rcPlace, false, wrapColour);
2876 xStart += ll->wrapIndent;
2880 bool selBackDrawn = vsDraw.selbackset &&
2881 ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA));
2883 // Does not take margin into account but not significant
2884 int xStartVisible = subLineStart - xStart;
2886 ll->psel = &sel;
2888 BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, xStartVisible, selBackDrawn, pdoc);
2889 int next = bfBack.First();
2891 // Background drawing loop
2892 while (twoPhaseDraw && (next < lineEnd)) {
2894 startseg = next;
2895 next = bfBack.Next();
2896 int i = next - 1;
2897 int iDoc = i + posLineStart;
2899 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2900 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2901 // Only try to draw if really visible - enhances performance by not calling environment to
2902 // draw strings that are completely past the right side of the window.
2903 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2904 // Clip to line rectangle, since may have a huge position which will not work with some platforms
2905 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2906 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2908 int styleMain = ll->styles[i];
2909 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2910 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2911 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2912 if (ll->chars[i] == '\t') {
2913 // Tab display
2914 if (drawWhitespaceBackground &&
2915 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2916 textBack = vsDraw.whitespaceBackground.allocated;
2917 surface->FillRectangle(rcSegment, textBack);
2918 } else if (IsControlCharacter(ll->chars[i])) {
2919 // Control character display
2920 inIndentation = false;
2921 surface->FillRectangle(rcSegment, textBack);
2922 } else {
2923 // Normal text display
2924 surface->FillRectangle(rcSegment, textBack);
2925 if (vsDraw.viewWhitespace != wsInvisible ||
2926 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
2927 for (int cpos = 0; cpos <= i - startseg; cpos++) {
2928 if (ll->chars[cpos + startseg] == ' ') {
2929 if (drawWhitespaceBackground &&
2930 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
2931 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
2932 rcSegment.top,
2933 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
2934 rcSegment.bottom);
2935 surface->FillRectangle(rcSpace, vsDraw.whitespaceBackground.allocated);
2937 } else {
2938 inIndentation = false;
2943 } else if (rcSegment.left > rcLine.right) {
2944 break;
2948 if (twoPhaseDraw) {
2949 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
2950 xStart, subLine, subLineStart, overrideBackground, background,
2951 drawWrapMarkEnd, wrapColour);
2954 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, true);
2956 if (vsDraw.edgeState == EDGE_LINE) {
2957 int edgeX = theEdge * vsDraw.spaceWidth;
2958 rcSegment.left = edgeX + xStart;
2959 if ((ll->wrapIndent != 0) && (lineStart != 0))
2960 rcSegment.left -= ll->wrapIndent;
2961 rcSegment.right = rcSegment.left + 1;
2962 surface->FillRectangle(rcSegment, vsDraw.edgecolour.allocated);
2965 // Draw underline mark as part of background if not transparent
2966 int marks = pdoc->GetMark(line);
2967 int markBit;
2968 for (markBit = 0; (markBit < 32) && marks; markBit++) {
2969 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
2970 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2971 PRectangle rcUnderline = rcLine;
2972 rcUnderline.top = rcUnderline.bottom - 2;
2973 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back.allocated);
2975 marks >>= 1;
2978 inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2979 // Foreground drawing loop
2980 BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, xStartVisible,
2981 ((!twoPhaseDraw && selBackDrawn) || vsDraw.selforeset), pdoc);
2982 next = bfFore.First();
2984 while (next < lineEnd) {
2986 startseg = next;
2987 next = bfFore.Next();
2988 int i = next - 1;
2990 int iDoc = i + posLineStart;
2992 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2993 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2994 // Only try to draw if really visible - enhances performance by not calling environment to
2995 // draw strings that are completely past the right side of the window.
2996 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2997 int styleMain = ll->styles[i];
2998 ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
2999 Font &textFont = vsDraw.styles[styleMain].font;
3000 //hotspot foreground
3001 if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) {
3002 if (vsDraw.hotspotForegroundSet)
3003 textFore = vsDraw.hotspotForeground.allocated;
3005 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
3006 if (inSelection && (vsDraw.selforeset)) {
3007 textFore = (inSelection == 1) ? vsDraw.selforeground.allocated : vsDraw.selAdditionalForeground.allocated;
3009 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
3010 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
3011 if (ll->chars[i] == '\t') {
3012 // Tab display
3013 if (!twoPhaseDraw) {
3014 if (drawWhitespaceBackground &&
3015 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
3016 textBack = vsDraw.whitespaceBackground.allocated;
3017 surface->FillRectangle(rcSegment, textBack);
3019 if ((vsDraw.viewWhitespace != wsInvisible) ||
3020 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
3021 if (vsDraw.whitespaceForegroundSet)
3022 textFore = vsDraw.whitespaceForeground.allocated;
3023 surface->PenColour(textFore);
3025 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
3026 for (int xIG = ll->positions[i] / indentWidth * indentWidth; xIG < ll->positions[i + 1]; xIG += indentWidth) {
3027 if (xIG >= ll->positions[i] && xIG > 0) {
3028 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIG + xStart, rcSegment,
3029 (ll->xHighlightGuide == xIG));
3033 if (vsDraw.viewWhitespace != wsInvisible) {
3034 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3035 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
3036 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
3037 DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
3040 } else if (IsControlCharacter(ll->chars[i])) {
3041 // Control character display
3042 inIndentation = false;
3043 if (controlCharSymbol < 32) {
3044 // Draw the character
3045 const char *ctrlChar = ControlCharacterString(ll->chars[i]);
3046 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
3047 } else {
3048 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
3049 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
3050 rcSegment.top + vsDraw.maxAscent,
3051 cc, 1, textBack, textFore);
3053 } else if ((i == startseg) && (static_cast<unsigned char>(ll->chars[i]) >= 0x80) && IsUnicodeMode()) {
3054 // A single byte >= 0x80 in UTF-8 is a bad byte and is displayed as its hex value
3055 char hexits[4];
3056 sprintf(hexits, "x%2X", ll->chars[i] & 0xff);
3057 DrawTextBlob(surface, vsDraw, rcSegment, hexits, textBack, textFore, twoPhaseDraw);
3058 } else {
3059 // Normal text display
3060 if (vsDraw.styles[styleMain].visible) {
3061 if (twoPhaseDraw) {
3062 surface->DrawTextTransparent(rcSegment, textFont,
3063 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
3064 i - startseg + 1, textFore);
3065 } else {
3066 surface->DrawTextNoClip(rcSegment, textFont,
3067 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
3068 i - startseg + 1, textFore, textBack);
3071 if (vsDraw.viewWhitespace != wsInvisible ||
3072 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
3073 for (int cpos = 0; cpos <= i - startseg; cpos++) {
3074 if (ll->chars[cpos + startseg] == ' ') {
3075 if (vsDraw.viewWhitespace != wsInvisible) {
3076 if (vsDraw.whitespaceForegroundSet)
3077 textFore = vsDraw.whitespaceForeground.allocated;
3078 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3079 int xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2;
3080 if (!twoPhaseDraw && drawWhitespaceBackground &&
3081 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
3082 textBack = vsDraw.whitespaceBackground.allocated;
3083 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
3084 rcSegment.top,
3085 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
3086 rcSegment.bottom);
3087 surface->FillRectangle(rcSpace, textBack);
3089 PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
3090 rcDot.right = rcDot.left + vs.whitespaceSize;
3091 rcDot.bottom = rcDot.top + vs.whitespaceSize;
3092 surface->FillRectangle(rcDot, textFore);
3095 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
3096 int startSpace = ll->positions[cpos + startseg];
3097 if (startSpace > 0 && (startSpace % indentWidth == 0)) {
3098 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, startSpace + xStart, rcSegment,
3099 (ll->xHighlightGuide == ll->positions[cpos + startseg]));
3102 } else {
3103 inIndentation = false;
3108 if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd) {
3109 PRectangle rcUL = rcSegment;
3110 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3111 rcUL.bottom = rcUL.top + 1;
3112 if (vsDraw.hotspotForegroundSet)
3113 surface->FillRectangle(rcUL, vsDraw.hotspotForeground.allocated);
3114 else
3115 surface->FillRectangle(rcUL, textFore);
3116 } else if (vsDraw.styles[styleMain].underline) {
3117 PRectangle rcUL = rcSegment;
3118 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3119 rcUL.bottom = rcUL.top + 1;
3120 surface->FillRectangle(rcUL, textFore);
3122 } else if (rcSegment.left > rcLine.right) {
3123 break;
3126 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
3127 && (subLine == 0)) {
3128 int indentSpace = pdoc->GetLineIndentation(line);
3129 int xStartText = ll->positions[pdoc->GetLineIndentPosition(line) - posLineStart];
3131 // Find the most recent line with some text
3133 int lineLastWithText = line;
3134 while (lineLastWithText > Platform::Maximum(line-20, 0) && pdoc->IsWhiteLine(lineLastWithText)) {
3135 lineLastWithText--;
3137 if (lineLastWithText < line) {
3138 xStartText = 100000; // Don't limit to visible indentation on empty line
3139 // This line is empty, so use indentation of last line with text
3140 int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText);
3141 int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
3142 if (isFoldHeader) {
3143 // Level is one more level than parent
3144 indentLastWithText += pdoc->IndentSize();
3146 if (vsDraw.viewIndentationGuides == ivLookForward) {
3147 // In viLookForward mode, previous line only used if it is a fold header
3148 if (isFoldHeader) {
3149 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3151 } else { // viLookBoth
3152 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3156 int lineNextWithText = line;
3157 while (lineNextWithText < Platform::Minimum(line+20, pdoc->LinesTotal()) && pdoc->IsWhiteLine(lineNextWithText)) {
3158 lineNextWithText++;
3160 if (lineNextWithText > line) {
3161 xStartText = 100000; // Don't limit to visible indentation on empty line
3162 // This line is empty, so use indentation of first next line with text
3163 indentSpace = Platform::Maximum(indentSpace,
3164 pdoc->GetLineIndentation(lineNextWithText));
3167 for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) {
3168 int xIndent = indentPos * vsDraw.spaceWidth;
3169 if (xIndent < xStartText) {
3170 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3171 (ll->xHighlightGuide == xIndent));
3176 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, false);
3178 // End of the drawing of the current line
3179 if (!twoPhaseDraw) {
3180 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
3181 xStart, subLine, subLineStart, overrideBackground, background,
3182 drawWrapMarkEnd, wrapColour);
3184 if (!hideSelection && ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA))) {
3185 // For each selection draw
3186 int virtualSpaces = 0;
3187 if (subLine == (ll->lines - 1)) {
3188 virtualSpaces = sel.VirtualSpaceFor(pdoc->LineEnd(line));
3190 SelectionPosition posStart(posLineStart);
3191 SelectionPosition posEnd(posLineStart + lineEnd, virtualSpaces);
3192 SelectionSegment virtualSpaceRange(posStart, posEnd);
3193 for (size_t r=0; r<sel.Count(); r++) {
3194 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
3195 if (alpha != SC_ALPHA_NOALPHA) {
3196 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
3197 if (!portion.Empty()) {
3198 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
3199 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
3200 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
3201 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
3202 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
3203 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == sel.Main()), alpha);
3209 // Draw any translucent whole line states
3210 rcSegment.left = 0;
3211 rcSegment.right = rcLine.right - 1;
3212 if (caret.active && vsDraw.showCaretLineBackground && ll->containsCaret) {
3213 SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground.allocated, vsDraw.caretLineAlpha);
3215 marks = pdoc->GetMark(line);
3216 for (markBit = 0; (markBit < 32) && marks; markBit++) {
3217 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
3218 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
3219 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
3220 PRectangle rcUnderline = rcSegment;
3221 rcUnderline.top = rcUnderline.bottom - 2;
3222 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
3224 marks >>= 1;
3226 if (vsDraw.maskInLine) {
3227 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
3228 if (marksMasked) {
3229 for (markBit = 0; (markBit < 32) && marksMasked; markBit++) {
3230 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
3231 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
3233 marksMasked >>= 1;
3239 void Editor::DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine,
3240 int xStart, int offset, int posCaret, PRectangle rcCaret, ColourAllocated caretColour) {
3242 int lineStart = ll->LineStart(subLine);
3243 int posBefore = posCaret;
3244 int posAfter = MovePositionOutsideChar(posCaret + 1, 1);
3245 int numCharsToDraw = posAfter - posCaret;
3247 // Work out where the starting and ending offsets are. We need to
3248 // see if the previous character shares horizontal space, such as a
3249 // glyph / combining character. If so we'll need to draw that too.
3250 int offsetFirstChar = offset;
3251 int offsetLastChar = offset + (posAfter - posCaret);
3252 while ((offsetLastChar - numCharsToDraw) >= lineStart) {
3253 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
3254 // The char does not share horizontal space
3255 break;
3257 // Char shares horizontal space, update the numChars to draw
3258 // Update posBefore to point to the prev char
3259 posBefore = MovePositionOutsideChar(posBefore - 1, -1);
3260 numCharsToDraw = posAfter - posBefore;
3261 offsetFirstChar = offset - (posCaret - posBefore);
3264 // See if the next character shares horizontal space, if so we'll
3265 // need to draw that too.
3266 numCharsToDraw = offsetLastChar - offsetFirstChar;
3267 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
3268 // Update posAfter to point to the 2nd next char, this is where
3269 // the next character ends, and 2nd next begins. We'll need
3270 // to compare these two
3271 posBefore = posAfter;
3272 posAfter = MovePositionOutsideChar(posAfter + 1, 1);
3273 offsetLastChar = offset + (posAfter - posCaret);
3274 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
3275 // The char does not share horizontal space
3276 break;
3278 // Char shares horizontal space, update the numChars to draw
3279 numCharsToDraw = offsetLastChar - offsetFirstChar;
3282 // We now know what to draw, update the caret drawing rectangle
3283 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
3284 rcCaret.right = ll->positions[offsetFirstChar+numCharsToDraw] - ll->positions[lineStart] + xStart;
3286 // Adjust caret position to take into account any word wrapping symbols.
3287 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3288 int wordWrapCharWidth = ll->wrapIndent;
3289 rcCaret.left += wordWrapCharWidth;
3290 rcCaret.right += wordWrapCharWidth;
3293 // This character is where the caret block is, we override the colours
3294 // (inversed) for drawing the caret here.
3295 int styleMain = ll->styles[offsetFirstChar];
3296 surface->DrawTextClipped(rcCaret, vsDraw.styles[styleMain].font,
3297 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
3298 numCharsToDraw, vsDraw.styles[styleMain].back.allocated,
3299 caretColour);
3302 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
3303 if (!pixmapSelPattern->Initialised()) {
3304 const int patternSize = 8;
3305 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3306 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
3307 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
3308 // way between the chrome colour and the chrome highlight colour making a nice transition
3309 // between the window chrome and the content area. And it works in low colour depths.
3310 PRectangle rcPattern(0, 0, patternSize, patternSize);
3312 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
3313 ColourAllocated colourFMFill = vs.selbar.allocated;
3314 ColourAllocated colourFMStripes = vs.selbarlight.allocated;
3316 if (!(vs.selbarlight.desired == ColourDesired(0xff, 0xff, 0xff))) {
3317 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
3318 // (Typically, the highlight colour is white.)
3319 colourFMFill = vs.selbarlight.allocated;
3322 if (vs.foldmarginColourSet) {
3323 // override default fold margin colour
3324 colourFMFill = vs.foldmarginColour.allocated;
3326 if (vs.foldmarginHighlightColourSet) {
3327 // override default fold margin highlight colour
3328 colourFMStripes = vs.foldmarginHighlightColour.allocated;
3331 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
3332 for (int y = 0; y < patternSize; y++) {
3333 for (int x = y % 2; x < patternSize; x+=2) {
3334 PRectangle rcPixel(x, y, x+1, y+1);
3335 pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);
3340 if (!pixmapIndentGuide->Initialised()) {
3341 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
3342 pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3343 pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3344 PRectangle rcIG(0, 0, 1, vs.lineHeight);
3345 pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back.allocated);
3346 pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore.allocated);
3347 pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back.allocated);
3348 pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore.allocated);
3349 for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
3350 PRectangle rcPixel(0, stripe, 1, stripe+1);
3351 pixmapIndentGuide->FillRectangle(rcPixel, vs.styles[STYLE_INDENTGUIDE].fore.allocated);
3352 pixmapIndentGuideHighlight->FillRectangle(rcPixel, vs.styles[STYLE_BRACELIGHT].fore.allocated);
3356 if (bufferedDraw) {
3357 if (!pixmapLine->Initialised()) {
3358 PRectangle rcClient = GetClientRectangle();
3359 pixmapLine->InitPixMap(rcClient.Width(), vs.lineHeight,
3360 surfaceWindow, wMain.GetID());
3361 pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
3362 rcClient.Height(), surfaceWindow, wMain.GetID());
3367 void Editor::DrawCarets(Surface *surface, ViewStyle &vsDraw, int lineDoc, int xStart,
3368 PRectangle rcLine, LineLayout *ll, int subLine) {
3369 // When drag is active it is the only caret drawn
3370 bool drawDrag = posDrag.IsValid();
3371 if (hideSelection && !drawDrag)
3372 return;
3373 const int posLineStart = pdoc->LineStart(lineDoc);
3374 // For each selection draw
3375 for (size_t r=0; (r<sel.Count()) || drawDrag; r++) {
3376 const bool mainCaret = r == sel.Main();
3377 const SelectionPosition posCaret = (drawDrag ? posDrag : sel.Range(r).caret);
3378 const int offset = posCaret.Position() - posLineStart;
3379 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
3380 const int virtualOffset = posCaret.VirtualSpace() * spaceWidth;
3381 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
3382 int xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
3383 if (ll->wrapIndent != 0) {
3384 int lineStart = ll->LineStart(subLine);
3385 if (lineStart != 0) // Wrapped
3386 xposCaret += ll->wrapIndent;
3388 bool caretBlinkState = (caret.active && caret.on) || (!additionalCaretsBlink && !mainCaret);
3389 bool caretVisibleState = additionalCaretsVisible || mainCaret;
3390 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
3391 ((posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
3392 bool caretAtEOF = false;
3393 bool caretAtEOL = false;
3394 bool drawBlockCaret = false;
3395 int widthOverstrikeCaret;
3396 int caretWidthOffset = 0;
3397 PRectangle rcCaret = rcLine;
3399 if (posCaret.Position() == pdoc->Length()) { // At end of document
3400 caretAtEOF = true;
3401 widthOverstrikeCaret = vsDraw.aveCharWidth;
3402 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
3403 caretAtEOL = true;
3404 widthOverstrikeCaret = vsDraw.aveCharWidth;
3405 } else {
3406 widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
3408 if (widthOverstrikeCaret < 3) // Make sure its visible
3409 widthOverstrikeCaret = 3;
3411 if (xposCaret > 0)
3412 caretWidthOffset = 1; // Move back so overlaps both character cells.
3413 xposCaret += xStart;
3414 if (posDrag.IsValid()) {
3415 /* Dragging text, use a line caret */
3416 rcCaret.left = xposCaret - caretWidthOffset;
3417 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3418 } else if (inOverstrike) {
3419 /* Overstrike (insert mode), use a modified bar caret */
3420 rcCaret.top = rcCaret.bottom - 2;
3421 rcCaret.left = xposCaret + 1;
3422 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
3423 } else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) {
3424 /* Block caret */
3425 rcCaret.left = xposCaret;
3426 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
3427 drawBlockCaret = true;
3428 rcCaret.right = xposCaret + widthOverstrikeCaret;
3429 } else {
3430 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
3432 } else {
3433 /* Line caret */
3434 rcCaret.left = xposCaret - caretWidthOffset;
3435 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3437 ColourAllocated caretColour = mainCaret ? vsDraw.caretcolour.allocated : vsDraw.additionalCaretColour.allocated;
3438 if (drawBlockCaret) {
3439 DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
3440 } else {
3441 surface->FillRectangle(rcCaret, caretColour);
3445 if (drawDrag)
3446 break;
3450 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
3451 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
3452 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
3454 StyleToPositionInView(PositionAfterArea(rcArea));
3456 pixmapLine->Release();
3457 RefreshStyleData();
3458 RefreshPixMaps(surfaceWindow);
3460 PRectangle rcClient = GetClientRectangle();
3461 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
3462 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3464 surfaceWindow->SetPalette(&palette, true);
3465 pixmapLine->SetPalette(&palette, !hasFocus);
3467 int screenLinePaintFirst = rcArea.top / vs.lineHeight;
3469 int xStart = vs.fixedColumnWidth - xOffset;
3470 int ypos = 0;
3471 if (!bufferedDraw)
3472 ypos += screenLinePaintFirst * vs.lineHeight;
3473 int yposScreen = screenLinePaintFirst * vs.lineHeight;
3475 bool paintAbandonedByStyling = paintState == paintAbandoned;
3476 if (needUpdateUI) {
3477 // Deselect palette by selecting a temporary palette
3478 Palette palTemp;
3479 surfaceWindow->SetPalette(&palTemp, true);
3481 NotifyUpdateUI();
3482 needUpdateUI = 0;
3484 RefreshStyleData();
3485 RefreshPixMaps(surfaceWindow);
3486 surfaceWindow->SetPalette(&palette, true);
3487 pixmapLine->SetPalette(&palette, !hasFocus);
3490 // Call priority lines wrap on a window of lines which are likely
3491 // to rendered with the following paint (that is wrap the visible
3492 // lines first).
3493 int startLineToWrap = cs.DocFromDisplay(topLine) - 5;
3494 if (startLineToWrap < 0)
3495 startLineToWrap = 0;
3496 if (WrapLines(false, startLineToWrap)) {
3497 // The wrapping process has changed the height of some lines so
3498 // abandon this paint for a complete repaint.
3499 if (AbandonPaint()) {
3500 return;
3502 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
3504 PLATFORM_ASSERT(pixmapSelPattern->Initialised());
3506 if (paintState != paintAbandoned) {
3507 PaintSelMargin(surfaceWindow, rcArea);
3509 PRectangle rcRightMargin = rcClient;
3510 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
3511 if (rcArea.Intersects(rcRightMargin)) {
3512 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back.allocated);
3516 if (paintState == paintAbandoned) {
3517 // Either styling or NotifyUpdateUI noticed that painting is needed
3518 // outside the current painting rectangle
3519 //Platform::DebugPrintf("Abandoning paint\n");
3520 if (wrapState != eWrapNone) {
3521 if (paintAbandonedByStyling) {
3522 // Styling has spilled over a line end, such as occurs by starting a multiline
3523 // comment. The width of subsequent text may have changed, so rewrap.
3524 NeedWrapping(cs.DocFromDisplay(topLine));
3527 return;
3529 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
3531 // Do the painting
3532 if (rcArea.right > vs.fixedColumnWidth) {
3534 Surface *surface = surfaceWindow;
3535 if (bufferedDraw) {
3536 surface = pixmapLine;
3537 PLATFORM_ASSERT(pixmapLine->Initialised());
3539 surface->SetUnicodeMode(IsUnicodeMode());
3540 surface->SetDBCSMode(CodePage());
3542 int visibleLine = topLine + screenLinePaintFirst;
3544 SelectionPosition posCaret = sel.RangeMain().caret;
3545 if (posDrag.IsValid())
3546 posCaret = posDrag;
3547 int lineCaret = pdoc->LineFromPosition(posCaret.Position());
3549 // Remove selection margin from drawing area so text will not be drawn
3550 // on it in unbuffered mode.
3551 PRectangle rcTextArea = rcClient;
3552 rcTextArea.left = vs.fixedColumnWidth;
3553 rcTextArea.right -= vs.rightMarginWidth;
3554 surfaceWindow->SetClip(rcTextArea);
3556 // Loop on visible lines
3557 //double durLayout = 0.0;
3558 //double durPaint = 0.0;
3559 //double durCopy = 0.0;
3560 //ElapsedTime etWhole;
3561 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
3562 AutoLineLayout ll(llc, 0);
3563 while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
3565 int lineDoc = cs.DocFromDisplay(visibleLine);
3566 // Only visible lines should be handled by the code within the loop
3567 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
3568 int lineStartSet = cs.DisplayFromDoc(lineDoc);
3569 int subLine = visibleLine - lineStartSet;
3571 // Copy this line and its styles from the document into local arrays
3572 // and determine the x position at which each character starts.
3573 //ElapsedTime et;
3574 if (lineDoc != lineDocPrevious) {
3575 ll.Set(0);
3576 ll.Set(RetrieveLineLayout(lineDoc));
3577 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
3578 lineDocPrevious = lineDoc;
3580 //durLayout += et.Duration(true);
3582 if (ll) {
3583 ll->containsCaret = lineDoc == lineCaret;
3584 if (hideSelection) {
3585 ll->containsCaret = false;
3588 GetHotSpotRange(ll->hsStart, ll->hsEnd);
3590 PRectangle rcLine = rcClient;
3591 rcLine.top = ypos;
3592 rcLine.bottom = ypos + vs.lineHeight;
3594 bool bracesIgnoreStyle = false;
3595 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
3596 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
3597 bracesIgnoreStyle = true;
3599 Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
3600 // Highlight the current braces if any
3601 ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
3602 highlightGuideColumn * vs.spaceWidth, bracesIgnoreStyle);
3604 // Draw the line
3605 DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
3606 //durPaint += et.Duration(true);
3608 // Restore the previous styles for the brace highlights in case layout is in cache.
3609 ll->RestoreBracesHighlight(rangeLine, braces, bracesIgnoreStyle);
3611 bool expanded = cs.GetExpanded(lineDoc);
3612 const int level = pdoc->GetLevel(lineDoc);
3613 const int levelNext = pdoc->GetLevel(lineDoc + 1);
3614 if ((level & SC_FOLDLEVELHEADERFLAG) &&
3615 ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
3616 // Paint the line above the fold
3617 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
3619 (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
3620 PRectangle rcFoldLine = rcLine;
3621 rcFoldLine.bottom = rcFoldLine.top + 1;
3622 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
3624 // Paint the line below the fold
3625 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
3627 (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
3628 PRectangle rcFoldLine = rcLine;
3629 rcFoldLine.top = rcFoldLine.bottom - 1;
3630 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
3634 DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine);
3636 if (bufferedDraw) {
3637 Point from(vs.fixedColumnWidth, 0);
3638 PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen,
3639 rcClient.right, yposScreen + vs.lineHeight);
3640 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
3643 lineWidthMaxSeen = Platform::Maximum(
3644 lineWidthMaxSeen, ll->positions[ll->numCharsInLine]);
3645 //durCopy += et.Duration(true);
3648 if (!bufferedDraw) {
3649 ypos += vs.lineHeight;
3652 yposScreen += vs.lineHeight;
3653 visibleLine++;
3655 //gdk_flush();
3657 ll.Set(0);
3658 //if (durPaint < 0.00000001)
3659 // durPaint = 0.00000001;
3661 // Right column limit indicator
3662 PRectangle rcBeyondEOF = rcClient;
3663 rcBeyondEOF.left = vs.fixedColumnWidth;
3664 rcBeyondEOF.right = rcBeyondEOF.right;
3665 rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight;
3666 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
3667 surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back.allocated);
3668 if (vs.edgeState == EDGE_LINE) {
3669 int edgeX = theEdge * vs.spaceWidth;
3670 rcBeyondEOF.left = edgeX + xStart;
3671 rcBeyondEOF.right = rcBeyondEOF.left + 1;
3672 surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour.allocated);
3675 //Platform::DebugPrintf(
3676 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
3677 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
3678 NotifyPainted();
3682 // Space (3 space characters) between line numbers and text when printing.
3683 #define lineNumberPrintSpace " "
3685 ColourDesired InvertedLight(ColourDesired orig) {
3686 unsigned int r = orig.GetRed();
3687 unsigned int g = orig.GetGreen();
3688 unsigned int b = orig.GetBlue();
3689 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
3690 unsigned int il = 0xff - l;
3691 if (l == 0)
3692 return ColourDesired(0xff, 0xff, 0xff);
3693 r = r * il / l;
3694 g = g * il / l;
3695 b = b * il / l;
3696 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
3699 // This is mostly copied from the Paint method but with some things omitted
3700 // such as the margin markers, line numbers, selection and caret
3701 // Should be merged back into a combined Draw method.
3702 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
3703 if (!pfr)
3704 return 0;
3706 AutoSurface surface(pfr->hdc, this);
3707 if (!surface)
3708 return 0;
3709 AutoSurface surfaceMeasure(pfr->hdcTarget, this);
3710 if (!surfaceMeasure) {
3711 return 0;
3714 // Can't use measurements cached for screen
3715 posCache.Clear();
3717 ViewStyle vsPrint(vs);
3719 // Modify the view style for printing as do not normally want any of the transient features to be printed
3720 // Printing supports only the line number margin.
3721 int lineNumberIndex = -1;
3722 for (int margin = 0; margin < ViewStyle::margins; margin++) {
3723 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
3724 lineNumberIndex = margin;
3725 } else {
3726 vsPrint.ms[margin].width = 0;
3729 vsPrint.showMarkedLines = false;
3730 vsPrint.fixedColumnWidth = 0;
3731 vsPrint.zoomLevel = printMagnification;
3732 vsPrint.viewIndentationGuides = ivNone;
3733 // Don't show the selection when printing
3734 vsPrint.selbackset = false;
3735 vsPrint.selforeset = false;
3736 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
3737 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
3738 vsPrint.whitespaceBackgroundSet = false;
3739 vsPrint.whitespaceForegroundSet = false;
3740 vsPrint.showCaretLineBackground = false;
3741 // Don't highlight matching braces using indicators
3742 vsPrint.braceHighlightIndicatorSet = false;
3743 vsPrint.braceBadLightIndicatorSet = false;
3745 // Set colours for printing according to users settings
3746 for (size_t sty = 0; sty < vsPrint.stylesSize; sty++) {
3747 if (printColourMode == SC_PRINT_INVERTLIGHT) {
3748 vsPrint.styles[sty].fore.desired = InvertedLight(vsPrint.styles[sty].fore.desired);
3749 vsPrint.styles[sty].back.desired = InvertedLight(vsPrint.styles[sty].back.desired);
3750 } else if (printColourMode == SC_PRINT_BLACKONWHITE) {
3751 vsPrint.styles[sty].fore.desired = ColourDesired(0, 0, 0);
3752 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3753 } else if (printColourMode == SC_PRINT_COLOURONWHITE) {
3754 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3755 } else if (printColourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
3756 if (sty <= STYLE_DEFAULT) {
3757 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3761 // White background for the line numbers
3762 vsPrint.styles[STYLE_LINENUMBER].back.desired = ColourDesired(0xff, 0xff, 0xff);
3764 vsPrint.Refresh(*surfaceMeasure);
3765 // Determining width must hapen after fonts have been realised in Refresh
3766 int lineNumberWidth = 0;
3767 if (lineNumberIndex >= 0) {
3768 lineNumberWidth = surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
3769 "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace));
3770 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
3771 vsPrint.Refresh(*surfaceMeasure); // Recalculate fixedColumnWidth
3773 // Ensure colours are set up
3774 vsPrint.RefreshColourPalette(palette, true);
3775 vsPrint.RefreshColourPalette(palette, false);
3777 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
3778 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
3779 if (linePrintLast < linePrintStart)
3780 linePrintLast = linePrintStart;
3781 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
3782 if (linePrintLast > linePrintMax)
3783 linePrintLast = linePrintMax;
3784 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
3785 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
3786 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
3787 int endPosPrint = pdoc->Length();
3788 if (linePrintLast < pdoc->LinesTotal())
3789 endPosPrint = pdoc->LineStart(linePrintLast + 1);
3791 // Ensure we are styled to where we are formatting.
3792 pdoc->EnsureStyledTo(endPosPrint);
3794 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
3795 int ypos = pfr->rc.top;
3797 int lineDoc = linePrintStart;
3799 int nPrintPos = pfr->chrg.cpMin;
3800 int visibleLine = 0;
3801 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
3802 if (printWrapState == eWrapNone)
3803 widthPrint = LineLayout::wrapWidthInfinite;
3805 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
3807 // When printing, the hdc and hdcTarget may be the same, so
3808 // changing the state of surfaceMeasure may change the underlying
3809 // state of surface. Therefore, any cached state is discarded before
3810 // using each surface.
3811 surfaceMeasure->FlushCachedState();
3813 // Copy this line and its styles from the document into local arrays
3814 // and determine the x position at which each character starts.
3815 LineLayout ll(8000);
3816 LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
3818 ll.containsCaret = false;
3820 PRectangle rcLine;
3821 rcLine.left = pfr->rc.left;
3822 rcLine.top = ypos;
3823 rcLine.right = pfr->rc.right - 1;
3824 rcLine.bottom = ypos + vsPrint.lineHeight;
3826 // When document line is wrapped over multiple display lines, find where
3827 // to start printing from to ensure a particular position is on the first
3828 // line of the page.
3829 if (visibleLine == 0) {
3830 int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
3831 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
3832 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
3833 visibleLine = -iwl;
3837 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
3838 visibleLine = -(ll.lines - 1);
3842 if (draw && lineNumberWidth &&
3843 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
3844 (visibleLine >= 0)) {
3845 char number[100];
3846 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
3847 PRectangle rcNumber = rcLine;
3848 rcNumber.right = rcNumber.left + lineNumberWidth;
3849 // Right justify
3850 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
3851 vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
3852 surface->FlushCachedState();
3853 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
3854 ypos + vsPrint.maxAscent, number, istrlen(number),
3855 vsPrint.styles[STYLE_LINENUMBER].fore.allocated,
3856 vsPrint.styles[STYLE_LINENUMBER].back.allocated);
3859 // Draw the line
3860 surface->FlushCachedState();
3862 for (int iwl = 0; iwl < ll.lines; iwl++) {
3863 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
3864 if (visibleLine >= 0) {
3865 if (draw) {
3866 rcLine.top = ypos;
3867 rcLine.bottom = ypos + vsPrint.lineHeight;
3868 DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
3870 ypos += vsPrint.lineHeight;
3872 visibleLine++;
3873 if (iwl == ll.lines - 1)
3874 nPrintPos = pdoc->LineStart(lineDoc + 1);
3875 else
3876 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
3880 ++lineDoc;
3883 // Clear cache so measurements are not used for screen
3884 posCache.Clear();
3886 return nPrintPos;
3889 int Editor::TextWidth(int style, const char *text) {
3890 RefreshStyleData();
3891 AutoSurface surface(this);
3892 if (surface) {
3893 return surface->WidthText(vs.styles[style].font, text, istrlen(text));
3894 } else {
3895 return 1;
3899 // Empty method is overridden on GTK+ to show / hide scrollbars
3900 void Editor::ReconfigureScrollBars() {}
3902 void Editor::SetScrollBars() {
3903 RefreshStyleData();
3905 int nMax = MaxScrollPos();
3906 int nPage = LinesOnScreen();
3907 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
3908 if (modified) {
3909 DwellEnd(true);
3912 // TODO: ensure always showing as many lines as possible
3913 // May not be, if, for example, window made larger
3914 if (topLine > MaxScrollPos()) {
3915 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
3916 SetVerticalScrollPos();
3917 Redraw();
3919 if (modified) {
3920 if (!AbandonPaint())
3921 Redraw();
3923 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
3926 void Editor::ChangeSize() {
3927 DropGraphics();
3928 SetScrollBars();
3929 if (wrapState != eWrapNone) {
3930 PRectangle rcTextArea = GetClientRectangle();
3931 rcTextArea.left = vs.fixedColumnWidth;
3932 rcTextArea.right -= vs.rightMarginWidth;
3933 if (wrapWidth != rcTextArea.Width()) {
3934 NeedWrapping();
3935 Redraw();
3940 int Editor::InsertSpace(int position, unsigned int spaces) {
3941 if (spaces > 0) {
3942 std::string spaceText(spaces, ' ');
3943 pdoc->InsertString(position, spaceText.c_str(), spaces);
3944 position += spaces;
3946 return position;
3949 void Editor::AddChar(char ch) {
3950 char s[2];
3951 s[0] = ch;
3952 s[1] = '\0';
3953 AddCharUTF(s, 1);
3956 void Editor::FilterSelections() {
3957 if (!additionalSelectionTyping && (sel.Count() > 1)) {
3958 SelectionRange rangeOnly = sel.RangeMain();
3959 InvalidateSelection(rangeOnly, true);
3960 sel.SetSelection(rangeOnly);
3964 static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
3965 return *a < *b;
3968 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
3969 void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
3970 FilterSelections();
3972 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
3974 std::vector<SelectionRange *> selPtrs;
3975 for (size_t r = 0; r < sel.Count(); r++) {
3976 selPtrs.push_back(&sel.Range(r));
3978 std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
3980 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
3981 rit != selPtrs.rend(); ++rit) {
3982 SelectionRange *currentSel = *rit;
3983 if (!RangeContainsProtected(currentSel->Start().Position(),
3984 currentSel->End().Position())) {
3985 int positionInsert = currentSel->Start().Position();
3986 if (!currentSel->Empty()) {
3987 if (currentSel->Length()) {
3988 pdoc->DeleteChars(positionInsert, currentSel->Length());
3989 currentSel->ClearVirtualSpace();
3990 } else {
3991 // Range is all virtual so collapse to start of virtual space
3992 currentSel->MinimizeVirtualSpace();
3994 } else if (inOverstrike) {
3995 if (positionInsert < pdoc->Length()) {
3996 if (!IsEOLChar(pdoc->CharAt(positionInsert))) {
3997 pdoc->DelChar(positionInsert);
3998 currentSel->ClearVirtualSpace();
4002 positionInsert = InsertSpace(positionInsert, currentSel->caret.VirtualSpace());
4003 if (pdoc->InsertString(positionInsert, s, len)) {
4004 currentSel->caret.SetPosition(positionInsert + len);
4005 currentSel->anchor.SetPosition(positionInsert + len);
4007 currentSel->ClearVirtualSpace();
4008 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4009 if (wrapState != eWrapNone) {
4010 AutoSurface surface(this);
4011 if (surface) {
4012 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
4013 SetScrollBars();
4014 SetVerticalScrollPos();
4015 Redraw();
4022 if (wrapState != eWrapNone) {
4023 SetScrollBars();
4025 ThinRectangularRange();
4026 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4027 EnsureCaretVisible();
4028 // Avoid blinking during rapid typing:
4029 ShowCaretAtCurrentPosition();
4030 if ((caretSticky == SC_CARETSTICKY_OFF) ||
4031 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
4032 SetLastXChosen();
4035 if (treatAsDBCS) {
4036 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
4037 static_cast<unsigned char>(s[1]));
4038 } else {
4039 int byte = static_cast<unsigned char>(s[0]);
4040 if ((byte < 0xC0) || (1 == len)) {
4041 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
4042 // characters when not in UTF-8 mode.
4043 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
4044 // characters representing themselves.
4045 } else {
4046 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
4047 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
4048 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
4049 if (byte < 0xE0) {
4050 int byte2 = static_cast<unsigned char>(s[1]);
4051 if ((byte2 & 0xC0) == 0x80) {
4052 // Two-byte-character lead-byte followed by a trail-byte.
4053 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
4055 // A two-byte-character lead-byte not followed by trail-byte
4056 // represents itself.
4057 } else if (byte < 0xF0) {
4058 int byte2 = static_cast<unsigned char>(s[1]);
4059 int byte3 = static_cast<unsigned char>(s[2]);
4060 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
4061 // Three-byte-character lead byte followed by two trail bytes.
4062 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
4063 (byte3 & 0x3F));
4065 // A three-byte-character lead-byte not followed by two trail-bytes
4066 // represents itself.
4069 NotifyChar(byte);
4072 if (recordingMacro) {
4073 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
4077 void Editor::InsertPaste(SelectionPosition selStart, const char *text, int len) {
4078 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
4079 selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
4080 if (pdoc->InsertString(selStart.Position(), text, len)) {
4081 SetEmptySelection(selStart.Position() + len);
4083 } else {
4084 // SC_MULTIPASTE_EACH
4085 for (size_t r=0; r<sel.Count(); r++) {
4086 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4087 sel.Range(r).End().Position())) {
4088 int positionInsert = sel.Range(r).Start().Position();
4089 if (!sel.Range(r).Empty()) {
4090 if (sel.Range(r).Length()) {
4091 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
4092 sel.Range(r).ClearVirtualSpace();
4093 } else {
4094 // Range is all virtual so collapse to start of virtual space
4095 sel.Range(r).MinimizeVirtualSpace();
4098 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
4099 if (pdoc->InsertString(positionInsert, text, len)) {
4100 sel.Range(r).caret.SetPosition(positionInsert + len);
4101 sel.Range(r).anchor.SetPosition(positionInsert + len);
4103 sel.Range(r).ClearVirtualSpace();
4109 void Editor::ClearSelection(bool retainMultipleSelections) {
4110 if (!sel.IsRectangular() && !retainMultipleSelections)
4111 FilterSelections();
4112 UndoGroup ug(pdoc);
4113 for (size_t r=0; r<sel.Count(); r++) {
4114 if (!sel.Range(r).Empty()) {
4115 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4116 sel.Range(r).End().Position())) {
4117 pdoc->DeleteChars(sel.Range(r).Start().Position(),
4118 sel.Range(r).Length());
4119 sel.Range(r) = sel.Range(r).Start();
4123 ThinRectangularRange();
4124 sel.RemoveDuplicates();
4125 ClaimSelection();
4128 void Editor::ClearAll() {
4130 UndoGroup ug(pdoc);
4131 if (0 != pdoc->Length()) {
4132 pdoc->DeleteChars(0, pdoc->Length());
4134 if (!pdoc->IsReadOnly()) {
4135 cs.Clear();
4136 pdoc->AnnotationClearAll();
4137 pdoc->MarginClearAll();
4140 sel.Clear();
4141 SetTopLine(0);
4142 SetVerticalScrollPos();
4143 InvalidateStyleRedraw();
4146 void Editor::ClearDocumentStyle() {
4147 Decoration *deco = pdoc->decorations.root;
4148 while (deco) {
4149 // Save next in case deco deleted
4150 Decoration *decoNext = deco->next;
4151 if (deco->indicator < INDIC_CONTAINER) {
4152 pdoc->decorations.SetCurrentIndicator(deco->indicator);
4153 pdoc->DecorationFillRange(0, 0, pdoc->Length());
4155 deco = decoNext;
4157 pdoc->StartStyling(0, '\377');
4158 pdoc->SetStyleFor(pdoc->Length(), 0);
4159 cs.ShowAll();
4160 pdoc->ClearLevels();
4163 void Editor::CopyAllowLine() {
4164 SelectionText selectedText;
4165 CopySelectionRange(&selectedText, true);
4166 CopyToClipboard(selectedText);
4169 void Editor::Cut() {
4170 pdoc->CheckReadOnly();
4171 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
4172 Copy();
4173 ClearSelection();
4177 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
4178 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
4179 return;
4181 sel.Clear();
4182 sel.RangeMain() = SelectionRange(pos);
4183 int line = pdoc->LineFromPosition(sel.MainCaret());
4184 UndoGroup ug(pdoc);
4185 sel.RangeMain().caret = SelectionPosition(
4186 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
4187 int xInsert = XFromPosition(sel.RangeMain().caret);
4188 bool prevCr = false;
4189 while ((len > 0) && IsEOLChar(ptr[len-1]))
4190 len--;
4191 for (int i = 0; i < len; i++) {
4192 if (IsEOLChar(ptr[i])) {
4193 if ((ptr[i] == '\r') || (!prevCr))
4194 line++;
4195 if (line >= pdoc->LinesTotal()) {
4196 if (pdoc->eolMode != SC_EOL_LF)
4197 pdoc->InsertChar(pdoc->Length(), '\r');
4198 if (pdoc->eolMode != SC_EOL_CR)
4199 pdoc->InsertChar(pdoc->Length(), '\n');
4201 // Pad the end of lines with spaces if required
4202 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
4203 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
4204 while (XFromPosition(sel.MainCaret()) < xInsert) {
4205 pdoc->InsertChar(sel.MainCaret(), ' ');
4206 sel.RangeMain().caret.Add(1);
4209 prevCr = ptr[i] == '\r';
4210 } else {
4211 pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
4212 sel.RangeMain().caret.Add(1);
4213 prevCr = false;
4216 SetEmptySelection(pos);
4219 bool Editor::CanPaste() {
4220 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
4223 void Editor::Clear() {
4224 // If multiple selections, don't delete EOLS
4225 if (sel.Empty()) {
4226 bool singleVirtual = false;
4227 if ((sel.Count() == 1) &&
4228 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
4229 sel.RangeMain().Start().VirtualSpace()) {
4230 singleVirtual = true;
4232 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
4233 for (size_t r=0; r<sel.Count(); r++) {
4234 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
4235 if (sel.Range(r).Start().VirtualSpace()) {
4236 if (sel.Range(r).anchor < sel.Range(r).caret)
4237 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
4238 else
4239 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
4241 if ((sel.Count() == 1) || !IsEOLChar(pdoc->CharAt(sel.Range(r).caret.Position()))) {
4242 pdoc->DelChar(sel.Range(r).caret.Position());
4243 sel.Range(r).ClearVirtualSpace();
4244 } // else multiple selection so don't eat line ends
4245 } else {
4246 sel.Range(r).ClearVirtualSpace();
4249 } else {
4250 ClearSelection();
4252 sel.RemoveDuplicates();
4255 void Editor::SelectAll() {
4256 sel.Clear();
4257 SetSelection(0, pdoc->Length());
4258 Redraw();
4261 void Editor::Undo() {
4262 if (pdoc->CanUndo()) {
4263 InvalidateCaret();
4264 int newPos = pdoc->Undo();
4265 if (newPos >= 0)
4266 SetEmptySelection(newPos);
4267 EnsureCaretVisible();
4271 void Editor::Redo() {
4272 if (pdoc->CanRedo()) {
4273 int newPos = pdoc->Redo();
4274 if (newPos >= 0)
4275 SetEmptySelection(newPos);
4276 EnsureCaretVisible();
4280 void Editor::DelChar() {
4281 if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) {
4282 pdoc->DelChar(sel.MainCaret());
4284 // Avoid blinking during rapid typing:
4285 ShowCaretAtCurrentPosition();
4288 void Editor::DelCharBack(bool allowLineStartDeletion) {
4289 if (!sel.IsRectangular())
4290 FilterSelections();
4291 if (sel.IsRectangular())
4292 allowLineStartDeletion = false;
4293 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
4294 if (sel.Empty()) {
4295 for (size_t r=0; r<sel.Count(); r++) {
4296 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
4297 if (sel.Range(r).caret.VirtualSpace()) {
4298 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
4299 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
4300 } else {
4301 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4302 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
4303 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
4304 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
4305 UndoGroup ugInner(pdoc, !ug.Needed());
4306 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4307 int indentationStep = pdoc->IndentSize();
4308 if (indentation % indentationStep == 0) {
4309 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
4310 } else {
4311 pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
4313 // SetEmptySelection
4314 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos),
4315 pdoc->GetLineIndentPosition(lineCurrentPos));
4316 } else {
4317 pdoc->DelCharBack(sel.Range(r).caret.Position());
4321 } else {
4322 sel.Range(r).ClearVirtualSpace();
4325 } else {
4326 ClearSelection();
4328 sel.RemoveDuplicates();
4329 // Avoid blinking during rapid typing:
4330 ShowCaretAtCurrentPosition();
4333 void Editor::NotifyFocus(bool) {}
4335 void Editor::SetCtrlID(int identifier) {
4336 ctrlID = identifier;
4339 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
4340 SCNotification scn = {0};
4341 scn.nmhdr.code = SCN_STYLENEEDED;
4342 scn.position = endStyleNeeded;
4343 NotifyParent(scn);
4346 void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
4347 NotifyStyleToNeeded(endStyleNeeded);
4350 void Editor::NotifyLexerChanged(Document *, void *) {
4353 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
4354 errorStatus = status;
4357 void Editor::NotifyChar(int ch) {
4358 SCNotification scn = {0};
4359 scn.nmhdr.code = SCN_CHARADDED;
4360 scn.ch = ch;
4361 NotifyParent(scn);
4364 void Editor::NotifySavePoint(bool isSavePoint) {
4365 SCNotification scn = {0};
4366 if (isSavePoint) {
4367 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
4368 } else {
4369 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
4371 NotifyParent(scn);
4374 void Editor::NotifyModifyAttempt() {
4375 SCNotification scn = {0};
4376 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
4377 NotifyParent(scn);
4380 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
4381 SCNotification scn = {0};
4382 scn.nmhdr.code = SCN_DOUBLECLICK;
4383 scn.line = LineFromLocation(pt);
4384 scn.position = PositionFromLocation(pt, true);
4385 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4386 (alt ? SCI_ALT : 0);
4387 NotifyParent(scn);
4390 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
4391 SCNotification scn = {0};
4392 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
4393 scn.position = position;
4394 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4395 (alt ? SCI_ALT : 0);
4396 NotifyParent(scn);
4399 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
4400 SCNotification scn = {0};
4401 scn.nmhdr.code = SCN_HOTSPOTCLICK;
4402 scn.position = position;
4403 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4404 (alt ? SCI_ALT : 0);
4405 NotifyParent(scn);
4408 void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
4409 SCNotification scn = {0};
4410 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
4411 scn.position = position;
4412 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4413 (alt ? SCI_ALT : 0);
4414 NotifyParent(scn);
4417 void Editor::NotifyUpdateUI() {
4418 SCNotification scn = {0};
4419 scn.nmhdr.code = SCN_UPDATEUI;
4420 scn.updated = needUpdateUI;
4421 NotifyParent(scn);
4424 void Editor::NotifyPainted() {
4425 SCNotification scn = {0};
4426 scn.nmhdr.code = SCN_PAINTED;
4427 NotifyParent(scn);
4430 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
4431 int mask = pdoc->decorations.AllOnFor(position);
4432 if ((click && mask) || pdoc->decorations.clickNotified) {
4433 SCNotification scn = {0};
4434 pdoc->decorations.clickNotified = click;
4435 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
4436 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | (alt ? SCI_ALT : 0);
4437 scn.position = position;
4438 NotifyParent(scn);
4442 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
4443 int marginClicked = -1;
4444 int x = 0;
4445 for (int margin = 0; margin < ViewStyle::margins; margin++) {
4446 if ((pt.x > x) && (pt.x < x + vs.ms[margin].width))
4447 marginClicked = margin;
4448 x += vs.ms[margin].width;
4450 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
4451 SCNotification scn = {0};
4452 scn.nmhdr.code = SCN_MARGINCLICK;
4453 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4454 (alt ? SCI_ALT : 0);
4455 scn.position = pdoc->LineStart(LineFromLocation(pt));
4456 scn.margin = marginClicked;
4457 NotifyParent(scn);
4458 return true;
4459 } else {
4460 return false;
4464 void Editor::NotifyNeedShown(int pos, int len) {
4465 SCNotification scn = {0};
4466 scn.nmhdr.code = SCN_NEEDSHOWN;
4467 scn.position = pos;
4468 scn.length = len;
4469 NotifyParent(scn);
4472 void Editor::NotifyDwelling(Point pt, bool state) {
4473 SCNotification scn = {0};
4474 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
4475 scn.position = PositionFromLocation(pt, true);
4476 scn.x = pt.x;
4477 scn.y = pt.y;
4478 NotifyParent(scn);
4481 void Editor::NotifyZoom() {
4482 SCNotification scn = {0};
4483 scn.nmhdr.code = SCN_ZOOM;
4484 NotifyParent(scn);
4487 // Notifications from document
4488 void Editor::NotifyModifyAttempt(Document *, void *) {
4489 //Platform::DebugPrintf("** Modify Attempt\n");
4490 NotifyModifyAttempt();
4493 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
4494 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
4495 NotifySavePoint(atSavePoint);
4498 void Editor::CheckModificationForWrap(DocModification mh) {
4499 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
4500 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4501 if (wrapState != eWrapNone) {
4502 int lineDoc = pdoc->LineFromPosition(mh.position);
4503 int lines = Platform::Maximum(0, mh.linesAdded);
4504 NeedWrapping(lineDoc, lineDoc + lines + 1);
4506 // Fix up annotation heights
4507 int lineDoc = pdoc->LineFromPosition(mh.position);
4508 int lines = Platform::Maximum(0, mh.linesAdded);
4509 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
4513 // Move a position so it is still after the same character as before the insertion.
4514 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
4515 if (position > startInsertion) {
4516 return position + length;
4518 return position;
4521 // Move a position so it is still after the same character as before the deletion if that
4522 // character is still present else after the previous surviving character.
4523 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
4524 if (position > startDeletion) {
4525 int endDeletion = startDeletion + length;
4526 if (position > endDeletion) {
4527 return position - length;
4528 } else {
4529 return startDeletion;
4531 } else {
4532 return position;
4536 void Editor::NotifyModified(Document *, DocModification mh, void *) {
4537 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
4538 if (paintState == painting) {
4539 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
4541 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
4542 if (paintState == painting) {
4543 CheckForChangeOutsidePaint(
4544 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
4545 } else {
4546 // Could check that change is before last visible line.
4547 Redraw();
4550 if (mh.modificationType & SC_MOD_LEXERSTATE) {
4551 if (paintState == painting) {
4552 CheckForChangeOutsidePaint(
4553 Range(mh.position, mh.position + mh.length));
4554 } else {
4555 Redraw();
4558 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
4559 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4560 pdoc->IncrementStyleClock();
4562 if (paintState == notPainting) {
4563 if (mh.position < pdoc->LineStart(topLine)) {
4564 // Styling performed before this view
4565 Redraw();
4566 } else {
4567 InvalidateRange(mh.position, mh.position + mh.length);
4570 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4571 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4573 } else {
4574 // Move selection and brace highlights
4575 if (mh.modificationType & SC_MOD_INSERTTEXT) {
4576 sel.MovePositions(true, mh.position, mh.length);
4577 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
4578 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
4579 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
4580 sel.MovePositions(false, mh.position, mh.length);
4581 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
4582 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
4584 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && cs.HiddenLines()) {
4585 // Some lines are hidden so may need shown.
4586 // TODO: check if the modified area is hidden.
4587 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
4588 int lineOfPos = pdoc->LineFromPosition(mh.position);
4589 bool insertingNewLine = false;
4590 for (int i=0; i < mh.length; i++) {
4591 if ((mh.text[i] == '\n') || (mh.text[i] == '\r'))
4592 insertingNewLine = true;
4594 if (insertingNewLine && (mh.position != pdoc->LineStart(lineOfPos)))
4595 NotifyNeedShown(mh.position, pdoc->LineStart(lineOfPos+1) - mh.position);
4596 else
4597 NotifyNeedShown(mh.position, 0);
4598 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
4599 NotifyNeedShown(mh.position, mh.length);
4602 if (mh.linesAdded != 0) {
4603 // Update contraction state for inserted and removed lines
4604 // lineOfPos should be calculated in context of state before modification, shouldn't it
4605 int lineOfPos = pdoc->LineFromPosition(mh.position);
4606 if (mh.linesAdded > 0) {
4607 cs.InsertLines(lineOfPos, mh.linesAdded);
4608 } else {
4609 cs.DeleteLines(lineOfPos, -mh.linesAdded);
4612 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
4613 int lineDoc = pdoc->LineFromPosition(mh.position);
4614 if (vs.annotationVisible) {
4615 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
4616 Redraw();
4619 CheckModificationForWrap(mh);
4620 if (mh.linesAdded != 0) {
4621 // Avoid scrolling of display if change before current display
4622 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
4623 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
4624 if (newTop != topLine) {
4625 SetTopLine(newTop);
4626 SetVerticalScrollPos();
4630 //Platform::DebugPrintf("** %x Doc Changed\n", this);
4631 // TODO: could invalidate from mh.startModification to end of screen
4632 //InvalidateRange(mh.position, mh.position + mh.length);
4633 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
4634 QueueStyling(pdoc->Length());
4635 Redraw();
4637 } else {
4638 //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
4639 // mh.position, mh.position + mh.length);
4640 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
4641 QueueStyling(mh.position + mh.length);
4642 InvalidateRange(mh.position, mh.position + mh.length);
4647 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
4648 SetScrollBars();
4651 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
4652 if ((paintState == notPainting) || !PaintContainsMargin()) {
4653 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
4654 // Fold changes can affect the drawing of following lines so redraw whole margin
4655 RedrawSelMargin(mh.line-1, true);
4656 } else {
4657 RedrawSelMargin(mh.line);
4662 // NOW pay the piper WRT "deferred" visual updates
4663 if (IsLastStep(mh)) {
4664 SetScrollBars();
4665 Redraw();
4668 // If client wants to see this modification
4669 if (mh.modificationType & modEventMask) {
4670 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
4671 // Real modification made to text of document.
4672 NotifyChange(); // Send EN_CHANGE
4675 SCNotification scn = {0};
4676 scn.nmhdr.code = SCN_MODIFIED;
4677 scn.position = mh.position;
4678 scn.modificationType = mh.modificationType;
4679 scn.text = mh.text;
4680 scn.length = mh.length;
4681 scn.linesAdded = mh.linesAdded;
4682 scn.line = mh.line;
4683 scn.foldLevelNow = mh.foldLevelNow;
4684 scn.foldLevelPrev = mh.foldLevelPrev;
4685 scn.token = mh.token;
4686 scn.annotationLinesAdded = mh.annotationLinesAdded;
4687 NotifyParent(scn);
4691 void Editor::NotifyDeleted(Document *, void *) {
4692 /* Do nothing */
4695 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
4697 // Enumerates all macroable messages
4698 switch (iMessage) {
4699 case SCI_CUT:
4700 case SCI_COPY:
4701 case SCI_PASTE:
4702 case SCI_CLEAR:
4703 case SCI_REPLACESEL:
4704 case SCI_ADDTEXT:
4705 case SCI_INSERTTEXT:
4706 case SCI_APPENDTEXT:
4707 case SCI_CLEARALL:
4708 case SCI_SELECTALL:
4709 case SCI_GOTOLINE:
4710 case SCI_GOTOPOS:
4711 case SCI_SEARCHANCHOR:
4712 case SCI_SEARCHNEXT:
4713 case SCI_SEARCHPREV:
4714 case SCI_LINEDOWN:
4715 case SCI_LINEDOWNEXTEND:
4716 case SCI_PARADOWN:
4717 case SCI_PARADOWNEXTEND:
4718 case SCI_LINEUP:
4719 case SCI_LINEUPEXTEND:
4720 case SCI_PARAUP:
4721 case SCI_PARAUPEXTEND:
4722 case SCI_CHARLEFT:
4723 case SCI_CHARLEFTEXTEND:
4724 case SCI_CHARRIGHT:
4725 case SCI_CHARRIGHTEXTEND:
4726 case SCI_WORDLEFT:
4727 case SCI_WORDLEFTEXTEND:
4728 case SCI_WORDRIGHT:
4729 case SCI_WORDRIGHTEXTEND:
4730 case SCI_WORDPARTLEFT:
4731 case SCI_WORDPARTLEFTEXTEND:
4732 case SCI_WORDPARTRIGHT:
4733 case SCI_WORDPARTRIGHTEXTEND:
4734 case SCI_WORDLEFTEND:
4735 case SCI_WORDLEFTENDEXTEND:
4736 case SCI_WORDRIGHTEND:
4737 case SCI_WORDRIGHTENDEXTEND:
4738 case SCI_HOME:
4739 case SCI_HOMEEXTEND:
4740 case SCI_LINEEND:
4741 case SCI_LINEENDEXTEND:
4742 case SCI_HOMEWRAP:
4743 case SCI_HOMEWRAPEXTEND:
4744 case SCI_LINEENDWRAP:
4745 case SCI_LINEENDWRAPEXTEND:
4746 case SCI_DOCUMENTSTART:
4747 case SCI_DOCUMENTSTARTEXTEND:
4748 case SCI_DOCUMENTEND:
4749 case SCI_DOCUMENTENDEXTEND:
4750 case SCI_STUTTEREDPAGEUP:
4751 case SCI_STUTTEREDPAGEUPEXTEND:
4752 case SCI_STUTTEREDPAGEDOWN:
4753 case SCI_STUTTEREDPAGEDOWNEXTEND:
4754 case SCI_PAGEUP:
4755 case SCI_PAGEUPEXTEND:
4756 case SCI_PAGEDOWN:
4757 case SCI_PAGEDOWNEXTEND:
4758 case SCI_EDITTOGGLEOVERTYPE:
4759 case SCI_CANCEL:
4760 case SCI_DELETEBACK:
4761 case SCI_TAB:
4762 case SCI_BACKTAB:
4763 case SCI_FORMFEED:
4764 case SCI_VCHOME:
4765 case SCI_VCHOMEEXTEND:
4766 case SCI_VCHOMEWRAP:
4767 case SCI_VCHOMEWRAPEXTEND:
4768 case SCI_DELWORDLEFT:
4769 case SCI_DELWORDRIGHT:
4770 case SCI_DELWORDRIGHTEND:
4771 case SCI_DELLINELEFT:
4772 case SCI_DELLINERIGHT:
4773 case SCI_LINECOPY:
4774 case SCI_LINECUT:
4775 case SCI_LINEDELETE:
4776 case SCI_LINETRANSPOSE:
4777 case SCI_LINEDUPLICATE:
4778 case SCI_LOWERCASE:
4779 case SCI_UPPERCASE:
4780 case SCI_LINESCROLLDOWN:
4781 case SCI_LINESCROLLUP:
4782 case SCI_DELETEBACKNOTLINE:
4783 case SCI_HOMEDISPLAY:
4784 case SCI_HOMEDISPLAYEXTEND:
4785 case SCI_LINEENDDISPLAY:
4786 case SCI_LINEENDDISPLAYEXTEND:
4787 case SCI_SETSELECTIONMODE:
4788 case SCI_LINEDOWNRECTEXTEND:
4789 case SCI_LINEUPRECTEXTEND:
4790 case SCI_CHARLEFTRECTEXTEND:
4791 case SCI_CHARRIGHTRECTEXTEND:
4792 case SCI_HOMERECTEXTEND:
4793 case SCI_VCHOMERECTEXTEND:
4794 case SCI_LINEENDRECTEXTEND:
4795 case SCI_PAGEUPRECTEXTEND:
4796 case SCI_PAGEDOWNRECTEXTEND:
4797 case SCI_SELECTIONDUPLICATE:
4798 case SCI_COPYALLOWLINE:
4799 case SCI_VERTICALCENTRECARET:
4800 case SCI_MOVESELECTEDLINESUP:
4801 case SCI_MOVESELECTEDLINESDOWN:
4802 case SCI_SCROLLTOSTART:
4803 case SCI_SCROLLTOEND:
4804 break;
4806 // Filter out all others like display changes. Also, newlines are redundant
4807 // with char insert messages.
4808 case SCI_NEWLINE:
4809 default:
4810 // printf("Filtered out %ld of macro recording\n", iMessage);
4811 return ;
4814 // Send notification
4815 SCNotification scn = {0};
4816 scn.nmhdr.code = SCN_MACRORECORD;
4817 scn.message = iMessage;
4818 scn.wParam = wParam;
4819 scn.lParam = lParam;
4820 NotifyParent(scn);
4823 // Something has changed that the container should know about
4824 void Editor::ContainerNeedsUpdate(int flags) {
4825 needUpdateUI |= flags;
4829 * Force scroll and keep position relative to top of window.
4831 * If stuttered = true and not already at first/last row, move to first/last row of window.
4832 * If stuttered = true and already at first/last row, scroll as normal.
4834 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
4835 int topLineNew;
4836 SelectionPosition newPos;
4838 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
4839 int topStutterLine = topLine + caretYSlop;
4840 int bottomStutterLine =
4841 pdoc->LineFromPosition(PositionFromLocation(
4842 Point(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
4843 - caretYSlop - 1;
4845 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
4846 topLineNew = topLine;
4847 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
4848 false, false, UserVirtualSpace());
4850 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
4851 topLineNew = topLine;
4852 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
4853 false, false, UserVirtualSpace());
4855 } else {
4856 Point pt = LocationFromPosition(sel.MainCaret());
4858 topLineNew = Platform::Clamp(
4859 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
4860 newPos = SPositionFromLocation(
4861 Point(lastXChosen - xOffset, pt.y + direction * (vs.lineHeight * LinesToScroll())),
4862 false, false, UserVirtualSpace());
4865 if (topLineNew != topLine) {
4866 SetTopLine(topLineNew);
4867 MovePositionTo(newPos, selt);
4868 Redraw();
4869 SetVerticalScrollPos();
4870 } else {
4871 MovePositionTo(newPos, selt);
4875 void Editor::ChangeCaseOfSelection(int caseMapping) {
4876 UndoGroup ug(pdoc);
4877 for (size_t r=0; r<sel.Count(); r++) {
4878 SelectionRange current = sel.Range(r);
4879 SelectionRange currentNoVS = current;
4880 currentNoVS.ClearVirtualSpace();
4881 char *text = CopyRange(currentNoVS.Start().Position(), currentNoVS.End().Position());
4882 size_t rangeBytes = currentNoVS.Length();
4883 if (rangeBytes > 0) {
4884 std::string sText(text, rangeBytes);
4886 std::string sMapped = CaseMapString(sText, caseMapping);
4888 if (sMapped != sText) {
4889 size_t firstDifference = 0;
4890 while (sMapped[firstDifference] == sText[firstDifference])
4891 firstDifference++;
4892 size_t lastDifference = sMapped.size() - 1;
4893 while (sMapped[lastDifference] == sText[lastDifference])
4894 lastDifference--;
4895 size_t endSame = sMapped.size() - 1 - lastDifference;
4896 pdoc->DeleteChars(
4897 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
4898 static_cast<int>(rangeBytes - firstDifference - endSame));
4899 pdoc->InsertString(
4900 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
4901 sMapped.c_str() + firstDifference,
4902 static_cast<int>(lastDifference - firstDifference + 1));
4903 // Automatic movement changes selection so reset to exactly the same as it was.
4904 sel.Range(r) = current;
4907 delete []text;
4911 void Editor::LineTranspose() {
4912 int line = pdoc->LineFromPosition(sel.MainCaret());
4913 if (line > 0) {
4914 UndoGroup ug(pdoc);
4915 int startPrev = pdoc->LineStart(line - 1);
4916 int endPrev = pdoc->LineEnd(line - 1);
4917 int start = pdoc->LineStart(line);
4918 int end = pdoc->LineEnd(line);
4919 char *line1 = CopyRange(startPrev, endPrev);
4920 int len1 = endPrev - startPrev;
4921 char *line2 = CopyRange(start, end);
4922 int len2 = end - start;
4923 pdoc->DeleteChars(start, len2);
4924 pdoc->DeleteChars(startPrev, len1);
4925 pdoc->InsertString(startPrev, line2, len2);
4926 pdoc->InsertString(start - len1 + len2, line1, len1);
4927 MovePositionTo(SelectionPosition(start - len1 + len2));
4928 delete []line1;
4929 delete []line2;
4933 void Editor::Duplicate(bool forLine) {
4934 if (sel.Empty()) {
4935 forLine = true;
4937 UndoGroup ug(pdoc, sel.Count() > 1);
4938 const char *eol = "";
4939 int eolLen = 0;
4940 if (forLine) {
4941 eol = StringFromEOLMode(pdoc->eolMode);
4942 eolLen = istrlen(eol);
4944 for (size_t r=0; r<sel.Count(); r++) {
4945 SelectionPosition start = sel.Range(r).Start();
4946 SelectionPosition end = sel.Range(r).End();
4947 if (forLine) {
4948 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4949 start = SelectionPosition(pdoc->LineStart(line));
4950 end = SelectionPosition(pdoc->LineEnd(line));
4952 char *text = CopyRange(start.Position(), end.Position());
4953 if (forLine)
4954 pdoc->InsertString(end.Position(), eol, eolLen);
4955 pdoc->InsertString(end.Position() + eolLen, text, SelectionRange(end, start).Length());
4956 delete []text;
4958 if (sel.Count() && sel.IsRectangular()) {
4959 SelectionPosition last = sel.Last();
4960 if (forLine) {
4961 int line = pdoc->LineFromPosition(last.Position());
4962 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
4964 if (sel.Rectangular().anchor > sel.Rectangular().caret)
4965 sel.Rectangular().anchor = last;
4966 else
4967 sel.Rectangular().caret = last;
4968 SetRectangularRange();
4972 void Editor::CancelModes() {
4973 sel.SetMoveExtends(false);
4976 void Editor::NewLine() {
4977 ClearSelection();
4978 const char *eol = "\n";
4979 if (pdoc->eolMode == SC_EOL_CRLF) {
4980 eol = "\r\n";
4981 } else if (pdoc->eolMode == SC_EOL_CR) {
4982 eol = "\r";
4983 } // else SC_EOL_LF -> "\n" already set
4984 if (pdoc->InsertCString(sel.MainCaret(), eol)) {
4985 SetEmptySelection(sel.MainCaret() + istrlen(eol));
4986 while (*eol) {
4987 NotifyChar(*eol);
4988 if (recordingMacro) {
4989 char txt[2];
4990 txt[0] = *eol;
4991 txt[1] = '\0';
4992 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
4994 eol++;
4997 SetLastXChosen();
4998 SetScrollBars();
4999 EnsureCaretVisible();
5000 // Avoid blinking during rapid typing:
5001 ShowCaretAtCurrentPosition();
5004 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
5005 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
5006 if (sel.IsRectangular()) {
5007 if (selt == Selection::noSel) {
5008 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
5009 } else {
5010 caretToUse = sel.Rectangular().caret;
5013 Point pt = LocationFromPosition(caretToUse);
5014 int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
5015 Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
5016 int subLine = (pt.y - ptStartLine.y) / vs.lineHeight;
5017 int commentLines = vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0;
5018 SelectionPosition posNew = SPositionFromLocation(
5019 Point(lastXChosen - xOffset, pt.y + direction * vs.lineHeight), false, false, UserVirtualSpace());
5020 if ((direction > 0) && (subLine >= (cs.GetHeight(lineDoc) - 1 - commentLines))) {
5021 posNew = SPositionFromLocation(
5022 Point(lastXChosen - xOffset, pt.y + (commentLines + 1) * vs.lineHeight), false, false, UserVirtualSpace());
5024 if (direction < 0) {
5025 // Line wrapping may lead to a location on the same line, so
5026 // seek back if that is the case.
5027 // There is an equivalent case when moving down which skips
5028 // over a line but as that does not trap the user it is fine.
5029 Point ptNew = LocationFromPosition(posNew.Position());
5030 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
5031 posNew.Add(- 1);
5032 posNew.SetVirtualSpace(0);
5033 ptNew = LocationFromPosition(posNew.Position());
5036 MovePositionTo(posNew, selt);
5039 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
5040 int lineDoc, savedPos = sel.MainCaret();
5041 do {
5042 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
5043 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
5044 if (direction > 0) {
5045 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
5046 if (selt == Selection::noSel) {
5047 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
5049 break;
5052 } while (!cs.GetVisible(lineDoc));
5055 int Editor::StartEndDisplayLine(int pos, bool start) {
5056 RefreshStyleData();
5057 int line = pdoc->LineFromPosition(pos);
5058 AutoSurface surface(this);
5059 AutoLineLayout ll(llc, RetrieveLineLayout(line));
5060 int posRet = INVALID_POSITION;
5061 if (surface && ll) {
5062 unsigned int posLineStart = pdoc->LineStart(line);
5063 LayoutLine(line, surface, vs, ll, wrapWidth);
5064 int posInLine = pos - posLineStart;
5065 if (posInLine <= ll->maxLineLength) {
5066 for (int subLine = 0; subLine < ll->lines; subLine++) {
5067 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
5068 if (start) {
5069 posRet = ll->LineStart(subLine) + posLineStart;
5070 } else {
5071 if (subLine == ll->lines - 1)
5072 posRet = ll->LineStart(subLine + 1) + posLineStart;
5073 else
5074 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
5080 if (posRet == INVALID_POSITION) {
5081 return pos;
5082 } else {
5083 return posRet;
5087 int Editor::KeyCommand(unsigned int iMessage) {
5088 switch (iMessage) {
5089 case SCI_LINEDOWN:
5090 CursorUpOrDown(1);
5091 break;
5092 case SCI_LINEDOWNEXTEND:
5093 CursorUpOrDown(1, Selection::selStream);
5094 break;
5095 case SCI_LINEDOWNRECTEXTEND:
5096 CursorUpOrDown(1, Selection::selRectangle);
5097 break;
5098 case SCI_PARADOWN:
5099 ParaUpOrDown(1);
5100 break;
5101 case SCI_PARADOWNEXTEND:
5102 ParaUpOrDown(1, Selection::selStream);
5103 break;
5104 case SCI_LINESCROLLDOWN:
5105 ScrollTo(topLine + 1);
5106 MoveCaretInsideView(false);
5107 break;
5108 case SCI_LINEUP:
5109 CursorUpOrDown(-1);
5110 break;
5111 case SCI_LINEUPEXTEND:
5112 CursorUpOrDown(-1, Selection::selStream);
5113 break;
5114 case SCI_LINEUPRECTEXTEND:
5115 CursorUpOrDown(-1, Selection::selRectangle);
5116 break;
5117 case SCI_PARAUP:
5118 ParaUpOrDown(-1);
5119 break;
5120 case SCI_PARAUPEXTEND:
5121 ParaUpOrDown(-1, Selection::selStream);
5122 break;
5123 case SCI_LINESCROLLUP:
5124 ScrollTo(topLine - 1);
5125 MoveCaretInsideView(false);
5126 break;
5127 case SCI_CHARLEFT:
5128 if (SelectionEmpty() || sel.MoveExtends()) {
5129 if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5130 SelectionPosition spCaret = sel.RangeMain().caret;
5131 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5132 MovePositionTo(spCaret);
5133 } else {
5134 MovePositionTo(MovePositionSoVisible(
5135 SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
5137 } else {
5138 MovePositionTo(sel.LimitsForRectangularElseMain().start);
5140 SetLastXChosen();
5141 break;
5142 case SCI_CHARLEFTEXTEND:
5143 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5144 SelectionPosition spCaret = sel.RangeMain().caret;
5145 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5146 MovePositionTo(spCaret, Selection::selStream);
5147 } else {
5148 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
5150 SetLastXChosen();
5151 break;
5152 case SCI_CHARLEFTRECTEXTEND:
5153 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5154 SelectionPosition spCaret = sel.RangeMain().caret;
5155 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5156 MovePositionTo(spCaret, Selection::selRectangle);
5157 } else {
5158 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
5160 SetLastXChosen();
5161 break;
5162 case SCI_CHARRIGHT:
5163 if (SelectionEmpty() || sel.MoveExtends()) {
5164 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5165 SelectionPosition spCaret = sel.RangeMain().caret;
5166 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5167 MovePositionTo(spCaret);
5168 } else {
5169 MovePositionTo(MovePositionSoVisible(
5170 SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
5172 } else {
5173 MovePositionTo(sel.LimitsForRectangularElseMain().end);
5175 SetLastXChosen();
5176 break;
5177 case SCI_CHARRIGHTEXTEND:
5178 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5179 SelectionPosition spCaret = sel.RangeMain().caret;
5180 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5181 MovePositionTo(spCaret, Selection::selStream);
5182 } else {
5183 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
5185 SetLastXChosen();
5186 break;
5187 case SCI_CHARRIGHTRECTEXTEND:
5188 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5189 SelectionPosition spCaret = sel.RangeMain().caret;
5190 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5191 MovePositionTo(spCaret, Selection::selRectangle);
5192 } else {
5193 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
5195 SetLastXChosen();
5196 break;
5197 case SCI_WORDLEFT:
5198 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
5199 SetLastXChosen();
5200 break;
5201 case SCI_WORDLEFTEXTEND:
5202 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
5203 SetLastXChosen();
5204 break;
5205 case SCI_WORDRIGHT:
5206 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
5207 SetLastXChosen();
5208 break;
5209 case SCI_WORDRIGHTEXTEND:
5210 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
5211 SetLastXChosen();
5212 break;
5214 case SCI_WORDLEFTEND:
5215 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
5216 SetLastXChosen();
5217 break;
5218 case SCI_WORDLEFTENDEXTEND:
5219 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
5220 SetLastXChosen();
5221 break;
5222 case SCI_WORDRIGHTEND:
5223 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
5224 SetLastXChosen();
5225 break;
5226 case SCI_WORDRIGHTENDEXTEND:
5227 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
5228 SetLastXChosen();
5229 break;
5231 case SCI_HOME:
5232 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5233 SetLastXChosen();
5234 break;
5235 case SCI_HOMEEXTEND:
5236 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
5237 SetLastXChosen();
5238 break;
5239 case SCI_HOMERECTEXTEND:
5240 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
5241 SetLastXChosen();
5242 break;
5243 case SCI_LINEEND:
5244 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
5245 SetLastXChosen();
5246 break;
5247 case SCI_LINEENDEXTEND:
5248 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
5249 SetLastXChosen();
5250 break;
5251 case SCI_LINEENDRECTEXTEND:
5252 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
5253 SetLastXChosen();
5254 break;
5255 case SCI_HOMEWRAP: {
5256 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5257 if (sel.RangeMain().caret <= homePos)
5258 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5259 MovePositionTo(homePos);
5260 SetLastXChosen();
5262 break;
5263 case SCI_HOMEWRAPEXTEND: {
5264 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5265 if (sel.RangeMain().caret <= homePos)
5266 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5267 MovePositionTo(homePos, Selection::selStream);
5268 SetLastXChosen();
5270 break;
5271 case SCI_LINEENDWRAP: {
5272 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5273 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5274 if (endPos > realEndPos // if moved past visible EOLs
5275 || sel.RangeMain().caret >= endPos) // if at end of display line already
5276 endPos = realEndPos;
5277 MovePositionTo(endPos);
5278 SetLastXChosen();
5280 break;
5281 case SCI_LINEENDWRAPEXTEND: {
5282 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5283 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5284 if (endPos > realEndPos // if moved past visible EOLs
5285 || sel.RangeMain().caret >= endPos) // if at end of display line already
5286 endPos = realEndPos;
5287 MovePositionTo(endPos, Selection::selStream);
5288 SetLastXChosen();
5290 break;
5291 case SCI_DOCUMENTSTART:
5292 MovePositionTo(0);
5293 SetLastXChosen();
5294 break;
5295 case SCI_DOCUMENTSTARTEXTEND:
5296 MovePositionTo(0, Selection::selStream);
5297 SetLastXChosen();
5298 break;
5299 case SCI_DOCUMENTEND:
5300 MovePositionTo(pdoc->Length());
5301 SetLastXChosen();
5302 break;
5303 case SCI_DOCUMENTENDEXTEND:
5304 MovePositionTo(pdoc->Length(), Selection::selStream);
5305 SetLastXChosen();
5306 break;
5307 case SCI_STUTTEREDPAGEUP:
5308 PageMove(-1, Selection::noSel, true);
5309 break;
5310 case SCI_STUTTEREDPAGEUPEXTEND:
5311 PageMove(-1, Selection::selStream, true);
5312 break;
5313 case SCI_STUTTEREDPAGEDOWN:
5314 PageMove(1, Selection::noSel, true);
5315 break;
5316 case SCI_STUTTEREDPAGEDOWNEXTEND:
5317 PageMove(1, Selection::selStream, true);
5318 break;
5319 case SCI_PAGEUP:
5320 PageMove(-1);
5321 break;
5322 case SCI_PAGEUPEXTEND:
5323 PageMove(-1, Selection::selStream);
5324 break;
5325 case SCI_PAGEUPRECTEXTEND:
5326 PageMove(-1, Selection::selRectangle);
5327 break;
5328 case SCI_PAGEDOWN:
5329 PageMove(1);
5330 break;
5331 case SCI_PAGEDOWNEXTEND:
5332 PageMove(1, Selection::selStream);
5333 break;
5334 case SCI_PAGEDOWNRECTEXTEND:
5335 PageMove(1, Selection::selRectangle);
5336 break;
5337 case SCI_EDITTOGGLEOVERTYPE:
5338 inOverstrike = !inOverstrike;
5339 DropCaret();
5340 ShowCaretAtCurrentPosition();
5341 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
5342 NotifyUpdateUI();
5343 break;
5344 case SCI_CANCEL: // Cancel any modes - handled in subclass
5345 // Also unselect text
5346 CancelModes();
5347 break;
5348 case SCI_DELETEBACK:
5349 DelCharBack(true);
5350 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5351 SetLastXChosen();
5353 EnsureCaretVisible();
5354 break;
5355 case SCI_DELETEBACKNOTLINE:
5356 DelCharBack(false);
5357 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5358 SetLastXChosen();
5360 EnsureCaretVisible();
5361 break;
5362 case SCI_TAB:
5363 Indent(true);
5364 if (caretSticky == SC_CARETSTICKY_OFF) {
5365 SetLastXChosen();
5367 EnsureCaretVisible();
5368 ShowCaretAtCurrentPosition(); // Avoid blinking
5369 break;
5370 case SCI_BACKTAB:
5371 Indent(false);
5372 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5373 SetLastXChosen();
5375 EnsureCaretVisible();
5376 ShowCaretAtCurrentPosition(); // Avoid blinking
5377 break;
5378 case SCI_NEWLINE:
5379 NewLine();
5380 break;
5381 case SCI_FORMFEED:
5382 AddChar('\f');
5383 break;
5384 case SCI_VCHOME:
5385 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
5386 SetLastXChosen();
5387 break;
5388 case SCI_VCHOMEEXTEND:
5389 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
5390 SetLastXChosen();
5391 break;
5392 case SCI_VCHOMERECTEXTEND:
5393 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
5394 SetLastXChosen();
5395 break;
5396 case SCI_VCHOMEWRAP: {
5397 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5398 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5399 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5400 homePos = viewLineStart;
5402 MovePositionTo(homePos);
5403 SetLastXChosen();
5405 break;
5406 case SCI_VCHOMEWRAPEXTEND: {
5407 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5408 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5409 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5410 homePos = viewLineStart;
5412 MovePositionTo(homePos, Selection::selStream);
5413 SetLastXChosen();
5415 break;
5416 case SCI_ZOOMIN:
5417 if (vs.zoomLevel < 20) {
5418 vs.zoomLevel++;
5419 InvalidateStyleRedraw();
5420 NotifyZoom();
5422 break;
5423 case SCI_ZOOMOUT:
5424 if (vs.zoomLevel > -10) {
5425 vs.zoomLevel--;
5426 InvalidateStyleRedraw();
5427 NotifyZoom();
5429 break;
5430 case SCI_DELWORDLEFT: {
5431 int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
5432 pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
5433 sel.RangeMain().ClearVirtualSpace();
5434 SetLastXChosen();
5436 break;
5437 case SCI_DELWORDRIGHT: {
5438 UndoGroup ug(pdoc);
5439 sel.RangeMain().caret = SelectionPosition(
5440 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5441 sel.RangeMain().anchor = sel.RangeMain().caret;
5442 int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
5443 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5445 break;
5446 case SCI_DELWORDRIGHTEND: {
5447 UndoGroup ug(pdoc);
5448 sel.RangeMain().caret = SelectionPosition(
5449 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5450 int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
5451 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5453 break;
5454 case SCI_DELLINELEFT: {
5455 int line = pdoc->LineFromPosition(sel.MainCaret());
5456 int start = pdoc->LineStart(line);
5457 pdoc->DeleteChars(start, sel.MainCaret() - start);
5458 sel.RangeMain().ClearVirtualSpace();
5459 SetLastXChosen();
5461 break;
5462 case SCI_DELLINERIGHT: {
5463 int line = pdoc->LineFromPosition(sel.MainCaret());
5464 int end = pdoc->LineEnd(line);
5465 pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
5467 break;
5468 case SCI_LINECOPY: {
5469 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5470 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5471 CopyRangeToClipboard(pdoc->LineStart(lineStart),
5472 pdoc->LineStart(lineEnd + 1));
5474 break;
5475 case SCI_LINECUT: {
5476 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5477 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5478 int start = pdoc->LineStart(lineStart);
5479 int end = pdoc->LineStart(lineEnd + 1);
5480 SetSelection(start, end);
5481 Cut();
5482 SetLastXChosen();
5484 break;
5485 case SCI_LINEDELETE: {
5486 int line = pdoc->LineFromPosition(sel.MainCaret());
5487 int start = pdoc->LineStart(line);
5488 int end = pdoc->LineStart(line + 1);
5489 pdoc->DeleteChars(start, end - start);
5491 break;
5492 case SCI_LINETRANSPOSE:
5493 LineTranspose();
5494 break;
5495 case SCI_LINEDUPLICATE:
5496 Duplicate(true);
5497 break;
5498 case SCI_SELECTIONDUPLICATE:
5499 Duplicate(false);
5500 break;
5501 case SCI_LOWERCASE:
5502 ChangeCaseOfSelection(cmLower);
5503 break;
5504 case SCI_UPPERCASE:
5505 ChangeCaseOfSelection(cmUpper);
5506 break;
5507 case SCI_WORDPARTLEFT:
5508 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
5509 SetLastXChosen();
5510 break;
5511 case SCI_WORDPARTLEFTEXTEND:
5512 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
5513 SetLastXChosen();
5514 break;
5515 case SCI_WORDPARTRIGHT:
5516 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
5517 SetLastXChosen();
5518 break;
5519 case SCI_WORDPARTRIGHTEXTEND:
5520 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
5521 SetLastXChosen();
5522 break;
5523 case SCI_HOMEDISPLAY:
5524 MovePositionTo(MovePositionSoVisible(
5525 StartEndDisplayLine(sel.MainCaret(), true), -1));
5526 SetLastXChosen();
5527 break;
5528 case SCI_HOMEDISPLAYEXTEND:
5529 MovePositionTo(MovePositionSoVisible(
5530 StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
5531 SetLastXChosen();
5532 break;
5533 case SCI_LINEENDDISPLAY:
5534 MovePositionTo(MovePositionSoVisible(
5535 StartEndDisplayLine(sel.MainCaret(), false), 1));
5536 SetLastXChosen();
5537 break;
5538 case SCI_LINEENDDISPLAYEXTEND:
5539 MovePositionTo(MovePositionSoVisible(
5540 StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
5541 SetLastXChosen();
5542 break;
5543 case SCI_SCROLLTOSTART:
5544 ScrollTo(0);
5545 break;
5546 case SCI_SCROLLTOEND:
5547 ScrollTo(MaxScrollPos());
5548 break;
5550 return 0;
5553 int Editor::KeyDefault(int, int) {
5554 return 0;
5557 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
5558 DwellEnd(false);
5559 int msg = kmap.Find(key, modifiers);
5560 if (msg) {
5561 if (consumed)
5562 *consumed = true;
5563 return WndProc(msg, 0, 0);
5564 } else {
5565 if (consumed)
5566 *consumed = false;
5567 return KeyDefault(key, modifiers);
5571 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
5572 int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
5573 (alt ? SCI_ALT : 0);
5574 return KeyDownWithModifiers(key, modifiers, consumed);
5577 void Editor::SetWhitespaceVisible(int view) {
5578 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(view);
5581 int Editor::GetWhitespaceVisible() {
5582 return vs.viewWhitespace;
5585 void Editor::Indent(bool forwards) {
5586 for (size_t r=0; r<sel.Count(); r++) {
5587 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
5588 int caretPosition = sel.Range(r).caret.Position();
5589 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
5590 if (lineOfAnchor == lineCurrentPos) {
5591 if (forwards) {
5592 UndoGroup ug(pdoc);
5593 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
5594 caretPosition = sel.Range(r).caret.Position();
5595 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
5596 pdoc->tabIndents) {
5597 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5598 int indentationStep = pdoc->IndentSize();
5599 pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
5600 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5601 } else {
5602 if (pdoc->useTabs) {
5603 pdoc->InsertChar(caretPosition, '\t');
5604 sel.Range(r) = SelectionRange(caretPosition+1);
5605 } else {
5606 int numSpaces = (pdoc->tabInChars) -
5607 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
5608 if (numSpaces < 1)
5609 numSpaces = pdoc->tabInChars;
5610 for (int i = 0; i < numSpaces; i++) {
5611 pdoc->InsertChar(caretPosition + i, ' ');
5613 sel.Range(r) = SelectionRange(caretPosition+numSpaces);
5616 } else {
5617 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
5618 pdoc->tabIndents) {
5619 UndoGroup ug(pdoc);
5620 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5621 int indentationStep = pdoc->IndentSize();
5622 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
5623 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5624 } else {
5625 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
5626 pdoc->tabInChars;
5627 if (newColumn < 0)
5628 newColumn = 0;
5629 int newPos = caretPosition;
5630 while (pdoc->GetColumn(newPos) > newColumn)
5631 newPos--;
5632 sel.Range(r) = SelectionRange(newPos);
5635 } else { // Multiline
5636 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
5637 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
5638 // Multiple lines selected so indent / dedent
5639 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
5640 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
5641 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
5642 lineBottomSel--; // If not selecting any characters on a line, do not indent
5644 UndoGroup ug(pdoc);
5645 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
5647 if (lineOfAnchor < lineCurrentPos) {
5648 if (currentPosPosOnLine == 0)
5649 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5650 else
5651 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
5652 } else {
5653 if (anchorPosOnLine == 0)
5654 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5655 else
5656 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
5662 class CaseFolderASCII : public CaseFolderTable {
5663 public:
5664 CaseFolderASCII() {
5665 StandardASCII();
5667 ~CaseFolderASCII() {
5669 virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
5670 if (lenMixed > sizeFolded) {
5671 return 0;
5672 } else {
5673 for (size_t i=0; i<lenMixed; i++) {
5674 folded[i] = mapping[static_cast<unsigned char>(mixed[i])];
5676 return lenMixed;
5682 CaseFolder *Editor::CaseFolderForEncoding() {
5683 // Simple default that only maps ASCII upper case to lower case.
5684 return new CaseFolderASCII();
5688 * Search of a text in the document, in the given range.
5689 * @return The position of the found text, -1 if not found.
5691 long Editor::FindText(
5692 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5693 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5694 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
5696 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
5697 int lengthFound = istrlen(ft->lpstrText);
5698 std::auto_ptr<CaseFolder> pcf(CaseFolderForEncoding());
5699 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
5700 (wParam & SCFIND_MATCHCASE) != 0,
5701 (wParam & SCFIND_WHOLEWORD) != 0,
5702 (wParam & SCFIND_WORDSTART) != 0,
5703 (wParam & SCFIND_REGEXP) != 0,
5704 wParam,
5705 &lengthFound,
5706 pcf.get());
5707 if (pos != -1) {
5708 ft->chrgText.cpMin = pos;
5709 ft->chrgText.cpMax = pos + lengthFound;
5711 return pos;
5715 * Relocatable search support : Searches relative to current selection
5716 * point and sets the selection to the found text range with
5717 * each search.
5720 * Anchor following searches at current selection start: This allows
5721 * multiple incremental interactive searches to be macro recorded
5722 * while still setting the selection to found text so the find/select
5723 * operation is self-contained.
5725 void Editor::SearchAnchor() {
5726 searchAnchor = SelectionStart().Position();
5730 * Find text from current search anchor: Must call @c SearchAnchor first.
5731 * Used for next text and previous text requests.
5732 * @return The position of the found text, -1 if not found.
5734 long Editor::SearchText(
5735 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
5736 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5737 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5738 sptr_t lParam) { ///< The text to search for.
5740 const char *txt = reinterpret_cast<char *>(lParam);
5741 int pos;
5742 int lengthFound = istrlen(txt);
5743 std::auto_ptr<CaseFolder> pcf(CaseFolderForEncoding());
5744 if (iMessage == SCI_SEARCHNEXT) {
5745 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
5746 (wParam & SCFIND_MATCHCASE) != 0,
5747 (wParam & SCFIND_WHOLEWORD) != 0,
5748 (wParam & SCFIND_WORDSTART) != 0,
5749 (wParam & SCFIND_REGEXP) != 0,
5750 wParam,
5751 &lengthFound,
5752 pcf.get());
5753 } else {
5754 pos = pdoc->FindText(searchAnchor, 0, txt,
5755 (wParam & SCFIND_MATCHCASE) != 0,
5756 (wParam & SCFIND_WHOLEWORD) != 0,
5757 (wParam & SCFIND_WORDSTART) != 0,
5758 (wParam & SCFIND_REGEXP) != 0,
5759 wParam,
5760 &lengthFound,
5761 pcf.get());
5763 if (pos != -1) {
5764 SetSelection(pos, pos + lengthFound);
5767 return pos;
5770 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
5771 std::string ret(s);
5772 for (size_t i=0; i<ret.size(); i++) {
5773 switch (caseMapping) {
5774 case cmUpper:
5775 if (ret[i] >= 'a' && ret[i] <= 'z')
5776 ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
5777 break;
5778 case cmLower:
5779 if (ret[i] >= 'A' && ret[i] <= 'Z')
5780 ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
5781 break;
5784 return ret;
5788 * Search for text in the target range of the document.
5789 * @return The position of the found text, -1 if not found.
5791 long Editor::SearchInTarget(const char *text, int length) {
5792 int lengthFound = length;
5794 std::auto_ptr<CaseFolder> pcf(CaseFolderForEncoding());
5795 int pos = pdoc->FindText(targetStart, targetEnd, text,
5796 (searchFlags & SCFIND_MATCHCASE) != 0,
5797 (searchFlags & SCFIND_WHOLEWORD) != 0,
5798 (searchFlags & SCFIND_WORDSTART) != 0,
5799 (searchFlags & SCFIND_REGEXP) != 0,
5800 searchFlags,
5801 &lengthFound,
5802 pcf.get());
5803 if (pos != -1) {
5804 targetStart = pos;
5805 targetEnd = pos + lengthFound;
5807 return pos;
5810 void Editor::GoToLine(int lineNo) {
5811 if (lineNo > pdoc->LinesTotal())
5812 lineNo = pdoc->LinesTotal();
5813 if (lineNo < 0)
5814 lineNo = 0;
5815 SetEmptySelection(pdoc->LineStart(lineNo));
5816 ShowCaretAtCurrentPosition();
5817 EnsureCaretVisible();
5820 static bool Close(Point pt1, Point pt2) {
5821 if (abs(pt1.x - pt2.x) > 3)
5822 return false;
5823 if (abs(pt1.y - pt2.y) > 3)
5824 return false;
5825 return true;
5828 char *Editor::CopyRange(int start, int end) {
5829 char *text = 0;
5830 if (start < end) {
5831 int len = end - start;
5832 text = new char[len + 1];
5833 for (int i = 0; i < len; i++) {
5834 text[i] = pdoc->CharAt(start + i);
5836 text[len] = '\0';
5838 return text;
5841 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
5842 if (sel.Empty()) {
5843 if (allowLineCopy) {
5844 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
5845 int start = pdoc->LineStart(currentLine);
5846 int end = pdoc->LineEnd(currentLine);
5848 char *text = CopyRange(start, end);
5849 size_t textLen = text ? strlen(text) : 0;
5850 // include room for \r\n\0
5851 textLen += 3;
5852 char *textWithEndl = new char[textLen];
5853 textWithEndl[0] = '\0';
5854 if (text)
5855 strncat(textWithEndl, text, textLen);
5856 if (pdoc->eolMode != SC_EOL_LF)
5857 strncat(textWithEndl, "\r", textLen);
5858 if (pdoc->eolMode != SC_EOL_CR)
5859 strncat(textWithEndl, "\n", textLen);
5860 ss->Set(textWithEndl, static_cast<int>(strlen(textWithEndl) + 1),
5861 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, true);
5862 delete []text;
5864 } else {
5865 int delimiterLength = 0;
5866 if (sel.selType == Selection::selRectangle) {
5867 if (pdoc->eolMode == SC_EOL_CRLF) {
5868 delimiterLength = 2;
5869 } else {
5870 delimiterLength = 1;
5873 size_t size = sel.Length() + delimiterLength * sel.Count();
5874 char *text = new char[size + 1];
5875 int j = 0;
5876 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
5877 if (sel.selType == Selection::selRectangle)
5878 std::sort(rangesInOrder.begin(), rangesInOrder.end());
5879 for (size_t r=0; r<rangesInOrder.size(); r++) {
5880 SelectionRange current = rangesInOrder[r];
5881 for (int i = current.Start().Position();
5882 i < current.End().Position();
5883 i++) {
5884 text[j++] = pdoc->CharAt(i);
5886 if (sel.selType == Selection::selRectangle) {
5887 if (pdoc->eolMode != SC_EOL_LF) {
5888 text[j++] = '\r';
5890 if (pdoc->eolMode != SC_EOL_CR) {
5891 text[j++] = '\n';
5895 text[size] = '\0';
5896 ss->Set(text, static_cast<int>(size + 1), pdoc->dbcsCodePage,
5897 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
5901 void Editor::CopyRangeToClipboard(int start, int end) {
5902 start = pdoc->ClampPositionIntoDocument(start);
5903 end = pdoc->ClampPositionIntoDocument(end);
5904 SelectionText selectedText;
5905 selectedText.Set(CopyRange(start, end), end - start + 1,
5906 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5907 CopyToClipboard(selectedText);
5910 void Editor::CopyText(int length, const char *text) {
5911 SelectionText selectedText;
5912 selectedText.Copy(text, length + 1,
5913 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5914 CopyToClipboard(selectedText);
5917 void Editor::SetDragPosition(SelectionPosition newPos) {
5918 if (newPos.Position() >= 0) {
5919 newPos = MovePositionOutsideChar(newPos, 1);
5920 posDrop = newPos;
5922 if (!(posDrag == newPos)) {
5923 caret.on = true;
5924 SetTicking(true);
5925 InvalidateCaret();
5926 posDrag = newPos;
5927 InvalidateCaret();
5931 void Editor::DisplayCursor(Window::Cursor c) {
5932 if (cursorMode == SC_CURSORNORMAL)
5933 wMain.SetCursor(c);
5934 else
5935 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
5938 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
5939 int xMove = ptStart.x - ptNow.x;
5940 int yMove = ptStart.y - ptNow.y;
5941 int distanceSquared = xMove * xMove + yMove * yMove;
5942 return distanceSquared > 16;
5945 void Editor::StartDrag() {
5946 // Always handled by subclasses
5947 //SetMouseCapture(true);
5948 //DisplayCursor(Window::cursorArrow);
5951 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
5952 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
5953 if (inDragDrop == ddDragging)
5954 dropWentOutside = false;
5956 bool positionWasInSelection = PositionInSelection(position.Position());
5958 bool positionOnEdgeOfSelection =
5959 (position == SelectionStart()) || (position == SelectionEnd());
5961 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
5962 (positionOnEdgeOfSelection && !moving)) {
5964 SelectionPosition selStart = SelectionStart();
5965 SelectionPosition selEnd = SelectionEnd();
5967 UndoGroup ug(pdoc);
5969 SelectionPosition positionAfterDeletion = position;
5970 if ((inDragDrop == ddDragging) && moving) {
5971 // Remove dragged out text
5972 if (rectangular || sel.selType == Selection::selLines) {
5973 for (size_t r=0; r<sel.Count(); r++) {
5974 if (position >= sel.Range(r).Start()) {
5975 if (position > sel.Range(r).End()) {
5976 positionAfterDeletion.Add(-sel.Range(r).Length());
5977 } else {
5978 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
5982 } else {
5983 if (position > selStart) {
5984 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
5987 ClearSelection();
5989 position = positionAfterDeletion;
5991 if (rectangular) {
5992 PasteRectangular(position, value, istrlen(value));
5993 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
5994 SetEmptySelection(position);
5995 } else {
5996 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
5997 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
5998 if (pdoc->InsertCString(position.Position(), value)) {
5999 SelectionPosition posAfterInsertion = position;
6000 posAfterInsertion.Add(istrlen(value));
6001 SetSelection(posAfterInsertion, position);
6004 } else if (inDragDrop == ddDragging) {
6005 SetEmptySelection(position);
6010 * @return true if given position is inside the selection,
6012 bool Editor::PositionInSelection(int pos) {
6013 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
6014 for (size_t r=0; r<sel.Count(); r++) {
6015 if (sel.Range(r).Contains(pos))
6016 return true;
6018 return false;
6021 bool Editor::PointInSelection(Point pt) {
6022 SelectionPosition pos = SPositionFromLocation(pt, false, true);
6023 Point ptPos = LocationFromPosition(pos);
6024 for (size_t r=0; r<sel.Count(); r++) {
6025 SelectionRange range = sel.Range(r);
6026 if (range.Contains(pos)) {
6027 bool hit = true;
6028 if (pos == range.Start()) {
6029 // see if just before selection
6030 if (pt.x < ptPos.x) {
6031 hit = false;
6034 if (pos == range.End()) {
6035 // see if just after selection
6036 if (pt.x > ptPos.x) {
6037 hit = false;
6040 if (hit)
6041 return true;
6044 return false;
6047 bool Editor::PointInSelMargin(Point pt) {
6048 // Really means: "Point in a margin"
6049 if (vs.fixedColumnWidth > 0) { // There is a margin
6050 PRectangle rcSelMargin = GetClientRectangle();
6051 rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth;
6052 return rcSelMargin.Contains(pt);
6053 } else {
6054 return false;
6058 Window::Cursor Editor::GetMarginCursor(Point pt) {
6059 int x = 0;
6060 for (int margin = 0; margin < ViewStyle::margins; margin++) {
6061 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
6062 return static_cast<Window::Cursor>(vs.ms[margin].cursor);
6063 x += vs.ms[margin].width;
6065 return Window::cursorReverseArrow;
6068 void Editor::LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine) {
6069 int selCurrentPos, selAnchorPos;
6070 if (wholeLine) {
6071 int lineCurrent_ = pdoc->LineFromPosition(lineCurrentPos_);
6072 int lineAnchor_ = pdoc->LineFromPosition(lineAnchorPos_);
6073 if (lineAnchorPos_ < lineCurrentPos_) {
6074 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
6075 selAnchorPos = pdoc->LineStart(lineAnchor_);
6076 } else if (lineAnchorPos_ > lineCurrentPos_) {
6077 selCurrentPos = pdoc->LineStart(lineCurrent_);
6078 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
6079 } else { // Same line, select it
6080 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
6081 selAnchorPos = pdoc->LineStart(lineAnchor_);
6083 } else {
6084 if (lineAnchorPos_ < lineCurrentPos_) {
6085 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
6086 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6087 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6088 } else if (lineAnchorPos_ > lineCurrentPos_) {
6089 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
6090 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6091 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
6092 } else { // Same line, select it
6093 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6094 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6095 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6098 SetSelection(selCurrentPos, selAnchorPos);
6101 void Editor::WordSelection(int pos) {
6102 if (pos < wordSelectAnchorStartPos) {
6103 // Extend backward to the word containing pos.
6104 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
6105 // This ensures that a series of empty lines isn't counted as a single "word".
6106 if (!pdoc->IsLineEndPosition(pos))
6107 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
6108 SetSelection(pos, wordSelectAnchorEndPos);
6109 } else if (pos > wordSelectAnchorEndPos) {
6110 // Extend forward to the word containing the character to the left of pos.
6111 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
6112 // This ensures that a series of empty lines isn't counted as a single "word".
6113 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
6114 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
6115 SetSelection(pos, wordSelectAnchorStartPos);
6116 } else {
6117 // Select only the anchored word
6118 if (pos >= originalAnchorPos)
6119 SetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
6120 else
6121 SetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
6125 void Editor::DwellEnd(bool mouseMoved) {
6126 if (mouseMoved)
6127 ticksToDwell = dwellDelay;
6128 else
6129 ticksToDwell = SC_TIME_FOREVER;
6130 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
6131 dwelling = false;
6132 NotifyDwelling(ptMouseLast, dwelling);
6136 void Editor::MouseLeave() {
6137 SetHotSpotRange(NULL);
6138 if (!HaveMouseCapture()) {
6139 ptMouseLast = Point(-1,-1);
6140 DwellEnd(true);
6144 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
6145 return ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0)
6146 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
6149 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
6150 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
6151 ptMouseLast = pt;
6152 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
6153 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6154 inDragDrop = ddNone;
6155 sel.SetMoveExtends(false);
6157 bool processed = NotifyMarginClick(pt, shift, ctrl, alt);
6158 if (processed)
6159 return;
6161 NotifyIndicatorClick(true, newPos.Position(), shift, ctrl, alt);
6163 bool inSelMargin = PointInSelMargin(pt);
6164 if (shift & !inSelMargin) {
6165 SetSelection(newPos.Position());
6167 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
6168 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
6169 SetMouseCapture(true);
6170 SetEmptySelection(newPos.Position());
6171 bool doubleClick = false;
6172 // Stop mouse button bounce changing selection type
6173 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
6174 if (selectionType == selChar) {
6175 selectionType = selWord;
6176 doubleClick = true;
6177 } else if (selectionType == selWord) {
6178 // Since we ended up here, we're inside a *triple* click, which should always select
6179 // whole line irregardless of word wrap being enabled or not.
6180 selectionType = selWholeLine;
6181 } else {
6182 if (inSelMargin) {
6183 // Selection type is either selSubLine or selWholeLine here and we're inside margin.
6184 // If it is selSubLine, we're inside a *double* click and word wrap is enabled,
6185 // so we switch to selWholeLine in order to select whole line.
6186 if (selectionType == selSubLine)
6187 selectionType = selWholeLine;
6188 } else {
6189 selectionType = selChar;
6190 originalAnchorPos = sel.MainCaret();
6195 if (selectionType == selWord) {
6196 int charPos = originalAnchorPos;
6197 if (sel.MainCaret() == originalAnchorPos) {
6198 charPos = PositionFromLocation(pt, false, true);
6199 charPos = MovePositionOutsideChar(charPos, -1);
6202 int startWord, endWord;
6203 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
6204 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
6205 endWord = pdoc->ExtendWordSelect(charPos, 1);
6206 } else {
6207 // Selecting backwards, or anchor beyond last character on line. In these cases,
6208 // we select the word containing the character to the *left* of the anchor.
6209 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
6210 startWord = pdoc->ExtendWordSelect(charPos, -1);
6211 endWord = pdoc->ExtendWordSelect(startWord, 1);
6212 } else {
6213 // Anchor at start of line; select nothing to begin with.
6214 startWord = charPos;
6215 endWord = charPos;
6219 wordSelectAnchorStartPos = startWord;
6220 wordSelectAnchorEndPos = endWord;
6221 wordSelectInitialCaretPos = sel.MainCaret();
6222 WordSelection(wordSelectInitialCaretPos);
6223 } else if (selectionType == selSubLine || selectionType == selWholeLine) {
6224 lineAnchorPos = newPos.Position();
6225 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6226 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
6227 } else {
6228 SetEmptySelection(sel.MainCaret());
6230 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
6231 if (doubleClick) {
6232 NotifyDoubleClick(pt, shift, ctrl, alt);
6233 if (PositionIsHotspot(newPos.Position()))
6234 NotifyHotSpotDoubleClicked(newPos.Position(), shift, ctrl, alt);
6236 } else { // Single click
6237 if (inSelMargin) {
6238 sel.selType = Selection::selStream;
6239 if (ctrl) {
6240 SelectAll();
6241 lastClickTime = curTime;
6242 return;
6244 if (!shift) {
6245 // Single click in margin: select whole line or only subline if word wrap is enabled
6246 lineAnchorPos = newPos.Position();
6247 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6248 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6249 } else {
6250 // Single shift+click in margin: select from line anchor to clicked line
6251 if (sel.MainAnchor() > sel.MainCaret())
6252 lineAnchorPos = sel.MainAnchor() - 1;
6253 else
6254 lineAnchorPos = sel.MainAnchor();
6255 // Reset selection type if there is an empty selection.
6256 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
6257 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
6258 // This ensures that we continue selecting in the same selection mode.
6259 if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
6260 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6261 LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
6264 SetDragPosition(SelectionPosition(invalidPosition));
6265 SetMouseCapture(true);
6266 } else {
6267 if (PointIsHotspot(pt)) {
6268 NotifyHotSpotClicked(newPos.Position(), shift, ctrl, alt);
6269 hotSpotClickPos = PositionFromLocation(pt,true,false);
6271 if (!shift) {
6272 if (PointInSelection(pt) && !SelectionEmpty())
6273 inDragDrop = ddInitial;
6274 else
6275 inDragDrop = ddNone;
6277 SetMouseCapture(true);
6278 if (inDragDrop != ddInitial) {
6279 SetDragPosition(SelectionPosition(invalidPosition));
6280 if (!shift) {
6281 if (ctrl && multipleSelection) {
6282 SelectionRange range(newPos);
6283 sel.TentativeSelection(range);
6284 InvalidateSelection(range, true);
6285 } else {
6286 InvalidateSelection(SelectionRange(newPos), true);
6287 if (sel.Count() > 1)
6288 Redraw();
6289 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
6290 sel.Clear();
6291 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6292 SetSelection(newPos, newPos);
6295 SelectionPosition anchorCurrent = newPos;
6296 if (shift)
6297 anchorCurrent = sel.IsRectangular() ?
6298 sel.Rectangular().anchor : sel.RangeMain().anchor;
6299 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6300 selectionType = selChar;
6301 originalAnchorPos = sel.MainCaret();
6302 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
6303 SetRectangularRange();
6307 lastClickTime = curTime;
6308 lastXChosen = pt.x + xOffset;
6309 ShowCaretAtCurrentPosition();
6312 bool Editor::PositionIsHotspot(int position) {
6313 return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
6316 bool Editor::PointIsHotspot(Point pt) {
6317 int pos = PositionFromLocation(pt, true);
6318 if (pos == INVALID_POSITION)
6319 return false;
6320 return PositionIsHotspot(pos);
6323 void Editor::SetHotSpotRange(Point *pt) {
6324 if (pt) {
6325 int pos = PositionFromLocation(*pt);
6327 // If we don't limit this to word characters then the
6328 // range can encompass more than the run range and then
6329 // the underline will not be drawn properly.
6330 int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
6331 int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
6333 // Only invalidate the range if the hotspot range has changed...
6334 if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
6335 if (hsStart != -1) {
6336 InvalidateRange(hsStart, hsEnd);
6338 hsStart = hsStart_;
6339 hsEnd = hsEnd_;
6340 InvalidateRange(hsStart, hsEnd);
6342 } else {
6343 if (hsStart != -1) {
6344 int hsStart_ = hsStart;
6345 int hsEnd_ = hsEnd;
6346 hsStart = -1;
6347 hsEnd = -1;
6348 InvalidateRange(hsStart_, hsEnd_);
6349 } else {
6350 hsStart = -1;
6351 hsEnd = -1;
6356 void Editor::GetHotSpotRange(int &hsStart_, int &hsEnd_) {
6357 hsStart_ = hsStart;
6358 hsEnd_ = hsEnd;
6361 void Editor::ButtonMove(Point pt) {
6362 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
6363 DwellEnd(true);
6366 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
6367 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6368 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
6370 if (inDragDrop == ddInitial) {
6371 if (DragThreshold(ptMouseLast, pt)) {
6372 SetMouseCapture(false);
6373 SetDragPosition(movePos);
6374 CopySelectionRange(&drag);
6375 StartDrag();
6377 return;
6380 ptMouseLast = pt;
6381 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
6382 if (HaveMouseCapture()) {
6384 // Slow down autoscrolling/selection
6385 autoScrollTimer.ticksToWait -= timer.tickSize;
6386 if (autoScrollTimer.ticksToWait > 0)
6387 return;
6388 autoScrollTimer.ticksToWait = autoScrollDelay;
6390 // Adjust selection
6391 if (posDrag.IsValid()) {
6392 SetDragPosition(movePos);
6393 } else {
6394 if (selectionType == selChar) {
6395 if (sel.IsRectangular()) {
6396 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
6397 SetSelection(movePos, sel.RangeMain().anchor);
6398 } else if (sel.Count() > 1) {
6399 SelectionRange range(movePos, sel.RangeMain().anchor);
6400 sel.TentativeSelection(range);
6401 InvalidateSelection(range, true);
6402 } else {
6403 SetSelection(movePos, sel.RangeMain().anchor);
6405 } else if (selectionType == selWord) {
6406 // Continue selecting by word
6407 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
6408 // No need to do anything. Previously this case was lumped
6409 // in with "Moved forward", but that can be harmful in this
6410 // case: a handler for the NotifyDoubleClick re-adjusts
6411 // the selection for a fancier definition of "word" (for
6412 // example, in Perl it is useful to include the leading
6413 // '$', '%' or '@' on variables for word selection). In this
6414 // the ButtonMove() called via Tick() for auto-scrolling
6415 // could result in the fancier word selection adjustment
6416 // being unmade.
6417 } else {
6418 wordSelectInitialCaretPos = -1;
6419 WordSelection(movePos.Position());
6421 } else {
6422 // Continue selecting by line
6423 LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
6427 // Autoscroll
6428 PRectangle rcClient = GetClientRectangle();
6429 int lineMove = DisplayFromPosition(movePos.Position());
6430 if (pt.y > rcClient.bottom) {
6431 ScrollTo(lineMove - LinesOnScreen() + 1);
6432 Redraw();
6433 } else if (pt.y < rcClient.top) {
6434 ScrollTo(lineMove);
6435 Redraw();
6437 EnsureCaretVisible(false, false, true);
6439 if (hsStart != -1 && !PositionIsHotspot(movePos.Position()))
6440 SetHotSpotRange(NULL);
6442 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,false) != hotSpotClickPos) {
6443 if (inDragDrop == ddNone) {
6444 DisplayCursor(Window::cursorText);
6446 hotSpotClickPos = INVALID_POSITION;
6449 } else {
6450 if (vs.fixedColumnWidth > 0) { // There is a margin
6451 if (PointInSelMargin(pt)) {
6452 DisplayCursor(GetMarginCursor(pt));
6453 SetHotSpotRange(NULL);
6454 return; // No need to test for selection
6457 // Display regular (drag) cursor over selection
6458 if (PointInSelection(pt) && !SelectionEmpty()) {
6459 DisplayCursor(Window::cursorArrow);
6460 } else if (PointIsHotspot(pt)) {
6461 DisplayCursor(Window::cursorHand);
6462 SetHotSpotRange(&pt);
6463 } else {
6464 DisplayCursor(Window::cursorText);
6465 SetHotSpotRange(NULL);
6470 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
6471 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
6472 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
6473 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6474 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6475 if (inDragDrop == ddInitial) {
6476 inDragDrop = ddNone;
6477 SetEmptySelection(newPos.Position());
6478 selectionType = selChar;
6479 originalAnchorPos = sel.MainCaret();
6481 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
6482 hotSpotClickPos = INVALID_POSITION;
6483 NotifyHotSpotReleaseClick(newPos.Position(), false, ctrl, false);
6485 if (HaveMouseCapture()) {
6486 if (PointInSelMargin(pt)) {
6487 DisplayCursor(GetMarginCursor(pt));
6488 } else {
6489 DisplayCursor(Window::cursorText);
6490 SetHotSpotRange(NULL);
6492 ptMouseLast = pt;
6493 SetMouseCapture(false);
6494 NotifyIndicatorClick(false, newPos.Position(), false, false, false);
6495 if (inDragDrop == ddDragging) {
6496 SelectionPosition selStart = SelectionStart();
6497 SelectionPosition selEnd = SelectionEnd();
6498 if (selStart < selEnd) {
6499 if (drag.len) {
6500 if (ctrl) {
6501 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6502 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6504 } else if (newPos < selStart) {
6505 pdoc->DeleteChars(selStart.Position(), drag.len);
6506 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6507 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6509 } else if (newPos > selEnd) {
6510 pdoc->DeleteChars(selStart.Position(), drag.len);
6511 newPos.Add(-drag.len);
6512 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6513 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6515 } else {
6516 SetEmptySelection(newPos.Position());
6518 drag.Free();
6520 selectionType = selChar;
6522 } else {
6523 if (selectionType == selChar) {
6524 if (sel.Count() > 1) {
6525 sel.RangeMain() =
6526 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
6527 InvalidateSelection(sel.RangeMain(), true);
6528 } else {
6529 SetSelection(newPos, sel.RangeMain().anchor);
6532 sel.CommitTentative();
6534 SetRectangularRange();
6535 lastClickTime = curTime;
6536 lastClick = pt;
6537 lastXChosen = pt.x + xOffset;
6538 if (sel.selType == Selection::selStream) {
6539 SetLastXChosen();
6541 inDragDrop = ddNone;
6542 EnsureCaretVisible(false);
6546 // Called frequently to perform background UI including
6547 // caret blinking and automatic scrolling.
6548 void Editor::Tick() {
6549 if (HaveMouseCapture()) {
6550 // Auto scroll
6551 ButtonMove(ptMouseLast);
6553 if (caret.period > 0) {
6554 timer.ticksToWait -= timer.tickSize;
6555 if (timer.ticksToWait <= 0) {
6556 caret.on = !caret.on;
6557 timer.ticksToWait = caret.period;
6558 if (caret.active) {
6559 InvalidateCaret();
6563 if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) {
6564 scrollWidth = lineWidthMaxSeen;
6565 SetScrollBars();
6567 if ((dwellDelay < SC_TIME_FOREVER) &&
6568 (ticksToDwell > 0) &&
6569 (!HaveMouseCapture()) &&
6570 (ptMouseLast.y >= 0)) {
6571 ticksToDwell -= timer.tickSize;
6572 if (ticksToDwell <= 0) {
6573 dwelling = true;
6574 NotifyDwelling(ptMouseLast, dwelling);
6579 bool Editor::Idle() {
6581 bool idleDone;
6583 bool wrappingDone = wrapState == eWrapNone;
6585 if (!wrappingDone) {
6586 // Wrap lines during idle.
6587 WrapLines(false, -1);
6588 // No more wrapping
6589 if (wrapStart == wrapEnd)
6590 wrappingDone = true;
6593 // Add more idle things to do here, but make sure idleDone is
6594 // set correctly before the function returns. returning
6595 // false will stop calling this idle funtion until SetIdle() is
6596 // called again.
6598 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
6600 return !idleDone;
6603 void Editor::SetFocusState(bool focusState) {
6604 hasFocus = focusState;
6605 NotifyFocus(hasFocus);
6606 if (hasFocus) {
6607 ShowCaretAtCurrentPosition();
6608 } else {
6609 CancelModes();
6610 DropCaret();
6614 int Editor::PositionAfterArea(PRectangle rcArea) {
6615 // The start of the document line after the display line after the area
6616 // This often means that the line after a modification is restyled which helps
6617 // detect multiline comment additions and heals single line comments
6618 int lineAfter = topLine + (rcArea.bottom - 1) / vs.lineHeight + 1;
6619 if (lineAfter < cs.LinesDisplayed())
6620 return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
6621 else
6622 return pdoc->Length();
6625 // Style to a position within the view. If this causes a change at end of last line then
6626 // affects later lines so style all the viewed text.
6627 void Editor::StyleToPositionInView(Position pos) {
6628 int endWindow = PositionAfterArea(GetClientRectangle());
6629 if (pos > endWindow)
6630 pos = endWindow;
6631 int styleAtEnd = pdoc->StyleAt(pos-1);
6632 pdoc->EnsureStyledTo(pos);
6633 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleAt(pos-1))) {
6634 // Style at end of line changed so is multi-line change like starting a comment
6635 // so require rest of window to be styled.
6636 pdoc->EnsureStyledTo(endWindow);
6640 void Editor::IdleStyling() {
6641 // Style the line after the modification as this allows modifications that change just the
6642 // line of the modification to heal instead of propagating to the rest of the window.
6643 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(styleNeeded.upTo) + 2));
6645 if (needUpdateUI) {
6646 NotifyUpdateUI();
6647 needUpdateUI = 0;
6649 styleNeeded.Reset();
6652 void Editor::QueueStyling(int upTo) {
6653 styleNeeded.NeedUpTo(upTo);
6656 bool Editor::PaintContains(PRectangle rc) {
6657 if (rc.Empty()) {
6658 return true;
6659 } else {
6660 return rcPaint.Contains(rc);
6664 bool Editor::PaintContainsMargin() {
6665 PRectangle rcSelMargin = GetClientRectangle();
6666 rcSelMargin.right = vs.fixedColumnWidth;
6667 return PaintContains(rcSelMargin);
6670 void Editor::CheckForChangeOutsidePaint(Range r) {
6671 if (paintState == painting && !paintingAllText) {
6672 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
6673 if (!r.Valid())
6674 return;
6676 PRectangle rcRange = RectangleFromRange(r.start, r.end);
6677 PRectangle rcText = GetTextRectangle();
6678 if (rcRange.top < rcText.top) {
6679 rcRange.top = rcText.top;
6681 if (rcRange.bottom > rcText.bottom) {
6682 rcRange.bottom = rcText.bottom;
6685 if (!PaintContains(rcRange)) {
6686 AbandonPaint();
6691 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
6692 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
6693 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
6694 CheckForChangeOutsidePaint(Range(braces[0]));
6695 CheckForChangeOutsidePaint(Range(pos0));
6696 braces[0] = pos0;
6698 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
6699 CheckForChangeOutsidePaint(Range(braces[1]));
6700 CheckForChangeOutsidePaint(Range(pos1));
6701 braces[1] = pos1;
6703 bracesMatchStyle = matchStyle;
6704 if (paintState == notPainting) {
6705 Redraw();
6710 void Editor::SetAnnotationHeights(int start, int end) {
6711 if (vs.annotationVisible) {
6712 for (int line=start; line<end; line++) {
6713 cs.SetHeight(line, pdoc->AnnotationLines(line) + 1);
6718 void Editor::SetDocPointer(Document *document) {
6719 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
6720 pdoc->RemoveWatcher(this, 0);
6721 pdoc->Release();
6722 if (document == NULL) {
6723 pdoc = new Document();
6724 } else {
6725 pdoc = document;
6727 pdoc->AddRef();
6729 // Ensure all positions within document
6730 sel.Clear();
6731 targetStart = 0;
6732 targetEnd = 0;
6734 braces[0] = invalidPosition;
6735 braces[1] = invalidPosition;
6737 // Reset the contraction state to fully shown.
6738 cs.Clear();
6739 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6740 SetAnnotationHeights(0, pdoc->LinesTotal());
6741 llc.Deallocate();
6742 NeedWrapping();
6744 pdoc->AddWatcher(this, 0);
6745 SetScrollBars();
6746 Redraw();
6749 void Editor::SetAnnotationVisible(int visible) {
6750 if (vs.annotationVisible != visible) {
6751 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
6752 vs.annotationVisible = visible;
6753 if (changedFromOrToHidden) {
6754 int dir = vs.annotationVisible ? 1 : -1;
6755 for (int line=0; line<pdoc->LinesTotal(); line++) {
6756 int annotationLines = pdoc->AnnotationLines(line);
6757 if (annotationLines > 0) {
6758 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
6762 Redraw();
6767 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
6769 void Editor::Expand(int &line, bool doExpand) {
6770 int lineMaxSubord = pdoc->GetLastChild(line);
6771 line++;
6772 while (line <= lineMaxSubord) {
6773 if (doExpand)
6774 cs.SetVisible(line, line, true);
6775 int level = pdoc->GetLevel(line);
6776 if (level & SC_FOLDLEVELHEADERFLAG) {
6777 if (doExpand && cs.GetExpanded(line)) {
6778 Expand(line, true);
6779 } else {
6780 Expand(line, false);
6782 } else {
6783 line++;
6788 void Editor::ToggleContraction(int line) {
6789 if (line >= 0) {
6790 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
6791 line = pdoc->GetFoldParent(line);
6792 if (line < 0)
6793 return;
6796 if (cs.GetExpanded(line)) {
6797 int lineMaxSubord = pdoc->GetLastChild(line);
6798 if (lineMaxSubord > line) {
6799 cs.SetExpanded(line, 0);
6800 cs.SetVisible(line + 1, lineMaxSubord, false);
6802 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
6803 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
6804 // This does not re-expand the fold
6805 EnsureCaretVisible();
6808 SetScrollBars();
6809 Redraw();
6812 } else {
6813 if (!(cs.GetVisible(line))) {
6814 EnsureLineVisible(line, false);
6815 GoToLine(line);
6817 cs.SetExpanded(line, 1);
6818 Expand(line, true);
6819 SetScrollBars();
6820 Redraw();
6825 int Editor::ContractedFoldNext(int lineStart) {
6826 for (int line = lineStart; line<pdoc->LinesTotal();) {
6827 if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
6828 return line;
6829 line = cs.ContractedNext(line+1);
6830 if (line < 0)
6831 return -1;
6834 return -1;
6838 * Recurse up from this line to find any folds that prevent this line from being visible
6839 * and unfold them all.
6841 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
6843 // In case in need of wrapping to ensure DisplayFromDoc works.
6844 WrapLines(true, -1);
6846 if (!cs.GetVisible(lineDoc)) {
6847 int lookLine = lineDoc;
6848 int lookLineLevel = pdoc->GetLevel(lookLine);
6849 while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
6850 lookLineLevel = pdoc->GetLevel(--lookLine);
6852 int lineParent = pdoc->GetFoldParent(lookLine);
6853 if (lineParent >= 0) {
6854 if (lineDoc != lineParent)
6855 EnsureLineVisible(lineParent, enforcePolicy);
6856 if (!cs.GetExpanded(lineParent)) {
6857 cs.SetExpanded(lineParent, 1);
6858 Expand(lineParent, true);
6861 SetScrollBars();
6862 Redraw();
6864 if (enforcePolicy) {
6865 int lineDisplay = cs.DisplayFromDoc(lineDoc);
6866 if (visiblePolicy & VISIBLE_SLOP) {
6867 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
6868 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
6869 SetVerticalScrollPos();
6870 Redraw();
6871 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
6872 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
6873 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
6874 SetVerticalScrollPos();
6875 Redraw();
6877 } else {
6878 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
6879 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
6880 SetVerticalScrollPos();
6881 Redraw();
6887 int Editor::GetTag(char *tagValue, int tagNumber) {
6888 char name[3] = "\\?";
6889 const char *text = 0;
6890 int length = 0;
6891 if ((tagNumber >= 1) && (tagNumber <= 9)) {
6892 name[1] = static_cast<char>(tagNumber + '0');
6893 length = 2;
6894 text = pdoc->SubstituteByPosition(name, &length);
6896 if (tagValue) {
6897 if (text)
6898 memcpy(tagValue, text, length + 1);
6899 else
6900 *tagValue = '\0';
6902 return length;
6905 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
6906 UndoGroup ug(pdoc);
6907 if (length == -1)
6908 length = istrlen(text);
6909 if (replacePatterns) {
6910 text = pdoc->SubstituteByPosition(text, &length);
6911 if (!text) {
6912 return 0;
6915 if (targetStart != targetEnd)
6916 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
6917 targetEnd = targetStart;
6918 pdoc->InsertString(targetStart, text, length);
6919 targetEnd = targetStart + length;
6920 return length;
6923 bool Editor::IsUnicodeMode() const {
6924 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
6927 int Editor::CodePage() const {
6928 if (pdoc)
6929 return pdoc->dbcsCodePage;
6930 else
6931 return 0;
6934 int Editor::WrapCount(int line) {
6935 AutoSurface surface(this);
6936 AutoLineLayout ll(llc, RetrieveLineLayout(line));
6938 if (surface && ll) {
6939 LayoutLine(line, surface, vs, ll, wrapWidth);
6940 return ll->lines;
6941 } else {
6942 return 1;
6946 void Editor::AddStyledText(char *buffer, int appendLength) {
6947 // The buffer consists of alternating character bytes and style bytes
6948 int textLength = appendLength / 2;
6949 char *text = new char[textLength];
6950 int i;
6951 for (i = 0; i < textLength; i++) {
6952 text[i] = buffer[i*2];
6954 pdoc->InsertString(CurrentPosition(), text, textLength);
6955 for (i = 0; i < textLength; i++) {
6956 text[i] = buffer[i*2+1];
6958 pdoc->StartStyling(CurrentPosition(), static_cast<char>(0xff));
6959 pdoc->SetStyles(textLength, text);
6960 delete []text;
6961 SetEmptySelection(sel.MainCaret() + textLength);
6964 static bool ValidMargin(unsigned long wParam) {
6965 return wParam < ViewStyle::margins;
6968 static char *CharPtrFromSPtr(sptr_t lParam) {
6969 return reinterpret_cast<char *>(lParam);
6972 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
6973 vs.EnsureStyle(wParam);
6974 switch (iMessage) {
6975 case SCI_STYLESETFORE:
6976 vs.styles[wParam].fore.desired = ColourDesired(lParam);
6977 break;
6978 case SCI_STYLESETBACK:
6979 vs.styles[wParam].back.desired = ColourDesired(lParam);
6980 break;
6981 case SCI_STYLESETBOLD:
6982 vs.styles[wParam].bold = lParam != 0;
6983 break;
6984 case SCI_STYLESETITALIC:
6985 vs.styles[wParam].italic = lParam != 0;
6986 break;
6987 case SCI_STYLESETEOLFILLED:
6988 vs.styles[wParam].eolFilled = lParam != 0;
6989 break;
6990 case SCI_STYLESETSIZE:
6991 vs.styles[wParam].size = lParam;
6992 break;
6993 case SCI_STYLESETFONT:
6994 if (lParam != 0) {
6995 vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam));
6997 break;
6998 case SCI_STYLESETUNDERLINE:
6999 vs.styles[wParam].underline = lParam != 0;
7000 break;
7001 case SCI_STYLESETCASE:
7002 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
7003 break;
7004 case SCI_STYLESETCHARACTERSET:
7005 vs.styles[wParam].characterSet = lParam;
7006 break;
7007 case SCI_STYLESETVISIBLE:
7008 vs.styles[wParam].visible = lParam != 0;
7009 break;
7010 case SCI_STYLESETCHANGEABLE:
7011 vs.styles[wParam].changeable = lParam != 0;
7012 break;
7013 case SCI_STYLESETHOTSPOT:
7014 vs.styles[wParam].hotspot = lParam != 0;
7015 break;
7017 InvalidateStyleRedraw();
7020 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7021 vs.EnsureStyle(wParam);
7022 switch (iMessage) {
7023 case SCI_STYLEGETFORE:
7024 return vs.styles[wParam].fore.desired.AsLong();
7025 case SCI_STYLEGETBACK:
7026 return vs.styles[wParam].back.desired.AsLong();
7027 case SCI_STYLEGETBOLD:
7028 return vs.styles[wParam].bold ? 1 : 0;
7029 case SCI_STYLEGETITALIC:
7030 return vs.styles[wParam].italic ? 1 : 0;
7031 case SCI_STYLEGETEOLFILLED:
7032 return vs.styles[wParam].eolFilled ? 1 : 0;
7033 case SCI_STYLEGETSIZE:
7034 return vs.styles[wParam].size;
7035 case SCI_STYLEGETFONT:
7036 if (!vs.styles[wParam].fontName)
7037 return 0;
7038 if (lParam != 0)
7039 strcpy(CharPtrFromSPtr(lParam), vs.styles[wParam].fontName);
7040 return strlen(vs.styles[wParam].fontName);
7041 case SCI_STYLEGETUNDERLINE:
7042 return vs.styles[wParam].underline ? 1 : 0;
7043 case SCI_STYLEGETCASE:
7044 return static_cast<int>(vs.styles[wParam].caseForce);
7045 case SCI_STYLEGETCHARACTERSET:
7046 return vs.styles[wParam].characterSet;
7047 case SCI_STYLEGETVISIBLE:
7048 return vs.styles[wParam].visible ? 1 : 0;
7049 case SCI_STYLEGETCHANGEABLE:
7050 return vs.styles[wParam].changeable ? 1 : 0;
7051 case SCI_STYLEGETHOTSPOT:
7052 return vs.styles[wParam].hotspot ? 1 : 0;
7054 return 0;
7057 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
7058 const size_t n = strlen(val);
7059 if (lParam != 0) {
7060 char *ptr = reinterpret_cast<char *>(lParam);
7061 strcpy(ptr, val);
7063 return n; // Not including NUL
7066 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7067 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
7069 // Optional macro recording hook
7070 if (recordingMacro)
7071 NotifyMacroRecord(iMessage, wParam, lParam);
7073 switch (iMessage) {
7075 case SCI_GETTEXT: {
7076 if (lParam == 0)
7077 return pdoc->Length() + 1;
7078 if (wParam == 0)
7079 return 0;
7080 char *ptr = CharPtrFromSPtr(lParam);
7081 unsigned int iChar = 0;
7082 for (; iChar < wParam - 1; iChar++)
7083 ptr[iChar] = pdoc->CharAt(iChar);
7084 ptr[iChar] = '\0';
7085 return iChar;
7088 case SCI_SETTEXT: {
7089 if (lParam == 0)
7090 return 0;
7091 UndoGroup ug(pdoc);
7092 pdoc->DeleteChars(0, pdoc->Length());
7093 SetEmptySelection(0);
7094 pdoc->InsertCString(0, CharPtrFromSPtr(lParam));
7095 return 1;
7098 case SCI_GETTEXTLENGTH:
7099 return pdoc->Length();
7101 case SCI_CUT:
7102 Cut();
7103 SetLastXChosen();
7104 break;
7106 case SCI_COPY:
7107 Copy();
7108 break;
7110 case SCI_COPYALLOWLINE:
7111 CopyAllowLine();
7112 break;
7114 case SCI_VERTICALCENTRECARET:
7115 VerticalCentreCaret();
7116 break;
7118 case SCI_MOVESELECTEDLINESUP:
7119 MoveSelectedLinesUp();
7120 break;
7122 case SCI_MOVESELECTEDLINESDOWN:
7123 MoveSelectedLinesDown();
7124 break;
7126 case SCI_COPYRANGE:
7127 CopyRangeToClipboard(wParam, lParam);
7128 break;
7130 case SCI_COPYTEXT:
7131 CopyText(wParam, CharPtrFromSPtr(lParam));
7132 break;
7134 case SCI_PASTE:
7135 Paste();
7136 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
7137 SetLastXChosen();
7139 EnsureCaretVisible();
7140 break;
7142 case SCI_CLEAR:
7143 Clear();
7144 SetLastXChosen();
7145 EnsureCaretVisible();
7146 break;
7148 case SCI_UNDO:
7149 Undo();
7150 SetLastXChosen();
7151 break;
7153 case SCI_CANUNDO:
7154 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
7156 case SCI_EMPTYUNDOBUFFER:
7157 pdoc->DeleteUndoHistory();
7158 return 0;
7160 case SCI_GETFIRSTVISIBLELINE:
7161 return topLine;
7163 case SCI_SETFIRSTVISIBLELINE:
7164 ScrollTo(wParam);
7165 break;
7167 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
7168 int lineStart = pdoc->LineStart(wParam);
7169 int lineEnd = pdoc->LineStart(wParam + 1);
7170 if (lParam == 0) {
7171 return lineEnd - lineStart;
7173 char *ptr = CharPtrFromSPtr(lParam);
7174 int iPlace = 0;
7175 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
7176 ptr[iPlace++] = pdoc->CharAt(iChar);
7178 return iPlace;
7181 case SCI_GETLINECOUNT:
7182 if (pdoc->LinesTotal() == 0)
7183 return 1;
7184 else
7185 return pdoc->LinesTotal();
7187 case SCI_GETMODIFY:
7188 return !pdoc->IsSavePoint();
7190 case SCI_SETSEL: {
7191 int nStart = static_cast<int>(wParam);
7192 int nEnd = static_cast<int>(lParam);
7193 if (nEnd < 0)
7194 nEnd = pdoc->Length();
7195 if (nStart < 0)
7196 nStart = nEnd; // Remove selection
7197 InvalidateSelection(SelectionRange(nStart, nEnd));
7198 sel.Clear();
7199 sel.selType = Selection::selStream;
7200 SetSelection(nEnd, nStart);
7201 EnsureCaretVisible();
7203 break;
7205 case SCI_GETSELTEXT: {
7206 SelectionText selectedText;
7207 CopySelectionRange(&selectedText);
7208 if (lParam == 0) {
7209 return selectedText.len ? selectedText.len : 1;
7210 } else {
7211 char *ptr = CharPtrFromSPtr(lParam);
7212 int iChar = 0;
7213 if (selectedText.len) {
7214 for (; iChar < selectedText.len; iChar++)
7215 ptr[iChar] = selectedText.s[iChar];
7216 } else {
7217 ptr[0] = '\0';
7219 return iChar;
7223 case SCI_LINEFROMPOSITION:
7224 if (static_cast<int>(wParam) < 0)
7225 return 0;
7226 return pdoc->LineFromPosition(wParam);
7228 case SCI_POSITIONFROMLINE:
7229 if (static_cast<int>(wParam) < 0)
7230 wParam = pdoc->LineFromPosition(SelectionStart().Position());
7231 if (wParam == 0)
7232 return 0; // Even if there is no text, there is a first line that starts at 0
7233 if (static_cast<int>(wParam) > pdoc->LinesTotal())
7234 return -1;
7235 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
7236 // return -1;
7237 return pdoc->LineStart(wParam);
7239 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
7240 case SCI_LINELENGTH:
7241 if ((static_cast<int>(wParam) < 0) ||
7242 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
7243 return 0;
7244 return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
7246 case SCI_REPLACESEL: {
7247 if (lParam == 0)
7248 return 0;
7249 UndoGroup ug(pdoc);
7250 ClearSelection();
7251 char *replacement = CharPtrFromSPtr(lParam);
7252 pdoc->InsertCString(sel.MainCaret(), replacement);
7253 SetEmptySelection(sel.MainCaret() + istrlen(replacement));
7254 EnsureCaretVisible();
7256 break;
7258 case SCI_SETTARGETSTART:
7259 targetStart = wParam;
7260 break;
7262 case SCI_GETTARGETSTART:
7263 return targetStart;
7265 case SCI_SETTARGETEND:
7266 targetEnd = wParam;
7267 break;
7269 case SCI_GETTARGETEND:
7270 return targetEnd;
7272 case SCI_TARGETFROMSELECTION:
7273 if (sel.MainCaret() < sel.MainAnchor()) {
7274 targetStart = sel.MainCaret();
7275 targetEnd = sel.MainAnchor();
7276 } else {
7277 targetStart = sel.MainAnchor();
7278 targetEnd = sel.MainCaret();
7280 break;
7282 case SCI_REPLACETARGET:
7283 PLATFORM_ASSERT(lParam);
7284 return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam);
7286 case SCI_REPLACETARGETRE:
7287 PLATFORM_ASSERT(lParam);
7288 return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam);
7290 case SCI_SEARCHINTARGET:
7291 PLATFORM_ASSERT(lParam);
7292 return SearchInTarget(CharPtrFromSPtr(lParam), wParam);
7294 case SCI_SETSEARCHFLAGS:
7295 searchFlags = wParam;
7296 break;
7298 case SCI_GETSEARCHFLAGS:
7299 return searchFlags;
7301 case SCI_GETTAG:
7302 return GetTag(CharPtrFromSPtr(lParam), wParam);
7304 case SCI_POSITIONBEFORE:
7305 return pdoc->MovePositionOutsideChar(wParam - 1, -1, true);
7307 case SCI_POSITIONAFTER:
7308 return pdoc->MovePositionOutsideChar(wParam + 1, 1, true);
7310 case SCI_LINESCROLL:
7311 ScrollTo(topLine + lParam);
7312 HorizontalScrollTo(xOffset + wParam * vs.spaceWidth);
7313 return 1;
7315 case SCI_SETXOFFSET:
7316 xOffset = wParam;
7317 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7318 SetHorizontalScrollPos();
7319 Redraw();
7320 break;
7322 case SCI_GETXOFFSET:
7323 return xOffset;
7325 case SCI_CHOOSECARETX:
7326 SetLastXChosen();
7327 break;
7329 case SCI_SCROLLCARET:
7330 EnsureCaretVisible();
7331 break;
7333 case SCI_SETREADONLY:
7334 pdoc->SetReadOnly(wParam != 0);
7335 return 1;
7337 case SCI_GETREADONLY:
7338 return pdoc->IsReadOnly();
7340 case SCI_CANPASTE:
7341 return CanPaste();
7343 case SCI_POINTXFROMPOSITION:
7344 if (lParam < 0) {
7345 return 0;
7346 } else {
7347 Point pt = LocationFromPosition(lParam);
7348 return pt.x;
7351 case SCI_POINTYFROMPOSITION:
7352 if (lParam < 0) {
7353 return 0;
7354 } else {
7355 Point pt = LocationFromPosition(lParam);
7356 return pt.y;
7359 case SCI_FINDTEXT:
7360 return FindText(wParam, lParam);
7362 case SCI_GETTEXTRANGE: {
7363 if (lParam == 0)
7364 return 0;
7365 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7366 int cpMax = tr->chrg.cpMax;
7367 if (cpMax == -1)
7368 cpMax = pdoc->Length();
7369 PLATFORM_ASSERT(cpMax <= pdoc->Length());
7370 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
7371 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
7372 // Spec says copied text is terminated with a NUL
7373 tr->lpstrText[len] = '\0';
7374 return len; // Not including NUL
7377 case SCI_HIDESELECTION:
7378 hideSelection = wParam != 0;
7379 Redraw();
7380 break;
7382 case SCI_FORMATRANGE:
7383 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
7385 case SCI_GETMARGINLEFT:
7386 return vs.leftMarginWidth;
7388 case SCI_GETMARGINRIGHT:
7389 return vs.rightMarginWidth;
7391 case SCI_SETMARGINLEFT:
7392 vs.leftMarginWidth = lParam;
7393 InvalidateStyleRedraw();
7394 break;
7396 case SCI_SETMARGINRIGHT:
7397 vs.rightMarginWidth = lParam;
7398 InvalidateStyleRedraw();
7399 break;
7401 // Control specific mesages
7403 case SCI_ADDTEXT: {
7404 if (lParam == 0)
7405 return 0;
7406 pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam);
7407 SetEmptySelection(sel.MainCaret() + wParam);
7408 return 0;
7411 case SCI_ADDSTYLEDTEXT:
7412 if (lParam)
7413 AddStyledText(CharPtrFromSPtr(lParam), wParam);
7414 return 0;
7416 case SCI_INSERTTEXT: {
7417 if (lParam == 0)
7418 return 0;
7419 int insertPos = wParam;
7420 if (static_cast<int>(wParam) == -1)
7421 insertPos = CurrentPosition();
7422 int newCurrent = CurrentPosition();
7423 char *sz = CharPtrFromSPtr(lParam);
7424 pdoc->InsertCString(insertPos, sz);
7425 if (newCurrent > insertPos)
7426 newCurrent += istrlen(sz);
7427 SetEmptySelection(newCurrent);
7428 return 0;
7431 case SCI_APPENDTEXT:
7432 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam);
7433 return 0;
7435 case SCI_CLEARALL:
7436 ClearAll();
7437 return 0;
7439 case SCI_CLEARDOCUMENTSTYLE:
7440 ClearDocumentStyle();
7441 return 0;
7443 case SCI_SETUNDOCOLLECTION:
7444 pdoc->SetUndoCollection(wParam != 0);
7445 return 0;
7447 case SCI_GETUNDOCOLLECTION:
7448 return pdoc->IsCollectingUndo();
7450 case SCI_BEGINUNDOACTION:
7451 pdoc->BeginUndoAction();
7452 return 0;
7454 case SCI_ENDUNDOACTION:
7455 pdoc->EndUndoAction();
7456 return 0;
7458 case SCI_GETCARETPERIOD:
7459 return caret.period;
7461 case SCI_SETCARETPERIOD:
7462 caret.period = wParam;
7463 break;
7465 case SCI_SETWORDCHARS: {
7466 pdoc->SetDefaultCharClasses(false);
7467 if (lParam == 0)
7468 return 0;
7469 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
7471 break;
7473 case SCI_SETWHITESPACECHARS: {
7474 if (lParam == 0)
7475 return 0;
7476 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
7478 break;
7480 case SCI_SETCHARSDEFAULT:
7481 pdoc->SetDefaultCharClasses(true);
7482 break;
7484 case SCI_GETLENGTH:
7485 return pdoc->Length();
7487 case SCI_ALLOCATE:
7488 pdoc->Allocate(wParam);
7489 break;
7491 case SCI_GETCHARAT:
7492 return pdoc->CharAt(wParam);
7494 case SCI_SETCURRENTPOS:
7495 if (sel.IsRectangular()) {
7496 sel.Rectangular().caret.SetPosition(wParam);
7497 SetRectangularRange();
7498 Redraw();
7499 } else {
7500 SetSelection(wParam, sel.MainAnchor());
7502 break;
7504 case SCI_GETCURRENTPOS:
7505 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
7507 case SCI_SETANCHOR:
7508 if (sel.IsRectangular()) {
7509 sel.Rectangular().anchor.SetPosition(wParam);
7510 SetRectangularRange();
7511 Redraw();
7512 } else {
7513 SetSelection(sel.MainCaret(), wParam);
7515 break;
7517 case SCI_GETANCHOR:
7518 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
7520 case SCI_SETSELECTIONSTART:
7521 SetSelection(Platform::Maximum(sel.MainCaret(), wParam), wParam);
7522 break;
7524 case SCI_GETSELECTIONSTART:
7525 return sel.LimitsForRectangularElseMain().start.Position();
7527 case SCI_SETSELECTIONEND:
7528 SetSelection(wParam, Platform::Minimum(sel.MainAnchor(), wParam));
7529 break;
7531 case SCI_GETSELECTIONEND:
7532 return sel.LimitsForRectangularElseMain().end.Position();
7534 case SCI_SETEMPTYSELECTION:
7535 SetEmptySelection(wParam);
7536 break;
7538 case SCI_SETPRINTMAGNIFICATION:
7539 printMagnification = wParam;
7540 break;
7542 case SCI_GETPRINTMAGNIFICATION:
7543 return printMagnification;
7545 case SCI_SETPRINTCOLOURMODE:
7546 printColourMode = wParam;
7547 break;
7549 case SCI_GETPRINTCOLOURMODE:
7550 return printColourMode;
7552 case SCI_SETPRINTWRAPMODE:
7553 printWrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
7554 break;
7556 case SCI_GETPRINTWRAPMODE:
7557 return printWrapState;
7559 case SCI_GETSTYLEAT:
7560 if (static_cast<int>(wParam) >= pdoc->Length())
7561 return 0;
7562 else
7563 return pdoc->StyleAt(wParam);
7565 case SCI_REDO:
7566 Redo();
7567 break;
7569 case SCI_SELECTALL:
7570 SelectAll();
7571 break;
7573 case SCI_SETSAVEPOINT:
7574 pdoc->SetSavePoint();
7575 break;
7577 case SCI_GETSTYLEDTEXT: {
7578 if (lParam == 0)
7579 return 0;
7580 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7581 int iPlace = 0;
7582 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
7583 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
7584 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
7586 tr->lpstrText[iPlace] = '\0';
7587 tr->lpstrText[iPlace + 1] = '\0';
7588 return iPlace;
7591 case SCI_CANREDO:
7592 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
7594 case SCI_MARKERLINEFROMHANDLE:
7595 return pdoc->LineFromHandle(wParam);
7597 case SCI_MARKERDELETEHANDLE:
7598 pdoc->DeleteMarkFromHandle(wParam);
7599 break;
7601 case SCI_GETVIEWWS:
7602 return vs.viewWhitespace;
7604 case SCI_SETVIEWWS:
7605 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
7606 Redraw();
7607 break;
7609 case SCI_GETWHITESPACESIZE:
7610 return vs.whitespaceSize;
7612 case SCI_SETWHITESPACESIZE:
7613 vs.whitespaceSize = static_cast<int>(wParam);
7614 Redraw();
7615 break;
7617 case SCI_POSITIONFROMPOINT:
7618 return PositionFromLocation(Point(wParam, lParam), false, false);
7620 case SCI_POSITIONFROMPOINTCLOSE:
7621 return PositionFromLocation(Point(wParam, lParam), true, false);
7623 case SCI_CHARPOSITIONFROMPOINT:
7624 return PositionFromLocation(Point(wParam, lParam), false, true);
7626 case SCI_CHARPOSITIONFROMPOINTCLOSE:
7627 return PositionFromLocation(Point(wParam, lParam), true, true);
7629 case SCI_GOTOLINE:
7630 GoToLine(wParam);
7631 break;
7633 case SCI_GOTOPOS:
7634 SetEmptySelection(wParam);
7635 EnsureCaretVisible();
7636 break;
7638 case SCI_GETCURLINE: {
7639 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
7640 int lineStart = pdoc->LineStart(lineCurrentPos);
7641 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
7642 if (lParam == 0) {
7643 return 1 + lineEnd - lineStart;
7645 PLATFORM_ASSERT(wParam > 0);
7646 char *ptr = CharPtrFromSPtr(lParam);
7647 unsigned int iPlace = 0;
7648 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
7649 ptr[iPlace++] = pdoc->CharAt(iChar);
7651 ptr[iPlace] = '\0';
7652 return sel.MainCaret() - lineStart;
7655 case SCI_GETENDSTYLED:
7656 return pdoc->GetEndStyled();
7658 case SCI_GETEOLMODE:
7659 return pdoc->eolMode;
7661 case SCI_SETEOLMODE:
7662 pdoc->eolMode = wParam;
7663 break;
7665 case SCI_STARTSTYLING:
7666 pdoc->StartStyling(wParam, static_cast<char>(lParam));
7667 break;
7669 case SCI_SETSTYLING:
7670 pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
7671 break;
7673 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
7674 if (lParam == 0)
7675 return 0;
7676 pdoc->SetStyles(wParam, CharPtrFromSPtr(lParam));
7677 break;
7679 case SCI_SETBUFFEREDDRAW:
7680 bufferedDraw = wParam != 0;
7681 break;
7683 case SCI_GETBUFFEREDDRAW:
7684 return bufferedDraw;
7686 case SCI_GETTWOPHASEDRAW:
7687 return twoPhaseDraw;
7689 case SCI_SETTWOPHASEDRAW:
7690 twoPhaseDraw = wParam != 0;
7691 InvalidateStyleRedraw();
7692 break;
7694 case SCI_SETFONTQUALITY:
7695 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
7696 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
7697 InvalidateStyleRedraw();
7698 break;
7700 case SCI_GETFONTQUALITY:
7701 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
7703 case SCI_SETTABWIDTH:
7704 if (wParam > 0) {
7705 pdoc->tabInChars = wParam;
7706 if (pdoc->indentInChars == 0)
7707 pdoc->actualIndentInChars = pdoc->tabInChars;
7709 InvalidateStyleRedraw();
7710 break;
7712 case SCI_GETTABWIDTH:
7713 return pdoc->tabInChars;
7715 case SCI_SETINDENT:
7716 pdoc->indentInChars = wParam;
7717 if (pdoc->indentInChars != 0)
7718 pdoc->actualIndentInChars = pdoc->indentInChars;
7719 else
7720 pdoc->actualIndentInChars = pdoc->tabInChars;
7721 InvalidateStyleRedraw();
7722 break;
7724 case SCI_GETINDENT:
7725 return pdoc->indentInChars;
7727 case SCI_SETUSETABS:
7728 pdoc->useTabs = wParam != 0;
7729 InvalidateStyleRedraw();
7730 break;
7732 case SCI_GETUSETABS:
7733 return pdoc->useTabs;
7735 case SCI_SETLINEINDENTATION:
7736 pdoc->SetLineIndentation(wParam, lParam);
7737 break;
7739 case SCI_GETLINEINDENTATION:
7740 return pdoc->GetLineIndentation(wParam);
7742 case SCI_GETLINEINDENTPOSITION:
7743 return pdoc->GetLineIndentPosition(wParam);
7745 case SCI_SETTABINDENTS:
7746 pdoc->tabIndents = wParam != 0;
7747 break;
7749 case SCI_GETTABINDENTS:
7750 return pdoc->tabIndents;
7752 case SCI_SETBACKSPACEUNINDENTS:
7753 pdoc->backspaceUnindents = wParam != 0;
7754 break;
7756 case SCI_GETBACKSPACEUNINDENTS:
7757 return pdoc->backspaceUnindents;
7759 case SCI_SETMOUSEDWELLTIME:
7760 dwellDelay = wParam;
7761 ticksToDwell = dwellDelay;
7762 break;
7764 case SCI_GETMOUSEDWELLTIME:
7765 return dwellDelay;
7767 case SCI_WORDSTARTPOSITION:
7768 return pdoc->ExtendWordSelect(wParam, -1, lParam != 0);
7770 case SCI_WORDENDPOSITION:
7771 return pdoc->ExtendWordSelect(wParam, 1, lParam != 0);
7773 case SCI_SETWRAPMODE:
7774 switch (wParam) {
7775 case SC_WRAP_WORD:
7776 wrapState = eWrapWord;
7777 break;
7778 case SC_WRAP_CHAR:
7779 wrapState = eWrapChar;
7780 break;
7781 default:
7782 wrapState = eWrapNone;
7783 break;
7785 xOffset = 0;
7786 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7787 InvalidateStyleRedraw();
7788 ReconfigureScrollBars();
7789 break;
7791 case SCI_GETWRAPMODE:
7792 return wrapState;
7794 case SCI_SETWRAPVISUALFLAGS:
7795 if (wrapVisualFlags != static_cast<int>(wParam)) {
7796 wrapVisualFlags = wParam;
7797 InvalidateStyleRedraw();
7798 ReconfigureScrollBars();
7800 break;
7802 case SCI_GETWRAPVISUALFLAGS:
7803 return wrapVisualFlags;
7805 case SCI_SETWRAPVISUALFLAGSLOCATION:
7806 wrapVisualFlagsLocation = wParam;
7807 InvalidateStyleRedraw();
7808 break;
7810 case SCI_GETWRAPVISUALFLAGSLOCATION:
7811 return wrapVisualFlagsLocation;
7813 case SCI_SETWRAPSTARTINDENT:
7814 if (wrapVisualStartIndent != static_cast<int>(wParam)) {
7815 wrapVisualStartIndent = wParam;
7816 InvalidateStyleRedraw();
7817 ReconfigureScrollBars();
7819 break;
7821 case SCI_GETWRAPSTARTINDENT:
7822 return wrapVisualStartIndent;
7824 case SCI_SETWRAPINDENTMODE:
7825 if (wrapIndentMode != static_cast<int>(wParam)) {
7826 wrapIndentMode = wParam;
7827 InvalidateStyleRedraw();
7828 ReconfigureScrollBars();
7830 break;
7832 case SCI_GETWRAPINDENTMODE:
7833 return wrapIndentMode;
7835 case SCI_SETLAYOUTCACHE:
7836 llc.SetLevel(wParam);
7837 break;
7839 case SCI_GETLAYOUTCACHE:
7840 return llc.GetLevel();
7842 case SCI_SETPOSITIONCACHE:
7843 posCache.SetSize(wParam);
7844 break;
7846 case SCI_GETPOSITIONCACHE:
7847 return posCache.GetSize();
7849 case SCI_SETSCROLLWIDTH:
7850 PLATFORM_ASSERT(wParam > 0);
7851 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
7852 lineWidthMaxSeen = 0;
7853 scrollWidth = wParam;
7854 SetScrollBars();
7856 break;
7858 case SCI_GETSCROLLWIDTH:
7859 return scrollWidth;
7861 case SCI_SETSCROLLWIDTHTRACKING:
7862 trackLineWidth = wParam != 0;
7863 break;
7865 case SCI_GETSCROLLWIDTHTRACKING:
7866 return trackLineWidth;
7868 case SCI_LINESJOIN:
7869 LinesJoin();
7870 break;
7872 case SCI_LINESSPLIT:
7873 LinesSplit(wParam);
7874 break;
7876 case SCI_TEXTWIDTH:
7877 PLATFORM_ASSERT(wParam < vs.stylesSize);
7878 PLATFORM_ASSERT(lParam);
7879 return TextWidth(wParam, CharPtrFromSPtr(lParam));
7881 case SCI_TEXTHEIGHT:
7882 return vs.lineHeight;
7884 case SCI_SETENDATLASTLINE:
7885 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
7886 if (endAtLastLine != (wParam != 0)) {
7887 endAtLastLine = wParam != 0;
7888 SetScrollBars();
7890 break;
7892 case SCI_GETENDATLASTLINE:
7893 return endAtLastLine;
7895 case SCI_SETCARETSTICKY:
7896 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
7897 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
7898 caretSticky = wParam;
7900 break;
7902 case SCI_GETCARETSTICKY:
7903 return caretSticky;
7905 case SCI_TOGGLECARETSTICKY:
7906 caretSticky = !caretSticky;
7907 break;
7909 case SCI_GETCOLUMN:
7910 return pdoc->GetColumn(wParam);
7912 case SCI_FINDCOLUMN:
7913 return pdoc->FindColumn(wParam, lParam);
7915 case SCI_SETHSCROLLBAR :
7916 if (horizontalScrollBarVisible != (wParam != 0)) {
7917 horizontalScrollBarVisible = wParam != 0;
7918 SetScrollBars();
7919 ReconfigureScrollBars();
7921 break;
7923 case SCI_GETHSCROLLBAR:
7924 return horizontalScrollBarVisible;
7926 case SCI_SETVSCROLLBAR:
7927 if (verticalScrollBarVisible != (wParam != 0)) {
7928 verticalScrollBarVisible = wParam != 0;
7929 SetScrollBars();
7930 ReconfigureScrollBars();
7932 break;
7934 case SCI_GETVSCROLLBAR:
7935 return verticalScrollBarVisible;
7937 case SCI_SETINDENTATIONGUIDES:
7938 vs.viewIndentationGuides = IndentView(wParam);
7939 Redraw();
7940 break;
7942 case SCI_GETINDENTATIONGUIDES:
7943 return vs.viewIndentationGuides;
7945 case SCI_SETHIGHLIGHTGUIDE:
7946 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
7947 highlightGuideColumn = wParam;
7948 Redraw();
7950 break;
7952 case SCI_GETHIGHLIGHTGUIDE:
7953 return highlightGuideColumn;
7955 case SCI_GETLINEENDPOSITION:
7956 return pdoc->LineEnd(wParam);
7958 case SCI_SETCODEPAGE:
7959 if (ValidCodePage(wParam)) {
7960 pdoc->dbcsCodePage = wParam;
7961 InvalidateStyleRedraw();
7963 break;
7965 case SCI_GETCODEPAGE:
7966 return pdoc->dbcsCodePage;
7968 case SCI_SETUSEPALETTE:
7969 palette.allowRealization = wParam != 0;
7970 InvalidateStyleRedraw();
7971 break;
7973 case SCI_GETUSEPALETTE:
7974 return palette.allowRealization;
7976 // Marker definition and setting
7977 case SCI_MARKERDEFINE:
7978 if (wParam <= MARKER_MAX)
7979 vs.markers[wParam].markType = lParam;
7980 InvalidateStyleData();
7981 RedrawSelMargin();
7982 break;
7984 case SCI_MARKERSYMBOLDEFINED:
7985 if (wParam <= MARKER_MAX)
7986 return vs.markers[wParam].markType;
7987 else
7988 return 0;
7990 case SCI_MARKERSETFORE:
7991 if (wParam <= MARKER_MAX)
7992 vs.markers[wParam].fore.desired = ColourDesired(lParam);
7993 InvalidateStyleData();
7994 RedrawSelMargin();
7995 break;
7996 case SCI_MARKERSETBACKSELECTED:
7997 if (wParam <= MARKER_MAX)
7998 vs.markers[wParam].backSelected.desired = ColourDesired(lParam);
7999 InvalidateStyleRedraw();
8000 break;
8001 case SCI_MARKERENABLEHIGHLIGHT:
8002 highlightDelimiter.isEnabled = wParam == 1;
8003 InvalidateStyleRedraw();
8004 break;
8005 case SCI_MARKERSETBACK:
8006 if (wParam <= MARKER_MAX)
8007 vs.markers[wParam].back.desired = ColourDesired(lParam);
8008 InvalidateStyleData();
8009 RedrawSelMargin();
8010 break;
8011 case SCI_MARKERSETALPHA:
8012 if (wParam <= MARKER_MAX)
8013 vs.markers[wParam].alpha = lParam;
8014 InvalidateStyleRedraw();
8015 break;
8016 case SCI_MARKERADD: {
8017 int markerID = pdoc->AddMark(wParam, lParam);
8018 return markerID;
8020 case SCI_MARKERADDSET:
8021 if (lParam != 0)
8022 pdoc->AddMarkSet(wParam, lParam);
8023 break;
8025 case SCI_MARKERDELETE:
8026 pdoc->DeleteMark(wParam, lParam);
8027 break;
8029 case SCI_MARKERDELETEALL:
8030 pdoc->DeleteAllMarks(static_cast<int>(wParam));
8031 break;
8033 case SCI_MARKERGET:
8034 return pdoc->GetMark(wParam);
8036 case SCI_MARKERNEXT: {
8037 int lt = pdoc->LinesTotal();
8038 for (int iLine = wParam; iLine < lt; iLine++) {
8039 if ((pdoc->GetMark(iLine) & lParam) != 0)
8040 return iLine;
8043 return -1;
8045 case SCI_MARKERPREVIOUS: {
8046 for (int iLine = wParam; iLine >= 0; iLine--) {
8047 if ((pdoc->GetMark(iLine) & lParam) != 0)
8048 return iLine;
8051 return -1;
8053 case SCI_MARKERDEFINEPIXMAP:
8054 if (wParam <= MARKER_MAX) {
8055 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
8057 InvalidateStyleData();
8058 RedrawSelMargin();
8059 break;
8061 case SCI_RGBAIMAGESETWIDTH:
8062 sizeRGBAImage.x = wParam;
8063 break;
8065 case SCI_RGBAIMAGESETHEIGHT:
8066 sizeRGBAImage.y = wParam;
8067 break;
8069 case SCI_MARKERDEFINERGBAIMAGE:
8070 if (wParam <= MARKER_MAX) {
8071 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, reinterpret_cast<unsigned char *>(lParam));
8073 InvalidateStyleData();
8074 RedrawSelMargin();
8075 break;
8077 case SCI_SETMARGINTYPEN:
8078 if (ValidMargin(wParam)) {
8079 vs.ms[wParam].style = lParam;
8080 InvalidateStyleRedraw();
8082 break;
8084 case SCI_GETMARGINTYPEN:
8085 if (ValidMargin(wParam))
8086 return vs.ms[wParam].style;
8087 else
8088 return 0;
8090 case SCI_SETMARGINWIDTHN:
8091 if (ValidMargin(wParam)) {
8092 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
8093 if (vs.ms[wParam].width != lParam) {
8094 vs.ms[wParam].width = lParam;
8095 InvalidateStyleRedraw();
8098 break;
8100 case SCI_GETMARGINWIDTHN:
8101 if (ValidMargin(wParam))
8102 return vs.ms[wParam].width;
8103 else
8104 return 0;
8106 case SCI_SETMARGINMASKN:
8107 if (ValidMargin(wParam)) {
8108 vs.ms[wParam].mask = lParam;
8109 InvalidateStyleRedraw();
8111 break;
8113 case SCI_GETMARGINMASKN:
8114 if (ValidMargin(wParam))
8115 return vs.ms[wParam].mask;
8116 else
8117 return 0;
8119 case SCI_SETMARGINSENSITIVEN:
8120 if (ValidMargin(wParam)) {
8121 vs.ms[wParam].sensitive = lParam != 0;
8122 InvalidateStyleRedraw();
8124 break;
8126 case SCI_GETMARGINSENSITIVEN:
8127 if (ValidMargin(wParam))
8128 return vs.ms[wParam].sensitive ? 1 : 0;
8129 else
8130 return 0;
8132 case SCI_SETMARGINCURSORN:
8133 if (ValidMargin(wParam))
8134 vs.ms[wParam].cursor = lParam;
8135 break;
8137 case SCI_GETMARGINCURSORN:
8138 if (ValidMargin(wParam))
8139 return vs.ms[wParam].cursor;
8140 else
8141 return 0;
8143 case SCI_STYLECLEARALL:
8144 vs.ClearStyles();
8145 InvalidateStyleRedraw();
8146 break;
8148 case SCI_STYLESETFORE:
8149 case SCI_STYLESETBACK:
8150 case SCI_STYLESETBOLD:
8151 case SCI_STYLESETITALIC:
8152 case SCI_STYLESETEOLFILLED:
8153 case SCI_STYLESETSIZE:
8154 case SCI_STYLESETFONT:
8155 case SCI_STYLESETUNDERLINE:
8156 case SCI_STYLESETCASE:
8157 case SCI_STYLESETCHARACTERSET:
8158 case SCI_STYLESETVISIBLE:
8159 case SCI_STYLESETCHANGEABLE:
8160 case SCI_STYLESETHOTSPOT:
8161 StyleSetMessage(iMessage, wParam, lParam);
8162 break;
8164 case SCI_STYLEGETFORE:
8165 case SCI_STYLEGETBACK:
8166 case SCI_STYLEGETBOLD:
8167 case SCI_STYLEGETITALIC:
8168 case SCI_STYLEGETEOLFILLED:
8169 case SCI_STYLEGETSIZE:
8170 case SCI_STYLEGETFONT:
8171 case SCI_STYLEGETUNDERLINE:
8172 case SCI_STYLEGETCASE:
8173 case SCI_STYLEGETCHARACTERSET:
8174 case SCI_STYLEGETVISIBLE:
8175 case SCI_STYLEGETCHANGEABLE:
8176 case SCI_STYLEGETHOTSPOT:
8177 return StyleGetMessage(iMessage, wParam, lParam);
8179 case SCI_STYLERESETDEFAULT:
8180 vs.ResetDefaultStyle();
8181 InvalidateStyleRedraw();
8182 break;
8183 case SCI_SETSTYLEBITS:
8184 vs.EnsureStyle((1 << wParam) - 1);
8185 pdoc->SetStylingBits(wParam);
8186 break;
8188 case SCI_GETSTYLEBITS:
8189 return pdoc->stylingBits;
8191 case SCI_SETLINESTATE:
8192 return pdoc->SetLineState(wParam, lParam);
8194 case SCI_GETLINESTATE:
8195 return pdoc->GetLineState(wParam);
8197 case SCI_GETMAXLINESTATE:
8198 return pdoc->GetMaxLineState();
8200 case SCI_GETCARETLINEVISIBLE:
8201 return vs.showCaretLineBackground;
8202 case SCI_SETCARETLINEVISIBLE:
8203 vs.showCaretLineBackground = wParam != 0;
8204 InvalidateStyleRedraw();
8205 break;
8206 case SCI_GETCARETLINEBACK:
8207 return vs.caretLineBackground.desired.AsLong();
8208 case SCI_SETCARETLINEBACK:
8209 vs.caretLineBackground.desired = wParam;
8210 InvalidateStyleRedraw();
8211 break;
8212 case SCI_GETCARETLINEBACKALPHA:
8213 return vs.caretLineAlpha;
8214 case SCI_SETCARETLINEBACKALPHA:
8215 vs.caretLineAlpha = wParam;
8216 InvalidateStyleRedraw();
8217 break;
8219 // Folding messages
8221 case SCI_VISIBLEFROMDOCLINE:
8222 return cs.DisplayFromDoc(wParam);
8224 case SCI_DOCLINEFROMVISIBLE:
8225 return cs.DocFromDisplay(wParam);
8227 case SCI_WRAPCOUNT:
8228 return WrapCount(wParam);
8230 case SCI_SETFOLDLEVEL: {
8231 int prev = pdoc->SetLevel(wParam, lParam);
8232 if (prev != lParam)
8233 RedrawSelMargin();
8234 return prev;
8237 case SCI_GETFOLDLEVEL:
8238 return pdoc->GetLevel(wParam);
8240 case SCI_GETLASTCHILD:
8241 return pdoc->GetLastChild(wParam, lParam);
8243 case SCI_GETFOLDPARENT:
8244 return pdoc->GetFoldParent(wParam);
8246 case SCI_SHOWLINES:
8247 cs.SetVisible(wParam, lParam, true);
8248 SetScrollBars();
8249 Redraw();
8250 break;
8252 case SCI_HIDELINES:
8253 if (wParam > 0)
8254 cs.SetVisible(wParam, lParam, false);
8255 SetScrollBars();
8256 Redraw();
8257 break;
8259 case SCI_GETLINEVISIBLE:
8260 return cs.GetVisible(wParam);
8262 case SCI_SETFOLDEXPANDED:
8263 if (cs.SetExpanded(wParam, lParam != 0)) {
8264 RedrawSelMargin();
8266 break;
8268 case SCI_GETFOLDEXPANDED:
8269 return cs.GetExpanded(wParam);
8271 case SCI_SETFOLDFLAGS:
8272 foldFlags = wParam;
8273 Redraw();
8274 break;
8276 case SCI_TOGGLEFOLD:
8277 ToggleContraction(wParam);
8278 break;
8280 case SCI_CONTRACTEDFOLDNEXT:
8281 return ContractedFoldNext(wParam);
8283 case SCI_ENSUREVISIBLE:
8284 EnsureLineVisible(wParam, false);
8285 break;
8287 case SCI_ENSUREVISIBLEENFORCEPOLICY:
8288 EnsureLineVisible(wParam, true);
8289 break;
8291 case SCI_SEARCHANCHOR:
8292 SearchAnchor();
8293 break;
8295 case SCI_SEARCHNEXT:
8296 case SCI_SEARCHPREV:
8297 return SearchText(iMessage, wParam, lParam);
8299 case SCI_SETXCARETPOLICY:
8300 caretXPolicy = wParam;
8301 caretXSlop = lParam;
8302 break;
8304 case SCI_SETYCARETPOLICY:
8305 caretYPolicy = wParam;
8306 caretYSlop = lParam;
8307 break;
8309 case SCI_SETVISIBLEPOLICY:
8310 visiblePolicy = wParam;
8311 visibleSlop = lParam;
8312 break;
8314 case SCI_LINESONSCREEN:
8315 return LinesOnScreen();
8317 case SCI_SETSELFORE:
8318 vs.selforeset = wParam != 0;
8319 vs.selforeground.desired = ColourDesired(lParam);
8320 vs.selAdditionalForeground.desired = ColourDesired(lParam);
8321 InvalidateStyleRedraw();
8322 break;
8324 case SCI_SETSELBACK:
8325 vs.selbackset = wParam != 0;
8326 vs.selbackground.desired = ColourDesired(lParam);
8327 vs.selAdditionalBackground.desired = ColourDesired(lParam);
8328 InvalidateStyleRedraw();
8329 break;
8331 case SCI_SETSELALPHA:
8332 vs.selAlpha = wParam;
8333 vs.selAdditionalAlpha = wParam;
8334 InvalidateStyleRedraw();
8335 break;
8337 case SCI_GETSELALPHA:
8338 return vs.selAlpha;
8340 case SCI_GETSELEOLFILLED:
8341 return vs.selEOLFilled;
8343 case SCI_SETSELEOLFILLED:
8344 vs.selEOLFilled = wParam != 0;
8345 InvalidateStyleRedraw();
8346 break;
8348 case SCI_SETWHITESPACEFORE:
8349 vs.whitespaceForegroundSet = wParam != 0;
8350 vs.whitespaceForeground.desired = ColourDesired(lParam);
8351 InvalidateStyleRedraw();
8352 break;
8354 case SCI_SETWHITESPACEBACK:
8355 vs.whitespaceBackgroundSet = wParam != 0;
8356 vs.whitespaceBackground.desired = ColourDesired(lParam);
8357 InvalidateStyleRedraw();
8358 break;
8360 case SCI_SETCARETFORE:
8361 vs.caretcolour.desired = ColourDesired(wParam);
8362 InvalidateStyleRedraw();
8363 break;
8365 case SCI_GETCARETFORE:
8366 return vs.caretcolour.desired.AsLong();
8368 case SCI_SETCARETSTYLE:
8369 if (wParam <= CARETSTYLE_BLOCK)
8370 vs.caretStyle = wParam;
8371 else
8372 /* Default to the line caret */
8373 vs.caretStyle = CARETSTYLE_LINE;
8374 InvalidateStyleRedraw();
8375 break;
8377 case SCI_GETCARETSTYLE:
8378 return vs.caretStyle;
8380 case SCI_SETCARETWIDTH:
8381 if (static_cast<int>(wParam) <= 0)
8382 vs.caretWidth = 0;
8383 else if (wParam >= 3)
8384 vs.caretWidth = 3;
8385 else
8386 vs.caretWidth = wParam;
8387 InvalidateStyleRedraw();
8388 break;
8390 case SCI_GETCARETWIDTH:
8391 return vs.caretWidth;
8393 case SCI_ASSIGNCMDKEY:
8394 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8395 Platform::HighShortFromLong(wParam), lParam);
8396 break;
8398 case SCI_CLEARCMDKEY:
8399 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8400 Platform::HighShortFromLong(wParam), SCI_NULL);
8401 break;
8403 case SCI_CLEARALLCMDKEYS:
8404 kmap.Clear();
8405 break;
8407 case SCI_INDICSETSTYLE:
8408 if (wParam <= INDIC_MAX) {
8409 vs.indicators[wParam].style = lParam;
8410 InvalidateStyleRedraw();
8412 break;
8414 case SCI_INDICGETSTYLE:
8415 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
8417 case SCI_INDICSETFORE:
8418 if (wParam <= INDIC_MAX) {
8419 vs.indicators[wParam].fore.desired = ColourDesired(lParam);
8420 InvalidateStyleRedraw();
8422 break;
8424 case SCI_INDICGETFORE:
8425 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.desired.AsLong() : 0;
8427 case SCI_INDICSETUNDER:
8428 if (wParam <= INDIC_MAX) {
8429 vs.indicators[wParam].under = lParam != 0;
8430 InvalidateStyleRedraw();
8432 break;
8434 case SCI_INDICGETUNDER:
8435 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
8437 case SCI_INDICSETALPHA:
8438 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8439 vs.indicators[wParam].fillAlpha = lParam;
8440 InvalidateStyleRedraw();
8442 break;
8444 case SCI_INDICGETALPHA:
8445 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
8447 case SCI_INDICSETOUTLINEALPHA:
8448 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8449 vs.indicators[wParam].outlineAlpha = lParam;
8450 InvalidateStyleRedraw();
8452 break;
8454 case SCI_INDICGETOUTLINEALPHA:
8455 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
8457 case SCI_SETINDICATORCURRENT:
8458 pdoc->decorations.SetCurrentIndicator(wParam);
8459 break;
8460 case SCI_GETINDICATORCURRENT:
8461 return pdoc->decorations.GetCurrentIndicator();
8462 case SCI_SETINDICATORVALUE:
8463 pdoc->decorations.SetCurrentValue(wParam);
8464 break;
8465 case SCI_GETINDICATORVALUE:
8466 return pdoc->decorations.GetCurrentValue();
8468 case SCI_INDICATORFILLRANGE:
8469 pdoc->DecorationFillRange(wParam, pdoc->decorations.GetCurrentValue(), lParam);
8470 break;
8472 case SCI_INDICATORCLEARRANGE:
8473 pdoc->DecorationFillRange(wParam, 0, lParam);
8474 break;
8476 case SCI_INDICATORALLONFOR:
8477 return pdoc->decorations.AllOnFor(wParam);
8479 case SCI_INDICATORVALUEAT:
8480 return pdoc->decorations.ValueAt(wParam, lParam);
8482 case SCI_INDICATORSTART:
8483 return pdoc->decorations.Start(wParam, lParam);
8485 case SCI_INDICATOREND:
8486 return pdoc->decorations.End(wParam, lParam);
8488 case SCI_LINEDOWN:
8489 case SCI_LINEDOWNEXTEND:
8490 case SCI_PARADOWN:
8491 case SCI_PARADOWNEXTEND:
8492 case SCI_LINEUP:
8493 case SCI_LINEUPEXTEND:
8494 case SCI_PARAUP:
8495 case SCI_PARAUPEXTEND:
8496 case SCI_CHARLEFT:
8497 case SCI_CHARLEFTEXTEND:
8498 case SCI_CHARRIGHT:
8499 case SCI_CHARRIGHTEXTEND:
8500 case SCI_WORDLEFT:
8501 case SCI_WORDLEFTEXTEND:
8502 case SCI_WORDRIGHT:
8503 case SCI_WORDRIGHTEXTEND:
8504 case SCI_WORDLEFTEND:
8505 case SCI_WORDLEFTENDEXTEND:
8506 case SCI_WORDRIGHTEND:
8507 case SCI_WORDRIGHTENDEXTEND:
8508 case SCI_HOME:
8509 case SCI_HOMEEXTEND:
8510 case SCI_LINEEND:
8511 case SCI_LINEENDEXTEND:
8512 case SCI_HOMEWRAP:
8513 case SCI_HOMEWRAPEXTEND:
8514 case SCI_LINEENDWRAP:
8515 case SCI_LINEENDWRAPEXTEND:
8516 case SCI_DOCUMENTSTART:
8517 case SCI_DOCUMENTSTARTEXTEND:
8518 case SCI_DOCUMENTEND:
8519 case SCI_DOCUMENTENDEXTEND:
8520 case SCI_SCROLLTOSTART:
8521 case SCI_SCROLLTOEND:
8523 case SCI_STUTTEREDPAGEUP:
8524 case SCI_STUTTEREDPAGEUPEXTEND:
8525 case SCI_STUTTEREDPAGEDOWN:
8526 case SCI_STUTTEREDPAGEDOWNEXTEND:
8528 case SCI_PAGEUP:
8529 case SCI_PAGEUPEXTEND:
8530 case SCI_PAGEDOWN:
8531 case SCI_PAGEDOWNEXTEND:
8532 case SCI_EDITTOGGLEOVERTYPE:
8533 case SCI_CANCEL:
8534 case SCI_DELETEBACK:
8535 case SCI_TAB:
8536 case SCI_BACKTAB:
8537 case SCI_NEWLINE:
8538 case SCI_FORMFEED:
8539 case SCI_VCHOME:
8540 case SCI_VCHOMEEXTEND:
8541 case SCI_VCHOMEWRAP:
8542 case SCI_VCHOMEWRAPEXTEND:
8543 case SCI_ZOOMIN:
8544 case SCI_ZOOMOUT:
8545 case SCI_DELWORDLEFT:
8546 case SCI_DELWORDRIGHT:
8547 case SCI_DELWORDRIGHTEND:
8548 case SCI_DELLINELEFT:
8549 case SCI_DELLINERIGHT:
8550 case SCI_LINECOPY:
8551 case SCI_LINECUT:
8552 case SCI_LINEDELETE:
8553 case SCI_LINETRANSPOSE:
8554 case SCI_LINEDUPLICATE:
8555 case SCI_LOWERCASE:
8556 case SCI_UPPERCASE:
8557 case SCI_LINESCROLLDOWN:
8558 case SCI_LINESCROLLUP:
8559 case SCI_WORDPARTLEFT:
8560 case SCI_WORDPARTLEFTEXTEND:
8561 case SCI_WORDPARTRIGHT:
8562 case SCI_WORDPARTRIGHTEXTEND:
8563 case SCI_DELETEBACKNOTLINE:
8564 case SCI_HOMEDISPLAY:
8565 case SCI_HOMEDISPLAYEXTEND:
8566 case SCI_LINEENDDISPLAY:
8567 case SCI_LINEENDDISPLAYEXTEND:
8568 case SCI_LINEDOWNRECTEXTEND:
8569 case SCI_LINEUPRECTEXTEND:
8570 case SCI_CHARLEFTRECTEXTEND:
8571 case SCI_CHARRIGHTRECTEXTEND:
8572 case SCI_HOMERECTEXTEND:
8573 case SCI_VCHOMERECTEXTEND:
8574 case SCI_LINEENDRECTEXTEND:
8575 case SCI_PAGEUPRECTEXTEND:
8576 case SCI_PAGEDOWNRECTEXTEND:
8577 case SCI_SELECTIONDUPLICATE:
8578 return KeyCommand(iMessage);
8580 case SCI_BRACEHIGHLIGHT:
8581 SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
8582 break;
8584 case SCI_BRACEHIGHLIGHTINDICATOR:
8585 if (lParam >= 0 && lParam <= INDIC_MAX) {
8586 vs.braceHighlightIndicatorSet = wParam != 0;
8587 vs.braceHighlightIndicator = lParam;
8589 break;
8591 case SCI_BRACEBADLIGHT:
8592 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
8593 break;
8595 case SCI_BRACEBADLIGHTINDICATOR:
8596 if (lParam >= 0 && lParam <= INDIC_MAX) {
8597 vs.braceBadLightIndicatorSet = wParam != 0;
8598 vs.braceBadLightIndicator = lParam;
8600 break;
8602 case SCI_BRACEMATCH:
8603 // wParam is position of char to find brace for,
8604 // lParam is maximum amount of text to restyle to find it
8605 return pdoc->BraceMatch(wParam, lParam);
8607 case SCI_GETVIEWEOL:
8608 return vs.viewEOL;
8610 case SCI_SETVIEWEOL:
8611 vs.viewEOL = wParam != 0;
8612 InvalidateStyleRedraw();
8613 break;
8615 case SCI_SETZOOM:
8616 vs.zoomLevel = wParam;
8617 InvalidateStyleRedraw();
8618 NotifyZoom();
8619 break;
8621 case SCI_GETZOOM:
8622 return vs.zoomLevel;
8624 case SCI_GETEDGECOLUMN:
8625 return theEdge;
8627 case SCI_SETEDGECOLUMN:
8628 theEdge = wParam;
8629 InvalidateStyleRedraw();
8630 break;
8632 case SCI_GETEDGEMODE:
8633 return vs.edgeState;
8635 case SCI_SETEDGEMODE:
8636 vs.edgeState = wParam;
8637 InvalidateStyleRedraw();
8638 break;
8640 case SCI_GETEDGECOLOUR:
8641 return vs.edgecolour.desired.AsLong();
8643 case SCI_SETEDGECOLOUR:
8644 vs.edgecolour.desired = ColourDesired(wParam);
8645 InvalidateStyleRedraw();
8646 break;
8648 case SCI_GETDOCPOINTER:
8649 return reinterpret_cast<sptr_t>(pdoc);
8651 case SCI_SETDOCPOINTER:
8652 CancelModes();
8653 SetDocPointer(reinterpret_cast<Document *>(lParam));
8654 return 0;
8656 case SCI_CREATEDOCUMENT: {
8657 Document *doc = new Document();
8658 if (doc) {
8659 doc->AddRef();
8661 return reinterpret_cast<sptr_t>(doc);
8664 case SCI_ADDREFDOCUMENT:
8665 (reinterpret_cast<Document *>(lParam))->AddRef();
8666 break;
8668 case SCI_RELEASEDOCUMENT:
8669 (reinterpret_cast<Document *>(lParam))->Release();
8670 break;
8672 case SCI_SETMODEVENTMASK:
8673 modEventMask = wParam;
8674 return 0;
8676 case SCI_GETMODEVENTMASK:
8677 return modEventMask;
8679 case SCI_CONVERTEOLS:
8680 pdoc->ConvertLineEnds(wParam);
8681 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
8682 return 0;
8684 case SCI_SETLENGTHFORENCODE:
8685 lengthForEncode = wParam;
8686 return 0;
8688 case SCI_SELECTIONISRECTANGLE:
8689 return sel.selType == Selection::selRectangle ? 1 : 0;
8691 case SCI_SETSELECTIONMODE: {
8692 switch (wParam) {
8693 case SC_SEL_STREAM:
8694 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
8695 sel.selType = Selection::selStream;
8696 break;
8697 case SC_SEL_RECTANGLE:
8698 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
8699 sel.selType = Selection::selRectangle;
8700 break;
8701 case SC_SEL_LINES:
8702 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
8703 sel.selType = Selection::selLines;
8704 break;
8705 case SC_SEL_THIN:
8706 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
8707 sel.selType = Selection::selThin;
8708 break;
8709 default:
8710 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
8711 sel.selType = Selection::selStream;
8713 InvalidateSelection(sel.RangeMain(), true);
8715 case SCI_GETSELECTIONMODE:
8716 switch (sel.selType) {
8717 case Selection::selStream:
8718 return SC_SEL_STREAM;
8719 case Selection::selRectangle:
8720 return SC_SEL_RECTANGLE;
8721 case Selection::selLines:
8722 return SC_SEL_LINES;
8723 case Selection::selThin:
8724 return SC_SEL_THIN;
8725 default: // ?!
8726 return SC_SEL_STREAM;
8728 case SCI_GETLINESELSTARTPOSITION:
8729 case SCI_GETLINESELENDPOSITION: {
8730 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(wParam)),
8731 SelectionPosition(pdoc->LineEnd(wParam)));
8732 for (size_t r=0; r<sel.Count(); r++) {
8733 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
8734 if (portion.start.IsValid()) {
8735 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
8738 return INVALID_POSITION;
8741 case SCI_SETOVERTYPE:
8742 inOverstrike = wParam != 0;
8743 break;
8745 case SCI_GETOVERTYPE:
8746 return inOverstrike ? 1 : 0;
8748 case SCI_SETFOCUS:
8749 SetFocusState(wParam != 0);
8750 break;
8752 case SCI_GETFOCUS:
8753 return hasFocus;
8755 case SCI_SETSTATUS:
8756 errorStatus = wParam;
8757 break;
8759 case SCI_GETSTATUS:
8760 return errorStatus;
8762 case SCI_SETMOUSEDOWNCAPTURES:
8763 mouseDownCaptures = wParam != 0;
8764 break;
8766 case SCI_GETMOUSEDOWNCAPTURES:
8767 return mouseDownCaptures;
8769 case SCI_SETCURSOR:
8770 cursorMode = wParam;
8771 DisplayCursor(Window::cursorText);
8772 break;
8774 case SCI_GETCURSOR:
8775 return cursorMode;
8777 case SCI_SETCONTROLCHARSYMBOL:
8778 controlCharSymbol = wParam;
8779 break;
8781 case SCI_GETCONTROLCHARSYMBOL:
8782 return controlCharSymbol;
8784 case SCI_STARTRECORD:
8785 recordingMacro = true;
8786 return 0;
8788 case SCI_STOPRECORD:
8789 recordingMacro = false;
8790 return 0;
8792 case SCI_MOVECARETINSIDEVIEW:
8793 MoveCaretInsideView();
8794 break;
8796 case SCI_SETFOLDMARGINCOLOUR:
8797 vs.foldmarginColourSet = wParam != 0;
8798 vs.foldmarginColour.desired = ColourDesired(lParam);
8799 InvalidateStyleRedraw();
8800 break;
8802 case SCI_SETFOLDMARGINHICOLOUR:
8803 vs.foldmarginHighlightColourSet = wParam != 0;
8804 vs.foldmarginHighlightColour.desired = ColourDesired(lParam);
8805 InvalidateStyleRedraw();
8806 break;
8808 case SCI_SETHOTSPOTACTIVEFORE:
8809 vs.hotspotForegroundSet = wParam != 0;
8810 vs.hotspotForeground.desired = ColourDesired(lParam);
8811 InvalidateStyleRedraw();
8812 break;
8814 case SCI_GETHOTSPOTACTIVEFORE:
8815 return vs.hotspotForeground.desired.AsLong();
8817 case SCI_SETHOTSPOTACTIVEBACK:
8818 vs.hotspotBackgroundSet = wParam != 0;
8819 vs.hotspotBackground.desired = ColourDesired(lParam);
8820 InvalidateStyleRedraw();
8821 break;
8823 case SCI_GETHOTSPOTACTIVEBACK:
8824 return vs.hotspotBackground.desired.AsLong();
8826 case SCI_SETHOTSPOTACTIVEUNDERLINE:
8827 vs.hotspotUnderline = wParam != 0;
8828 InvalidateStyleRedraw();
8829 break;
8831 case SCI_GETHOTSPOTACTIVEUNDERLINE:
8832 return vs.hotspotUnderline ? 1 : 0;
8834 case SCI_SETHOTSPOTSINGLELINE:
8835 vs.hotspotSingleLine = wParam != 0;
8836 InvalidateStyleRedraw();
8837 break;
8839 case SCI_GETHOTSPOTSINGLELINE:
8840 return vs.hotspotSingleLine ? 1 : 0;
8842 case SCI_SETPASTECONVERTENDINGS:
8843 convertPastes = wParam != 0;
8844 break;
8846 case SCI_GETPASTECONVERTENDINGS:
8847 return convertPastes ? 1 : 0;
8849 case SCI_GETCHARACTERPOINTER:
8850 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
8852 case SCI_SETEXTRAASCENT:
8853 vs.extraAscent = wParam;
8854 InvalidateStyleRedraw();
8855 break;
8857 case SCI_GETEXTRAASCENT:
8858 return vs.extraAscent;
8860 case SCI_SETEXTRADESCENT:
8861 vs.extraDescent = wParam;
8862 InvalidateStyleRedraw();
8863 break;
8865 case SCI_GETEXTRADESCENT:
8866 return vs.extraDescent;
8868 case SCI_MARGINSETSTYLEOFFSET:
8869 vs.marginStyleOffset = wParam;
8870 InvalidateStyleRedraw();
8871 break;
8873 case SCI_MARGINGETSTYLEOFFSET:
8874 return vs.marginStyleOffset;
8876 case SCI_SETMARGINOPTIONS:
8877 marginOptions = wParam;
8878 break;
8880 case SCI_GETMARGINOPTIONS:
8881 return marginOptions;
8883 case SCI_MARGINSETTEXT:
8884 pdoc->MarginSetText(wParam, CharPtrFromSPtr(lParam));
8885 break;
8887 case SCI_MARGINGETTEXT: {
8888 const StyledText st = pdoc->MarginStyledText(wParam);
8889 if (lParam) {
8890 if (st.text)
8891 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
8892 else
8893 strcpy(CharPtrFromSPtr(lParam), "");
8895 return st.length;
8898 case SCI_MARGINSETSTYLE:
8899 pdoc->MarginSetStyle(wParam, lParam);
8900 break;
8902 case SCI_MARGINGETSTYLE: {
8903 const StyledText st = pdoc->MarginStyledText(wParam);
8904 return st.style;
8907 case SCI_MARGINSETSTYLES:
8908 pdoc->MarginSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
8909 break;
8911 case SCI_MARGINGETSTYLES: {
8912 const StyledText st = pdoc->MarginStyledText(wParam);
8913 if (lParam) {
8914 if (st.styles)
8915 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
8916 else
8917 strcpy(CharPtrFromSPtr(lParam), "");
8919 return st.styles ? st.length : 0;
8922 case SCI_MARGINTEXTCLEARALL:
8923 pdoc->MarginClearAll();
8924 break;
8926 case SCI_ANNOTATIONSETTEXT:
8927 pdoc->AnnotationSetText(wParam, CharPtrFromSPtr(lParam));
8928 break;
8930 case SCI_ANNOTATIONGETTEXT: {
8931 const StyledText st = pdoc->AnnotationStyledText(wParam);
8932 if (lParam) {
8933 if (st.text)
8934 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
8935 else
8936 strcpy(CharPtrFromSPtr(lParam), "");
8938 return st.length;
8941 case SCI_ANNOTATIONGETSTYLE: {
8942 const StyledText st = pdoc->AnnotationStyledText(wParam);
8943 return st.style;
8946 case SCI_ANNOTATIONSETSTYLE:
8947 pdoc->AnnotationSetStyle(wParam, lParam);
8948 break;
8950 case SCI_ANNOTATIONSETSTYLES:
8951 pdoc->AnnotationSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
8952 break;
8954 case SCI_ANNOTATIONGETSTYLES: {
8955 const StyledText st = pdoc->AnnotationStyledText(wParam);
8956 if (lParam) {
8957 if (st.styles)
8958 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
8959 else
8960 strcpy(CharPtrFromSPtr(lParam), "");
8962 return st.styles ? st.length : 0;
8965 case SCI_ANNOTATIONGETLINES:
8966 return pdoc->AnnotationLines(wParam);
8968 case SCI_ANNOTATIONCLEARALL:
8969 pdoc->AnnotationClearAll();
8970 break;
8972 case SCI_ANNOTATIONSETVISIBLE:
8973 SetAnnotationVisible(wParam);
8974 break;
8976 case SCI_ANNOTATIONGETVISIBLE:
8977 return vs.annotationVisible;
8979 case SCI_ANNOTATIONSETSTYLEOFFSET:
8980 vs.annotationStyleOffset = wParam;
8981 InvalidateStyleRedraw();
8982 break;
8984 case SCI_ANNOTATIONGETSTYLEOFFSET:
8985 return vs.annotationStyleOffset;
8987 case SCI_ADDUNDOACTION:
8988 pdoc->AddUndoAction(wParam, lParam & UNDO_MAY_COALESCE);
8989 break;
8991 case SCI_SETMULTIPLESELECTION:
8992 multipleSelection = wParam != 0;
8993 InvalidateCaret();
8994 break;
8996 case SCI_GETMULTIPLESELECTION:
8997 return multipleSelection;
8999 case SCI_SETADDITIONALSELECTIONTYPING:
9000 additionalSelectionTyping = wParam != 0;
9001 InvalidateCaret();
9002 break;
9004 case SCI_GETADDITIONALSELECTIONTYPING:
9005 return additionalSelectionTyping;
9007 case SCI_SETMULTIPASTE:
9008 multiPasteMode = wParam;
9009 break;
9011 case SCI_GETMULTIPASTE:
9012 return multiPasteMode;
9014 case SCI_SETADDITIONALCARETSBLINK:
9015 additionalCaretsBlink = wParam != 0;
9016 InvalidateCaret();
9017 break;
9019 case SCI_GETADDITIONALCARETSBLINK:
9020 return additionalCaretsBlink;
9022 case SCI_SETADDITIONALCARETSVISIBLE:
9023 additionalCaretsVisible = wParam != 0;
9024 InvalidateCaret();
9025 break;
9027 case SCI_GETADDITIONALCARETSVISIBLE:
9028 return additionalCaretsVisible;
9030 case SCI_GETSELECTIONS:
9031 return sel.Count();
9033 case SCI_CLEARSELECTIONS:
9034 sel.Clear();
9035 Redraw();
9036 break;
9038 case SCI_SETSELECTION:
9039 sel.SetSelection(SelectionRange(wParam, lParam));
9040 Redraw();
9041 break;
9043 case SCI_ADDSELECTION:
9044 sel.AddSelection(SelectionRange(wParam, lParam));
9045 Redraw();
9046 break;
9048 case SCI_SETMAINSELECTION:
9049 sel.SetMain(wParam);
9050 Redraw();
9051 break;
9053 case SCI_GETMAINSELECTION:
9054 return sel.Main();
9056 case SCI_SETSELECTIONNCARET:
9057 sel.Range(wParam).caret.SetPosition(lParam);
9058 Redraw();
9059 break;
9061 case SCI_GETSELECTIONNCARET:
9062 return sel.Range(wParam).caret.Position();
9064 case SCI_SETSELECTIONNANCHOR:
9065 sel.Range(wParam).anchor.SetPosition(lParam);
9066 Redraw();
9067 break;
9068 case SCI_GETSELECTIONNANCHOR:
9069 return sel.Range(wParam).anchor.Position();
9071 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
9072 sel.Range(wParam).caret.SetVirtualSpace(lParam);
9073 Redraw();
9074 break;
9076 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
9077 return sel.Range(wParam).caret.VirtualSpace();
9079 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
9080 sel.Range(wParam).anchor.SetVirtualSpace(lParam);
9081 Redraw();
9082 break;
9084 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
9085 return sel.Range(wParam).anchor.VirtualSpace();
9087 case SCI_SETSELECTIONNSTART:
9088 sel.Range(wParam).anchor.SetPosition(lParam);
9089 Redraw();
9090 break;
9092 case SCI_GETSELECTIONNSTART:
9093 return sel.Range(wParam).Start().Position();
9095 case SCI_SETSELECTIONNEND:
9096 sel.Range(wParam).caret.SetPosition(lParam);
9097 Redraw();
9098 break;
9100 case SCI_GETSELECTIONNEND:
9101 return sel.Range(wParam).End().Position();
9103 case SCI_SETRECTANGULARSELECTIONCARET:
9104 if (!sel.IsRectangular())
9105 sel.Clear();
9106 sel.selType = Selection::selRectangle;
9107 sel.Rectangular().caret.SetPosition(wParam);
9108 SetRectangularRange();
9109 Redraw();
9110 break;
9112 case SCI_GETRECTANGULARSELECTIONCARET:
9113 return sel.Rectangular().caret.Position();
9115 case SCI_SETRECTANGULARSELECTIONANCHOR:
9116 if (!sel.IsRectangular())
9117 sel.Clear();
9118 sel.selType = Selection::selRectangle;
9119 sel.Rectangular().anchor.SetPosition(wParam);
9120 SetRectangularRange();
9121 Redraw();
9122 break;
9124 case SCI_GETRECTANGULARSELECTIONANCHOR:
9125 return sel.Rectangular().anchor.Position();
9127 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9128 if (!sel.IsRectangular())
9129 sel.Clear();
9130 sel.selType = Selection::selRectangle;
9131 sel.Rectangular().caret.SetVirtualSpace(wParam);
9132 SetRectangularRange();
9133 Redraw();
9134 break;
9136 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9137 return sel.Rectangular().caret.VirtualSpace();
9139 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9140 if (!sel.IsRectangular())
9141 sel.Clear();
9142 sel.selType = Selection::selRectangle;
9143 sel.Rectangular().anchor.SetVirtualSpace(wParam);
9144 SetRectangularRange();
9145 Redraw();
9146 break;
9148 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9149 return sel.Rectangular().anchor.VirtualSpace();
9151 case SCI_SETVIRTUALSPACEOPTIONS:
9152 virtualSpaceOptions = wParam;
9153 break;
9155 case SCI_GETVIRTUALSPACEOPTIONS:
9156 return virtualSpaceOptions;
9158 case SCI_SETADDITIONALSELFORE:
9159 vs.selAdditionalForeground.desired = ColourDesired(wParam);
9160 InvalidateStyleRedraw();
9161 break;
9163 case SCI_SETADDITIONALSELBACK:
9164 vs.selAdditionalBackground.desired = ColourDesired(wParam);
9165 InvalidateStyleRedraw();
9166 break;
9168 case SCI_SETADDITIONALSELALPHA:
9169 vs.selAdditionalAlpha = wParam;
9170 InvalidateStyleRedraw();
9171 break;
9173 case SCI_GETADDITIONALSELALPHA:
9174 return vs.selAdditionalAlpha;
9176 case SCI_SETADDITIONALCARETFORE:
9177 vs.additionalCaretColour.desired = ColourDesired(wParam);
9178 InvalidateStyleRedraw();
9179 break;
9181 case SCI_GETADDITIONALCARETFORE:
9182 return vs.additionalCaretColour.desired.AsLong();
9184 case SCI_ROTATESELECTION:
9185 sel.RotateMain();
9186 InvalidateSelection(sel.RangeMain(), true);
9187 break;
9189 case SCI_SWAPMAINANCHORCARET:
9190 InvalidateSelection(sel.RangeMain());
9191 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
9192 break;
9194 case SCI_CHANGELEXERSTATE:
9195 pdoc->ChangeLexerState(wParam, lParam);
9196 break;
9198 case SCI_SETIDENTIFIER:
9199 SetCtrlID(wParam);
9200 break;
9202 case SCI_GETIDENTIFIER:
9203 return GetCtrlID();
9205 default:
9206 return DefWndProc(iMessage, wParam, lParam);
9208 //Platform::DebugPrintf("end wnd proc\n");
9209 return 0l;